mirror of
https://github.com/ton-blockchain/ton
synced 2025-03-09 15:40:10 +00:00
integrating the existing state of TON Storage / TON Payments / CPS Fift development branches
This commit is contained in:
parent
040df63c98
commit
4e2624459b
153 changed files with 10760 additions and 1695 deletions
|
@ -124,9 +124,6 @@ set(TON_CRYPTO_SOURCE
|
|||
|
||||
vm/db/StaticBagOfCellsDb.h
|
||||
vm/db/StaticBagOfCellsDb.cpp
|
||||
|
||||
vm/db/BlobView.h
|
||||
vm/db/BlobView.cpp
|
||||
)
|
||||
|
||||
set(TON_DB_SOURCE
|
||||
|
@ -144,6 +141,7 @@ set(FIFT_SOURCE
|
|||
fift/Dictionary.cpp
|
||||
fift/Fift.cpp
|
||||
fift/IntCtx.cpp
|
||||
fift/Continuation.cpp
|
||||
fift/SourceLookup.cpp
|
||||
fift/utils.cpp
|
||||
fift/words.cpp
|
||||
|
@ -151,6 +149,7 @@ set(FIFT_SOURCE
|
|||
fift/Dictionary.h
|
||||
fift/Fift.h
|
||||
fift/IntCtx.h
|
||||
fift/Continuation.h
|
||||
fift/SourceLookup.h
|
||||
fift/utils.h
|
||||
fift/words.h
|
||||
|
@ -216,9 +215,7 @@ set(SMC_ENVELOPE_SOURCE
|
|||
smc-envelope/PaymentChannel.cpp
|
||||
smc-envelope/SmartContract.cpp
|
||||
smc-envelope/SmartContractCode.cpp
|
||||
smc-envelope/TestGiver.cpp
|
||||
smc-envelope/TestWallet.cpp
|
||||
smc-envelope/Wallet.cpp
|
||||
smc-envelope/WalletInterface.cpp
|
||||
smc-envelope/WalletV3.cpp
|
||||
|
||||
smc-envelope/GenericAccount.h
|
||||
|
@ -228,9 +225,6 @@ set(SMC_ENVELOPE_SOURCE
|
|||
smc-envelope/MultisigWallet.h
|
||||
smc-envelope/SmartContract.h
|
||||
smc-envelope/SmartContractCode.h
|
||||
smc-envelope/TestGiver.h
|
||||
smc-envelope/TestWallet.h
|
||||
smc-envelope/Wallet.h
|
||||
smc-envelope/WalletInterface.h
|
||||
smc-envelope/WalletV3.h
|
||||
)
|
||||
|
@ -268,7 +262,7 @@ set(FIFT_TEST_SOURCE
|
|||
add_library(ton_crypto STATIC ${TON_CRYPTO_SOURCE})
|
||||
target_include_directories(ton_crypto PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/..>)
|
||||
target_link_libraries(ton_crypto PUBLIC ${OPENSSL_CRYPTO_LIBRARY} tdutils)
|
||||
target_link_libraries(ton_crypto PUBLIC ${OPENSSL_CRYPTO_LIBRARY} tdutils tddb_utils)
|
||||
if (NOT WIN32)
|
||||
target_link_libraries(ton_crypto PUBLIC dl z)
|
||||
endif()
|
||||
|
|
|
@ -21,6 +21,11 @@
|
|||
#include "td/utils/ScopeGuard.h"
|
||||
|
||||
namespace td {
|
||||
|
||||
Ref<CntObject> CntObject::clone() const {
|
||||
return Ref<CntObject>{make_copy(), Ref<CntObject>::acquire_t()};
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
struct SafeDeleter {
|
||||
public:
|
||||
|
|
|
@ -83,6 +83,7 @@ class CntObject {
|
|||
void assert_unique() const {
|
||||
assert(is_unique());
|
||||
}
|
||||
Ref<CntObject> clone() const;
|
||||
};
|
||||
|
||||
typedef Ref<CntObject> RefAny;
|
||||
|
@ -159,6 +160,7 @@ struct static_cast_ref {};
|
|||
namespace detail {
|
||||
void safe_delete(const CntObject* ptr);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
class Ref {
|
||||
T* ptr;
|
||||
|
|
398
crypto/fift/Continuation.cpp
Normal file
398
crypto/fift/Continuation.cpp
Normal file
|
@ -0,0 +1,398 @@
|
|||
/*
|
||||
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 2020 Telegram Systems LLP
|
||||
*/
|
||||
#include "Continuation.h"
|
||||
#include "IntCtx.h"
|
||||
#include "Dictionary.h"
|
||||
|
||||
namespace fift {
|
||||
|
||||
//
|
||||
// FiftCont
|
||||
//
|
||||
bool FiftCont::print_dict_name(std::ostream& os, const IntCtx& ctx) const {
|
||||
std::string word_name;
|
||||
if (ctx.dictionary && ctx.dictionary->lookup_def(this, &word_name)) {
|
||||
if (word_name.size() && word_name.back() == ' ') {
|
||||
word_name.pop_back();
|
||||
}
|
||||
os << word_name;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string FiftCont::get_dict_name(const IntCtx& ctx) const {
|
||||
std::string word_name;
|
||||
if (ctx.dictionary && ctx.dictionary->lookup_def(this, &word_name)) {
|
||||
if (word_name.size() && word_name.back() == ' ') {
|
||||
word_name.pop_back();
|
||||
}
|
||||
return word_name;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
bool FiftCont::print_dummy_name(std::ostream& os, const IntCtx& ctx) const {
|
||||
os << "<continuation " << (const void*)this << ">";
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FiftCont::print_name(std::ostream& os, const IntCtx& ctx) const {
|
||||
return print_dict_name(os, ctx) || print_dummy_name(os, ctx);
|
||||
}
|
||||
|
||||
bool FiftCont::dump(std::ostream& os, const IntCtx& ctx) const {
|
||||
bool ok = print_name(os, ctx);
|
||||
os << std::endl;
|
||||
return ok;
|
||||
}
|
||||
|
||||
//
|
||||
// QuitCont
|
||||
//
|
||||
Ref<FiftCont> QuitCont::run_tail(IntCtx& ctx) const {
|
||||
ctx.set_exit_code(exit_code);
|
||||
ctx.next.clear();
|
||||
return {};
|
||||
}
|
||||
|
||||
bool QuitCont::print_name(std::ostream& os, const IntCtx& ctx) const {
|
||||
os << "<quit " << exit_code << ">";
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// SeqCont
|
||||
//
|
||||
Ref<FiftCont> SeqCont::run_tail(IntCtx& ctx) const {
|
||||
ctx.next = seq(second, std::move(ctx.next));
|
||||
return first;
|
||||
}
|
||||
|
||||
Ref<FiftCont> SeqCont::run_modify(IntCtx& ctx) {
|
||||
if (ctx.next.is_null()) {
|
||||
ctx.next = std::move(second);
|
||||
return std::move(first);
|
||||
} else {
|
||||
auto res = std::move(first);
|
||||
first = std::move(second);
|
||||
second = std::move(ctx.next);
|
||||
ctx.next = self();
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
bool SeqCont::print_name(std::ostream& os, const IntCtx& ctx) const {
|
||||
if (first.not_null()) {
|
||||
return first->print_name(os, ctx);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool SeqCont::dump(std::ostream& os, const IntCtx& ctx) const {
|
||||
if (first.not_null()) {
|
||||
return first->dump(os, ctx);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// TimesCont
|
||||
//
|
||||
Ref<FiftCont> TimesCont::run_tail(IntCtx& ctx) const {
|
||||
if (count > 1) {
|
||||
ctx.next = td::make_ref<TimesCont>(body, SeqCont::seq(after, std::move(ctx.next)), count - 1);
|
||||
} else {
|
||||
ctx.next = SeqCont::seq(after, std::move(ctx.next));
|
||||
}
|
||||
return body;
|
||||
}
|
||||
|
||||
Ref<FiftCont> TimesCont::run_modify(IntCtx& ctx) {
|
||||
if (ctx.next.not_null()) {
|
||||
after = SeqCont::seq(std::move(after), std::move(ctx.next));
|
||||
}
|
||||
if (count > 1) {
|
||||
--count;
|
||||
ctx.next = self();
|
||||
return body;
|
||||
} else {
|
||||
ctx.next = std::move(after);
|
||||
return std::move(body);
|
||||
}
|
||||
}
|
||||
|
||||
bool TimesCont::print_name(std::ostream& os, const IntCtx& ctx) const {
|
||||
os << "<repeat " << count << " times>";
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TimesCont::dump(std::ostream& os, const IntCtx& ctx) const {
|
||||
os << "<repeat " << count << " times:> ";
|
||||
return body->dump(os, ctx);
|
||||
}
|
||||
|
||||
//
|
||||
// UntilCont
|
||||
//
|
||||
Ref<FiftCont> UntilCont::run_tail(IntCtx& ctx) const {
|
||||
if (ctx.stack.pop_bool()) {
|
||||
return after;
|
||||
} else if (ctx.next.not_null()) {
|
||||
ctx.next = td::make_ref<UntilCont>(body, SeqCont::seq(after, std::move(ctx.next)));
|
||||
return body;
|
||||
} else {
|
||||
ctx.next = self();
|
||||
return body;
|
||||
}
|
||||
}
|
||||
|
||||
Ref<FiftCont> UntilCont::run_modify(IntCtx& ctx) {
|
||||
if (ctx.stack.pop_bool()) {
|
||||
return std::move(after);
|
||||
} else {
|
||||
if (ctx.next.not_null()) {
|
||||
after = SeqCont::seq(after, std::move(ctx.next));
|
||||
}
|
||||
ctx.next = self();
|
||||
return body;
|
||||
}
|
||||
}
|
||||
|
||||
bool UntilCont::print_name(std::ostream& os, const IntCtx& ctx) const {
|
||||
os << "<until loop continuation>";
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UntilCont::dump(std::ostream& os, const IntCtx& ctx) const {
|
||||
os << "<until loop continuation:> ";
|
||||
return body->dump(os, ctx);
|
||||
}
|
||||
|
||||
//
|
||||
// WhileCont
|
||||
//
|
||||
Ref<FiftCont> WhileCont::run_tail(IntCtx& ctx) const {
|
||||
if (!stage) {
|
||||
ctx.next = td::make_ref<WhileCont>(cond, body, SeqCont::seq(after, std::move(ctx.next)), true);
|
||||
return cond;
|
||||
}
|
||||
if (!ctx.stack.pop_bool()) {
|
||||
return after;
|
||||
} else {
|
||||
ctx.next = td::make_ref<WhileCont>(cond, body, SeqCont::seq(after, std::move(ctx.next)));
|
||||
return body;
|
||||
}
|
||||
}
|
||||
|
||||
Ref<FiftCont> WhileCont::run_modify(IntCtx& ctx) {
|
||||
if (!stage) {
|
||||
if (ctx.next.not_null()) {
|
||||
after = SeqCont::seq(std::move(after), std::move(ctx.next));
|
||||
}
|
||||
stage = true;
|
||||
ctx.next = self();
|
||||
return cond;
|
||||
}
|
||||
if (!ctx.stack.pop_bool()) {
|
||||
return std::move(after);
|
||||
} else {
|
||||
if (ctx.next.not_null()) {
|
||||
after = SeqCont::seq(std::move(after), std::move(ctx.next));
|
||||
}
|
||||
stage = false;
|
||||
ctx.next = self();
|
||||
return body;
|
||||
}
|
||||
}
|
||||
|
||||
bool WhileCont::print_name(std::ostream& os, const IntCtx& ctx) const {
|
||||
os << "<while loop " << (stage ? "body" : "condition") << ">";
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WhileCont::dump(std::ostream& os, const IntCtx& ctx) const {
|
||||
os << "<while loop " << (stage ? "body" : "condition") << ":> ";
|
||||
return (stage ? body : cond)->dump(os, ctx);
|
||||
}
|
||||
|
||||
//
|
||||
// LoopCont
|
||||
//
|
||||
Ref<FiftCont> LoopCont::run_tail(IntCtx& ctx) const {
|
||||
return Ref<FiftCont>(clone());
|
||||
}
|
||||
|
||||
Ref<FiftCont> LoopCont::run_modify(IntCtx& ctx) {
|
||||
if (ctx.next.not_null()) {
|
||||
after = SeqCont::seq(std::move(after), std::move(ctx.next));
|
||||
}
|
||||
switch (state) {
|
||||
case 0:
|
||||
if (!init(ctx)) {
|
||||
return std::move(after);
|
||||
}
|
||||
state = 1;
|
||||
// fallthrough
|
||||
case 1:
|
||||
if (!pre_exec(ctx)) {
|
||||
state = 3;
|
||||
if (finalize(ctx)) {
|
||||
return std::move(after);
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
state = 2;
|
||||
ctx.next = self();
|
||||
return func;
|
||||
case 2:
|
||||
if (post_exec(ctx)) {
|
||||
state = 1;
|
||||
return self();
|
||||
}
|
||||
state = 3;
|
||||
// fallthrough
|
||||
case 3:
|
||||
if (finalize(ctx)) {
|
||||
return std::move(after);
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
default:
|
||||
throw IntError{"invalid LoopCont state"};
|
||||
}
|
||||
}
|
||||
|
||||
bool LoopCont::print_name(std::ostream& os, const IntCtx& ctx) const {
|
||||
os << "<generic loop continuation state " << state << ">";
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// GenericLitCont
|
||||
//
|
||||
bool GenericLitCont::print_name(std::ostream& os, const IntCtx& ctx) const {
|
||||
auto list = get_literals();
|
||||
bool sp = false;
|
||||
for (auto entry : list) {
|
||||
if (sp) {
|
||||
os << sp;
|
||||
}
|
||||
sp = true;
|
||||
int tp = entry.type();
|
||||
if (entry.is_int() || entry.is(vm::StackEntry::t_string) || entry.is(vm::StackEntry::t_bytes)) {
|
||||
entry.dump(os);
|
||||
} else {
|
||||
auto cont_lit = entry.as_object<FiftCont>();
|
||||
if (cont_lit.not_null()) {
|
||||
os << "{ ";
|
||||
cont_lit->print_name(os, ctx);
|
||||
os << " }";
|
||||
} else {
|
||||
os << "<literal of type " << tp << ">";
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// SmallIntLitCont
|
||||
//
|
||||
Ref<FiftCont> SmallIntLitCont::run_tail(IntCtx& ctx) const {
|
||||
ctx.stack.push_smallint(value_);
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<vm::StackEntry> SmallIntLitCont::get_literals() const {
|
||||
return {td::make_refint(value_)};
|
||||
}
|
||||
|
||||
//
|
||||
// IntLitCont
|
||||
//
|
||||
Ref<FiftCont> IntLitCont::run_tail(IntCtx& ctx) const {
|
||||
ctx.stack.push_int(value_);
|
||||
return {};
|
||||
}
|
||||
|
||||
Ref<FiftCont> IntLitCont::run_modify(IntCtx& ctx) {
|
||||
ctx.stack.push_int(std::move(value_));
|
||||
return {};
|
||||
}
|
||||
|
||||
Ref<FiftCont> IntLitCont::literal(td::RefInt256 int_value) {
|
||||
if (int_value->signed_fits_bits(64)) {
|
||||
return literal(int_value->to_long());
|
||||
} else {
|
||||
return td::make_ref<IntLitCont>(std::move(int_value));
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// LitCont
|
||||
//
|
||||
Ref<FiftCont> LitCont::run_tail(IntCtx& ctx) const {
|
||||
ctx.stack.push(value_);
|
||||
return {};
|
||||
}
|
||||
|
||||
Ref<FiftCont> LitCont::run_modify(IntCtx& ctx) {
|
||||
ctx.stack.push(std::move(value_));
|
||||
return {};
|
||||
}
|
||||
|
||||
Ref<FiftCont> LitCont::literal(vm::StackEntry value) {
|
||||
if (value.is_int()) {
|
||||
return literal(std::move(value).as_int());
|
||||
} else {
|
||||
return td::make_ref<LitCont>(std::move(value));
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// MultiLitCont
|
||||
//
|
||||
Ref<FiftCont> MultiLitCont::run_tail(IntCtx& ctx) const {
|
||||
for (auto& value : values_) {
|
||||
ctx.stack.push(value);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
Ref<FiftCont> MultiLitCont::run_modify(IntCtx& ctx) {
|
||||
for (auto& value : values_) {
|
||||
ctx.stack.push(std::move(value));
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
MultiLitCont& MultiLitCont::push_back(vm::StackEntry new_literal) {
|
||||
values_.push_back(std::move(new_literal));
|
||||
return *this;
|
||||
}
|
||||
|
||||
vm::StackEntry MultiLitCont::at(int idx) const {
|
||||
return values_.at(idx);
|
||||
}
|
||||
|
||||
} // namespace fift
|
272
crypto/fift/Continuation.h
Normal file
272
crypto/fift/Continuation.h
Normal file
|
@ -0,0 +1,272 @@
|
|||
/*
|
||||
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 2020 Telegram Systems LLP
|
||||
*/
|
||||
#pragma once
|
||||
#include "common/refcnt.hpp"
|
||||
#include "common/refint.h"
|
||||
#include "vm/stack.hpp"
|
||||
|
||||
namespace fift {
|
||||
using td::Ref;
|
||||
struct IntCtx;
|
||||
|
||||
/*
|
||||
*
|
||||
* FIFT CONTINUATIONS
|
||||
*
|
||||
*/
|
||||
|
||||
class FiftCont : public td::CntObject {
|
||||
public:
|
||||
FiftCont() = default;
|
||||
virtual ~FiftCont() override = default;
|
||||
virtual Ref<FiftCont> run_tail(IntCtx& ctx) const = 0;
|
||||
virtual Ref<FiftCont> run_modify(IntCtx& ctx) {
|
||||
return run_tail(ctx);
|
||||
}
|
||||
virtual Ref<FiftCont> handle_tail(IntCtx& ctx) const {
|
||||
return {};
|
||||
}
|
||||
virtual Ref<FiftCont> handle_modify(IntCtx& ctx) {
|
||||
return handle_tail(ctx);
|
||||
}
|
||||
virtual Ref<FiftCont> up() const {
|
||||
return {};
|
||||
}
|
||||
virtual bool is_list() const {
|
||||
return false;
|
||||
}
|
||||
virtual long long list_size() const {
|
||||
return -1;
|
||||
}
|
||||
virtual const Ref<FiftCont>* get_list() const {
|
||||
return nullptr;
|
||||
}
|
||||
virtual bool is_literal() const {
|
||||
return false;
|
||||
}
|
||||
virtual int literal_count() const {
|
||||
return -1;
|
||||
}
|
||||
virtual std::vector<vm::StackEntry> get_literals() const {
|
||||
return {};
|
||||
}
|
||||
std::string get_dict_name(const IntCtx& ctx) const;
|
||||
bool print_dict_name(std::ostream& os, const IntCtx& ctx) const;
|
||||
bool print_dummy_name(std::ostream& os, const IntCtx& ctx) const;
|
||||
virtual bool print_name(std::ostream& os, const IntCtx& ctx) const;
|
||||
virtual bool dump(std::ostream& os, const IntCtx& ctx) const;
|
||||
Ref<FiftCont> self() const {
|
||||
return Ref<FiftCont>{this};
|
||||
}
|
||||
};
|
||||
|
||||
class QuitCont : public FiftCont {
|
||||
int exit_code;
|
||||
|
||||
public:
|
||||
QuitCont(int _exit_code = -1) : exit_code(_exit_code) {
|
||||
}
|
||||
Ref<FiftCont> run_tail(IntCtx& ctx) const override;
|
||||
bool print_name(std::ostream& os, const IntCtx& ctx) const override;
|
||||
};
|
||||
|
||||
class SeqCont : public FiftCont {
|
||||
Ref<FiftCont> first, second;
|
||||
|
||||
public:
|
||||
SeqCont(Ref<FiftCont> _first, Ref<FiftCont> _second) : first(std::move(_first)), second(std::move(_second)) {
|
||||
}
|
||||
static Ref<FiftCont> seq(Ref<FiftCont> _first, Ref<FiftCont> _second) {
|
||||
return _second.is_null() ? std::move(_first) : td::make_ref<SeqCont>(std::move(_first), std::move(_second));
|
||||
}
|
||||
Ref<FiftCont> run_tail(IntCtx& ctx) const override;
|
||||
Ref<FiftCont> run_modify(IntCtx& ctx) override;
|
||||
Ref<FiftCont> up() const override {
|
||||
return second;
|
||||
}
|
||||
bool print_name(std::ostream& os, const IntCtx& ctx) const override;
|
||||
bool dump(std::ostream& os, const IntCtx& ctx) const override;
|
||||
};
|
||||
|
||||
class TimesCont : public FiftCont {
|
||||
Ref<FiftCont> body, after;
|
||||
int count;
|
||||
|
||||
public:
|
||||
TimesCont(Ref<FiftCont> _body, Ref<FiftCont> _after, int _count)
|
||||
: body(std::move(_body)), after(std::move(_after)), count(_count) {
|
||||
}
|
||||
Ref<FiftCont> run_tail(IntCtx& ctx) const override;
|
||||
Ref<FiftCont> run_modify(IntCtx& ctx) override;
|
||||
Ref<FiftCont> up() const override {
|
||||
return after;
|
||||
}
|
||||
bool print_name(std::ostream& os, const IntCtx& ctx) const override;
|
||||
bool dump(std::ostream& os, const IntCtx& ctx) const override;
|
||||
};
|
||||
|
||||
class UntilCont : public FiftCont {
|
||||
Ref<FiftCont> body, after;
|
||||
|
||||
public:
|
||||
UntilCont(Ref<FiftCont> _body, Ref<FiftCont> _after) : body(std::move(_body)), after(std::move(_after)) {
|
||||
}
|
||||
Ref<FiftCont> run_tail(IntCtx& ctx) const override;
|
||||
Ref<FiftCont> run_modify(IntCtx& ctx) override;
|
||||
bool print_name(std::ostream& os, const IntCtx& ctx) const override;
|
||||
bool dump(std::ostream& os, const IntCtx& ctx) const override;
|
||||
};
|
||||
|
||||
class WhileCont : public FiftCont {
|
||||
Ref<FiftCont> cond, body, after;
|
||||
bool stage;
|
||||
|
||||
public:
|
||||
WhileCont(Ref<FiftCont> _cond, Ref<FiftCont> _body, Ref<FiftCont> _after, bool _stage = false)
|
||||
: cond(std::move(_cond)), body(std::move(_body)), after(std::move(_after)), stage(_stage) {
|
||||
}
|
||||
Ref<FiftCont> run_tail(IntCtx& ctx) const override;
|
||||
Ref<FiftCont> run_modify(IntCtx& ctx) override;
|
||||
Ref<FiftCont> up() const override {
|
||||
return after;
|
||||
}
|
||||
bool print_name(std::ostream& os, const IntCtx& ctx) const override;
|
||||
bool dump(std::ostream& os, const IntCtx& ctx) const override;
|
||||
};
|
||||
|
||||
class LoopCont : public FiftCont {
|
||||
Ref<FiftCont> func, after;
|
||||
int state;
|
||||
|
||||
public:
|
||||
LoopCont(Ref<FiftCont> _func, Ref<FiftCont> _after, int _state = 0)
|
||||
: func(std::move(_func)), after(std::move(_after)), state(_state) {
|
||||
}
|
||||
LoopCont(const LoopCont&) = default;
|
||||
virtual bool init(IntCtx& ctx) {
|
||||
return true;
|
||||
}
|
||||
virtual bool pre_exec(IntCtx& ctx) {
|
||||
return true;
|
||||
}
|
||||
virtual bool post_exec(IntCtx& ctx) {
|
||||
return true;
|
||||
}
|
||||
virtual bool finalize(IntCtx& ctx) {
|
||||
return true;
|
||||
}
|
||||
Ref<FiftCont> run_tail(IntCtx& ctx) const override;
|
||||
Ref<FiftCont> run_modify(IntCtx& ctx) override;
|
||||
Ref<FiftCont> up() const override {
|
||||
return after;
|
||||
}
|
||||
bool print_name(std::ostream& os, const IntCtx& ctx) const override;
|
||||
};
|
||||
|
||||
class GenericLitCont : public FiftCont {
|
||||
public:
|
||||
bool is_literal() const override {
|
||||
return true;
|
||||
}
|
||||
std::vector<vm::StackEntry> get_literals() const override = 0;
|
||||
bool print_name(std::ostream& os, const IntCtx& ctx) const override;
|
||||
};
|
||||
|
||||
class SmallIntLitCont : public GenericLitCont {
|
||||
long long value_;
|
||||
|
||||
public:
|
||||
SmallIntLitCont(long long value) : value_(value) {
|
||||
}
|
||||
Ref<FiftCont> run_tail(IntCtx& ctx) const override;
|
||||
std::vector<vm::StackEntry> get_literals() const override;
|
||||
static Ref<FiftCont> literal(long long int_value) {
|
||||
return td::make_ref<SmallIntLitCont>(int_value);
|
||||
}
|
||||
};
|
||||
|
||||
class IntLitCont : public GenericLitCont {
|
||||
td::RefInt256 value_;
|
||||
|
||||
public:
|
||||
IntLitCont(td::RefInt256 value) : value_(std::move(value)) {
|
||||
}
|
||||
Ref<FiftCont> run_tail(IntCtx& ctx) const override;
|
||||
Ref<FiftCont> run_modify(IntCtx& ctx) override;
|
||||
std::vector<vm::StackEntry> get_literals() const override {
|
||||
return {vm::StackEntry(value_)};
|
||||
}
|
||||
static Ref<FiftCont> literal(td::RefInt256 int_value);
|
||||
static Ref<FiftCont> literal(long long int_value) {
|
||||
return SmallIntLitCont::literal(int_value);
|
||||
}
|
||||
};
|
||||
|
||||
class LitCont : public GenericLitCont {
|
||||
vm::StackEntry value_;
|
||||
|
||||
public:
|
||||
LitCont(const vm::StackEntry& value) : value_(value) {
|
||||
}
|
||||
LitCont(vm::StackEntry&& value) : value_(std::move(value)) {
|
||||
}
|
||||
Ref<FiftCont> run_tail(IntCtx& ctx) const override;
|
||||
Ref<FiftCont> run_modify(IntCtx& ctx) override;
|
||||
std::vector<vm::StackEntry> get_literals() const override {
|
||||
return {value_};
|
||||
}
|
||||
static Ref<FiftCont> literal(vm::StackEntry value);
|
||||
static Ref<FiftCont> literal(td::RefInt256 int_value) {
|
||||
return IntLitCont::literal(std::move(int_value));
|
||||
}
|
||||
static Ref<FiftCont> literal(long long int_value) {
|
||||
return SmallIntLitCont::literal(int_value);
|
||||
}
|
||||
};
|
||||
|
||||
class MultiLitCont : public GenericLitCont {
|
||||
std::vector<vm::StackEntry> values_;
|
||||
|
||||
public:
|
||||
MultiLitCont(const std::vector<vm::StackEntry>& values) : values_(values) {
|
||||
}
|
||||
MultiLitCont(std::vector<vm::StackEntry>&& values) : values_(std::move(values)) {
|
||||
}
|
||||
MultiLitCont(std::initializer_list<vm::StackEntry> value_list) : values_(value_list) {
|
||||
}
|
||||
Ref<FiftCont> run_tail(IntCtx& ctx) const override;
|
||||
Ref<FiftCont> run_modify(IntCtx& ctx) override;
|
||||
std::vector<vm::StackEntry> get_literals() const override {
|
||||
return values_;
|
||||
}
|
||||
MultiLitCont& push_back(vm::StackEntry new_literal);
|
||||
vm::StackEntry at(int idx) const;
|
||||
};
|
||||
|
||||
class InterpretCont : public FiftCont {
|
||||
public:
|
||||
InterpretCont() = default;
|
||||
Ref<FiftCont> run_tail(IntCtx&) const override; // text interpreter, defined in words.cpp
|
||||
bool print_name(std::ostream& os, const IntCtx& ctx) const override {
|
||||
os << "<text interpreter continuation>";
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace fift
|
|
@ -20,20 +20,10 @@
|
|||
|
||||
namespace fift {
|
||||
|
||||
//
|
||||
// WordDef
|
||||
//
|
||||
void WordDef::run(IntCtx& ctx) const {
|
||||
auto next = run_tail(ctx);
|
||||
while (next.not_null()) {
|
||||
next = next->run_tail(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// StackWord
|
||||
//
|
||||
Ref<WordDef> StackWord::run_tail(IntCtx& ctx) const {
|
||||
Ref<FiftCont> StackWord::run_tail(IntCtx& ctx) const {
|
||||
f(ctx.stack);
|
||||
return {};
|
||||
}
|
||||
|
@ -41,7 +31,7 @@ Ref<WordDef> StackWord::run_tail(IntCtx& ctx) const {
|
|||
//
|
||||
// CtxWord
|
||||
//
|
||||
Ref<WordDef> CtxWord::run_tail(IntCtx& ctx) const {
|
||||
Ref<FiftCont> CtxWord::run_tail(IntCtx& ctx) const {
|
||||
f(ctx);
|
||||
return {};
|
||||
}
|
||||
|
@ -49,57 +39,125 @@ Ref<WordDef> CtxWord::run_tail(IntCtx& ctx) const {
|
|||
//
|
||||
// CtxTailWord
|
||||
//
|
||||
Ref<WordDef> CtxTailWord::run_tail(IntCtx& ctx) const {
|
||||
Ref<FiftCont> CtxTailWord::run_tail(IntCtx& ctx) const {
|
||||
return f(ctx);
|
||||
}
|
||||
|
||||
//
|
||||
// WordList
|
||||
//
|
||||
WordList::WordList(std::vector<Ref<WordDef>>&& _list) : list(std::move(_list)) {
|
||||
WordList::WordList(std::vector<Ref<FiftCont>>&& _list) : list(std::move(_list)) {
|
||||
}
|
||||
|
||||
WordList::WordList(const std::vector<Ref<WordDef>>& _list) : list(_list) {
|
||||
WordList::WordList(const std::vector<Ref<FiftCont>>& _list) : list(_list) {
|
||||
}
|
||||
|
||||
WordList& WordList::push_back(Ref<WordDef> word_def) {
|
||||
WordList& WordList::push_back(Ref<FiftCont> word_def) {
|
||||
list.push_back(std::move(word_def));
|
||||
return *this;
|
||||
}
|
||||
|
||||
WordList& WordList::push_back(WordDef& wd) {
|
||||
WordList& WordList::push_back(FiftCont& wd) {
|
||||
list.emplace_back(&wd);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Ref<WordDef> WordList::run_tail(IntCtx& ctx) const {
|
||||
Ref<FiftCont> WordList::run_tail(IntCtx& ctx) const {
|
||||
if (list.empty()) {
|
||||
return {};
|
||||
}
|
||||
auto it = list.cbegin(), it2 = list.cend() - 1;
|
||||
while (it < it2) {
|
||||
(*it)->run(ctx);
|
||||
++it;
|
||||
if (list.size() > 1) {
|
||||
ctx.next = td::make_ref<ListCont>(std::move(ctx.next), Ref<WordList>(this), 1);
|
||||
}
|
||||
return *it;
|
||||
return list[0];
|
||||
}
|
||||
|
||||
void WordList::close() {
|
||||
list.shrink_to_fit();
|
||||
}
|
||||
|
||||
WordList& WordList::append(const std::vector<Ref<WordDef>>& other) {
|
||||
WordList& WordList::append(const std::vector<Ref<FiftCont>>& other) {
|
||||
list.insert(list.end(), other.begin(), other.end());
|
||||
return *this;
|
||||
}
|
||||
|
||||
WordList& WordList::append(const Ref<FiftCont>* begin, const Ref<FiftCont>* end) {
|
||||
list.insert(list.end(), begin, end);
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool WordList::dump(std::ostream& os, const IntCtx& ctx) const {
|
||||
os << "{";
|
||||
for (auto entry : list) {
|
||||
os << ' ';
|
||||
entry->print_name(os, ctx);
|
||||
}
|
||||
os << " }" << std::endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// ListCont
|
||||
//
|
||||
|
||||
Ref<FiftCont> ListCont::run_tail(IntCtx& ctx) const {
|
||||
auto sz = list->size();
|
||||
if (pos >= sz) {
|
||||
return std::move(ctx.next);
|
||||
} else if (ctx.next.not_null()) {
|
||||
ctx.next = td::make_ref<ListCont>(SeqCont::seq(next, std::move(ctx.next)), list, pos + 1);
|
||||
} else if (pos + 1 == sz) {
|
||||
ctx.next = next;
|
||||
} else {
|
||||
ctx.next = td::make_ref<ListCont>(next, list, pos + 1);
|
||||
}
|
||||
return list->at(pos);
|
||||
}
|
||||
|
||||
Ref<FiftCont> ListCont::run_modify(IntCtx& ctx) {
|
||||
auto sz = list->size();
|
||||
if (pos >= sz) {
|
||||
return std::move(ctx.next);
|
||||
}
|
||||
auto cur = list->at(pos++);
|
||||
if (ctx.next.not_null()) {
|
||||
next = SeqCont::seq(next, std::move(ctx.next));
|
||||
}
|
||||
if (pos == sz) {
|
||||
ctx.next = std::move(next);
|
||||
} else {
|
||||
ctx.next = self();
|
||||
}
|
||||
return cur;
|
||||
}
|
||||
|
||||
bool ListCont::dump(std::ostream& os, const IntCtx& ctx) const {
|
||||
std::string dict_name = list->get_dict_name(ctx);
|
||||
if (!dict_name.empty()) {
|
||||
os << "[in " << dict_name << ":] ";
|
||||
}
|
||||
std::size_t sz = list->size(), i, a = (pos >= 16 ? pos - 16 : 0), b = std::min(pos + 16, sz);
|
||||
if (a > 0) {
|
||||
os << "... ";
|
||||
}
|
||||
for (i = a; i < b; i++) {
|
||||
if (i == pos) {
|
||||
os << "**HERE** ";
|
||||
}
|
||||
list->at(i)->print_name(os, ctx);
|
||||
os << ' ';
|
||||
}
|
||||
if (b < sz) {
|
||||
os << "...";
|
||||
}
|
||||
os << std::endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// DictEntry
|
||||
//
|
||||
|
||||
DictEntry::DictEntry(Ref<WordDef> _def, bool _act) : def(std::move(_def)), active(_act) {
|
||||
}
|
||||
|
||||
DictEntry::DictEntry(StackWordFunc func) : def(Ref<StackWord>{true, std::move(func)}), active(false) {
|
||||
}
|
||||
|
||||
|
@ -109,22 +167,6 @@ DictEntry::DictEntry(CtxWordFunc func, bool _act) : def(Ref<CtxWord>{true, std::
|
|||
DictEntry::DictEntry(CtxTailWordFunc func, bool _act) : def(Ref<CtxTailWord>{true, std::move(func)}), active(_act) {
|
||||
}
|
||||
|
||||
Ref<WordDef> DictEntry::get_def() const& {
|
||||
return def;
|
||||
}
|
||||
|
||||
Ref<WordDef> DictEntry::get_def() && {
|
||||
return std::move(def);
|
||||
}
|
||||
|
||||
void DictEntry::operator()(IntCtx& ctx) const {
|
||||
def->run(ctx);
|
||||
}
|
||||
|
||||
bool DictEntry::is_active() const {
|
||||
return active;
|
||||
}
|
||||
|
||||
//
|
||||
// Dictionary
|
||||
//
|
||||
|
@ -141,7 +183,7 @@ void Dictionary::def_ctx_word(std::string name, CtxWordFunc func) {
|
|||
}
|
||||
|
||||
void Dictionary::def_active_word(std::string name, CtxWordFunc func) {
|
||||
Ref<WordDef> wdef = Ref<CtxWord>{true, std::move(func)};
|
||||
Ref<FiftCont> wdef = Ref<CtxWord>{true, std::move(func)};
|
||||
def_word(std::move(name), {std::move(wdef), true});
|
||||
}
|
||||
|
||||
|
@ -166,17 +208,32 @@ void Dictionary::undef_word(td::Slice name) {
|
|||
words_.erase(it);
|
||||
}
|
||||
|
||||
bool Dictionary::lookup_def(const FiftCont* cont, std::string* word_ptr) const {
|
||||
if (!cont) {
|
||||
return false;
|
||||
}
|
||||
for (const auto& entry : words_) {
|
||||
if (entry.second.get_def().get() == cont) {
|
||||
if (word_ptr) {
|
||||
*word_ptr = entry.first;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void interpret_nop(vm::Stack& stack) {
|
||||
}
|
||||
|
||||
Ref<WordDef> Dictionary::nop_word_def = Ref<StackWord>{true, interpret_nop};
|
||||
Ref<FiftCont> Dictionary::nop_word_def = Ref<StackWord>{true, interpret_nop};
|
||||
|
||||
//
|
||||
// functions for wordef
|
||||
//
|
||||
Ref<WordDef> pop_exec_token(vm::Stack& stack) {
|
||||
Ref<FiftCont> pop_exec_token(vm::Stack& stack) {
|
||||
stack.check_underflow(1);
|
||||
auto wd_ref = stack.pop().as_object<WordDef>();
|
||||
auto wd_ref = stack.pop().as_object<FiftCont>();
|
||||
if (wd_ref.is_null()) {
|
||||
throw IntError{"execution token expected"};
|
||||
}
|
||||
|
|
|
@ -22,9 +22,11 @@
|
|||
#include <map>
|
||||
|
||||
#include "IntCtx.h"
|
||||
#include "Continuation.h"
|
||||
|
||||
namespace fift {
|
||||
using td::Ref;
|
||||
|
||||
/*
|
||||
*
|
||||
* WORD CLASSES
|
||||
|
@ -34,66 +36,49 @@ using td::Ref;
|
|||
typedef std::function<void(vm::Stack&)> StackWordFunc;
|
||||
typedef std::function<void(IntCtx&)> CtxWordFunc;
|
||||
|
||||
class WordDef : public td::CntObject {
|
||||
public:
|
||||
WordDef() = default;
|
||||
virtual ~WordDef() override = default;
|
||||
virtual Ref<WordDef> run_tail(IntCtx& ctx) const = 0;
|
||||
void run(IntCtx& ctx) const;
|
||||
virtual bool is_list() const {
|
||||
return false;
|
||||
}
|
||||
virtual long long list_size() const {
|
||||
return -1;
|
||||
}
|
||||
virtual const std::vector<Ref<WordDef>>* get_list() const {
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
class StackWord : public WordDef {
|
||||
class StackWord : public FiftCont {
|
||||
StackWordFunc f;
|
||||
|
||||
public:
|
||||
StackWord(StackWordFunc _f) : f(std::move(_f)) {
|
||||
}
|
||||
~StackWord() override = default;
|
||||
Ref<WordDef> run_tail(IntCtx& ctx) const override;
|
||||
Ref<FiftCont> run_tail(IntCtx& ctx) const override;
|
||||
};
|
||||
|
||||
class CtxWord : public WordDef {
|
||||
class CtxWord : public FiftCont {
|
||||
CtxWordFunc f;
|
||||
|
||||
public:
|
||||
CtxWord(CtxWordFunc _f) : f(std::move(_f)) {
|
||||
}
|
||||
~CtxWord() override = default;
|
||||
Ref<WordDef> run_tail(IntCtx& ctx) const override;
|
||||
Ref<FiftCont> run_tail(IntCtx& ctx) const override;
|
||||
};
|
||||
|
||||
typedef std::function<Ref<WordDef>(IntCtx&)> CtxTailWordFunc;
|
||||
typedef std::function<Ref<FiftCont>(IntCtx&)> CtxTailWordFunc;
|
||||
|
||||
class CtxTailWord : public WordDef {
|
||||
class CtxTailWord : public FiftCont {
|
||||
CtxTailWordFunc f;
|
||||
|
||||
public:
|
||||
CtxTailWord(CtxTailWordFunc _f) : f(std::move(_f)) {
|
||||
}
|
||||
~CtxTailWord() override = default;
|
||||
Ref<WordDef> run_tail(IntCtx& ctx) const override;
|
||||
Ref<FiftCont> run_tail(IntCtx& ctx) const override;
|
||||
};
|
||||
|
||||
class WordList : public WordDef {
|
||||
std::vector<Ref<WordDef>> list;
|
||||
class WordList : public FiftCont {
|
||||
std::vector<Ref<FiftCont>> list;
|
||||
|
||||
public:
|
||||
~WordList() override = default;
|
||||
WordList() = default;
|
||||
WordList(std::vector<Ref<WordDef>>&& _list);
|
||||
WordList(const std::vector<Ref<WordDef>>& _list);
|
||||
WordList& push_back(Ref<WordDef> word_def);
|
||||
WordList& push_back(WordDef& wd);
|
||||
Ref<WordDef> run_tail(IntCtx& ctx) const override;
|
||||
WordList(std::vector<Ref<FiftCont>>&& _list);
|
||||
WordList(const std::vector<Ref<FiftCont>>& _list);
|
||||
WordList& push_back(Ref<FiftCont> word_def);
|
||||
WordList& push_back(FiftCont& wd);
|
||||
Ref<FiftCont> run_tail(IntCtx& ctx) const override;
|
||||
void close();
|
||||
bool is_list() const override {
|
||||
return true;
|
||||
|
@ -101,43 +86,73 @@ class WordList : public WordDef {
|
|||
long long list_size() const override {
|
||||
return (long long)list.size();
|
||||
}
|
||||
const std::vector<Ref<WordDef>>* get_list() const override {
|
||||
return &list;
|
||||
std::size_t size() const {
|
||||
return list.size();
|
||||
}
|
||||
WordList& append(const std::vector<Ref<WordDef>>& other);
|
||||
const Ref<FiftCont>& at(std::size_t idx) const {
|
||||
return list.at(idx);
|
||||
}
|
||||
const Ref<FiftCont>* get_list() const override {
|
||||
return list.data();
|
||||
}
|
||||
WordList& append(const std::vector<Ref<FiftCont>>& other);
|
||||
WordList& append(const Ref<FiftCont>* begin, const Ref<FiftCont>* end);
|
||||
WordList* make_copy() const override {
|
||||
return new WordList(list);
|
||||
}
|
||||
bool dump(std::ostream& os, const IntCtx& ctx) const override;
|
||||
};
|
||||
|
||||
class ListCont : public FiftCont {
|
||||
Ref<FiftCont> next;
|
||||
Ref<WordList> list;
|
||||
std::size_t pos;
|
||||
|
||||
public:
|
||||
ListCont(Ref<FiftCont> nxt, Ref<WordList> wl, std::size_t p = 0) : next(std::move(nxt)), list(std::move(wl)), pos(p) {
|
||||
}
|
||||
~ListCont() override = default;
|
||||
Ref<FiftCont> run_tail(IntCtx& ctx) const override;
|
||||
Ref<FiftCont> run_modify(IntCtx& ctx) override;
|
||||
Ref<FiftCont> up() const override {
|
||||
return next;
|
||||
}
|
||||
bool dump(std::ostream& os, const IntCtx& ctx) const override;
|
||||
};
|
||||
|
||||
class DictEntry {
|
||||
Ref<WordDef> def;
|
||||
Ref<FiftCont> def;
|
||||
bool active;
|
||||
|
||||
public:
|
||||
DictEntry() = delete;
|
||||
DictEntry(const DictEntry& ref) = default;
|
||||
DictEntry(DictEntry&& ref) = default;
|
||||
DictEntry(Ref<WordDef> _def, bool _act = false);
|
||||
DictEntry(Ref<FiftCont> _def, bool _act = false) : def(std::move(_def)), active(_act) {
|
||||
}
|
||||
DictEntry(StackWordFunc func);
|
||||
DictEntry(CtxWordFunc func, bool _act = false);
|
||||
DictEntry(CtxTailWordFunc func, bool _act = false);
|
||||
//DictEntry(const std::vector<Ref<WordDef>>& word_list);
|
||||
//DictEntry(std::vector<Ref<WordDef>>&& word_list);
|
||||
//DictEntry(const std::vector<Ref<FiftCont>>& word_list);
|
||||
//DictEntry(std::vector<Ref<FiftCont>>&& word_list);
|
||||
DictEntry& operator=(const DictEntry&) = default;
|
||||
DictEntry& operator=(DictEntry&&) = default;
|
||||
Ref<WordDef> get_def() const&;
|
||||
Ref<WordDef> get_def() &&;
|
||||
void operator()(IntCtx& ctx) const;
|
||||
bool is_active() const;
|
||||
~DictEntry() = default;
|
||||
Ref<FiftCont> get_def() const& {
|
||||
return def;
|
||||
}
|
||||
Ref<FiftCont> get_def() && {
|
||||
return std::move(def);
|
||||
}
|
||||
bool is_active() const {
|
||||
return active;
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
DictEntry::DictEntry(const std::vector<Ref<WordDef>>& word_list) : def(Ref<WordList>{true, word_list}) {
|
||||
DictEntry::DictEntry(const std::vector<Ref<FiftCont>>& word_list) : def(Ref<WordList>{true, word_list}) {
|
||||
}
|
||||
|
||||
DictEntry::DictEntry(std::vector<Ref<WordDef>>&& word_list) : def(Ref<WordList>{true, std::move(word_list)}) {
|
||||
DictEntry::DictEntry(std::vector<Ref<FiftCont>>&& word_list) : def(Ref<WordList>{true, std::move(word_list)}) {
|
||||
}
|
||||
*/
|
||||
|
||||
|
@ -156,7 +171,10 @@ class Dictionary {
|
|||
void def_stack_word(std::string name, StackWordFunc func);
|
||||
void def_word(std::string name, DictEntry word);
|
||||
void undef_word(td::Slice name);
|
||||
|
||||
bool lookup_def(const FiftCont* cont, std::string* word_ptr = nullptr) const;
|
||||
bool lookup_def(Ref<FiftCont> cont, std::string* word_ptr = nullptr) const {
|
||||
return lookup_def(cont.get(), word_ptr);
|
||||
}
|
||||
auto begin() const {
|
||||
return words_.begin();
|
||||
}
|
||||
|
@ -164,7 +182,7 @@ class Dictionary {
|
|||
return words_.end();
|
||||
}
|
||||
|
||||
static Ref<WordDef> nop_word_def;
|
||||
static Ref<FiftCont> nop_word_def;
|
||||
|
||||
private:
|
||||
std::map<std::string, DictEntry, std::less<>> words_;
|
||||
|
@ -176,7 +194,7 @@ class Dictionary {
|
|||
*
|
||||
*/
|
||||
|
||||
Ref<WordDef> pop_exec_token(vm::Stack& stack);
|
||||
Ref<FiftCont> pop_exec_token(vm::Stack& stack);
|
||||
Ref<WordList> pop_word_list(vm::Stack& stack);
|
||||
void push_argcount(vm::Stack& stack, int args);
|
||||
} // namespace fift
|
||||
|
|
|
@ -37,25 +37,18 @@ td::Result<int> Fift::interpret_file(std::string fname, std::string current_dir,
|
|||
return td::Status::Error("cannot locate file `" + fname + "`");
|
||||
}
|
||||
auto file = r_file.move_as_ok();
|
||||
IntCtx ctx;
|
||||
std::stringstream ss(file.data);
|
||||
ctx.input_stream = &ss;
|
||||
ctx.filename = td::PathView(file.path).file_name().str();
|
||||
ctx.currentd_dir = td::PathView(file.path).parent_dir().str();
|
||||
ctx.include_depth = is_interactive ? 0 : 1;
|
||||
return do_interpret(ctx);
|
||||
IntCtx ctx{ss, td::PathView(file.path).file_name().str(), td::PathView(file.path).parent_dir().str(),
|
||||
(int)!is_interactive};
|
||||
return do_interpret(ctx, is_interactive);
|
||||
}
|
||||
|
||||
td::Result<int> Fift::interpret_istream(std::istream& stream, std::string current_dir, bool is_interactive) {
|
||||
IntCtx ctx;
|
||||
ctx.input_stream = &stream;
|
||||
ctx.filename = "stdin";
|
||||
ctx.currentd_dir = current_dir;
|
||||
ctx.include_depth = is_interactive ? 0 : 1;
|
||||
return do_interpret(ctx);
|
||||
IntCtx ctx{stream, "stdin", current_dir, (int)!is_interactive};
|
||||
return do_interpret(ctx, is_interactive);
|
||||
}
|
||||
|
||||
td::Result<int> Fift::do_interpret(IntCtx& ctx) {
|
||||
td::Result<int> Fift::do_interpret(IntCtx& ctx, bool is_interactive) {
|
||||
ctx.ton_db = &config_.ton_db;
|
||||
ctx.source_lookup = &config_.source_lookup;
|
||||
ctx.dictionary = &config_.dictionary;
|
||||
|
@ -64,13 +57,26 @@ td::Result<int> Fift::do_interpret(IntCtx& ctx) {
|
|||
if (!ctx.output_stream) {
|
||||
return td::Status::Error("Cannot run interpreter without output_stream");
|
||||
}
|
||||
try {
|
||||
return funny_interpret_loop(ctx);
|
||||
} catch (fift::IntError ab) {
|
||||
return td::Status::Error(ab.msg);
|
||||
} catch (fift::Quit q) {
|
||||
return q.res;
|
||||
while (true) {
|
||||
auto res = ctx.run(td::make_ref<InterpretCont>());
|
||||
if (res.is_error()) {
|
||||
res = ctx.add_error_loc(res.move_as_error());
|
||||
if (config_.show_backtrace) {
|
||||
std::ostringstream os;
|
||||
ctx.print_error_backtrace(os);
|
||||
LOG(ERROR) << os.str();
|
||||
}
|
||||
if (is_interactive) {
|
||||
LOG(ERROR) << res.move_as_error().message();
|
||||
ctx.top_ctx();
|
||||
ctx.clear_error();
|
||||
ctx.stack.clear();
|
||||
ctx.load_next_line();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace fift
|
||||
|
|
|
@ -26,7 +26,6 @@
|
|||
|
||||
namespace fift {
|
||||
struct IntCtx;
|
||||
int funny_interpret_loop(IntCtx& ctx);
|
||||
|
||||
struct Fift {
|
||||
public:
|
||||
|
@ -36,6 +35,7 @@ struct Fift {
|
|||
fift::Dictionary dictionary;
|
||||
std::ostream* output_stream{&std::cout};
|
||||
std::ostream* error_stream{&std::cerr};
|
||||
bool show_backtrace{true};
|
||||
};
|
||||
// Fift must own ton_db and dictionary, no concurrent access is allowed
|
||||
explicit Fift(Config config);
|
||||
|
@ -48,6 +48,6 @@ struct Fift {
|
|||
private:
|
||||
Config config_;
|
||||
|
||||
td::Result<int> do_interpret(IntCtx& ctx);
|
||||
td::Result<int> do_interpret(IntCtx& ctx, bool is_interactive = false);
|
||||
};
|
||||
} // namespace fift
|
||||
|
|
|
@ -68,33 +68,69 @@ void CharClassifier::set_char_class(int c, int cl) {
|
|||
}
|
||||
|
||||
IntCtx::Savepoint::Savepoint(IntCtx& _ctx, std::string new_filename, std::string new_current_dir,
|
||||
std::istream* new_input_stream)
|
||||
std::unique_ptr<std::istream> new_input_stream)
|
||||
: ctx(_ctx)
|
||||
, old_line_no(_ctx.line_no)
|
||||
, old_need_line(_ctx.need_line)
|
||||
, old_filename(_ctx.filename)
|
||||
, old_current_dir(_ctx.currentd_dir)
|
||||
, old_input_stream(_ctx.input_stream)
|
||||
, old_input_stream_holder(std::move(_ctx.input_stream_holder))
|
||||
, old_curline(_ctx.str)
|
||||
, old_curpos(_ctx.input_ptr - _ctx.str.c_str()) {
|
||||
, old_curpos(_ctx.input_ptr - _ctx.str.c_str())
|
||||
, old_word(_ctx.word) {
|
||||
ctx.line_no = 0;
|
||||
ctx.filename = new_filename;
|
||||
ctx.currentd_dir = new_current_dir;
|
||||
ctx.input_stream = new_input_stream;
|
||||
ctx.input_stream = new_input_stream.get();
|
||||
ctx.input_stream_holder = std::move(new_input_stream);
|
||||
ctx.str = "";
|
||||
ctx.input_ptr = 0;
|
||||
++(ctx.include_depth);
|
||||
}
|
||||
|
||||
IntCtx::Savepoint::~Savepoint() {
|
||||
bool IntCtx::Savepoint::restore(IntCtx& _ctx) {
|
||||
if (restored || &ctx != &_ctx) {
|
||||
return false;
|
||||
}
|
||||
ctx.line_no = old_line_no;
|
||||
ctx.need_line = old_need_line;
|
||||
ctx.filename = old_filename;
|
||||
ctx.currentd_dir = old_current_dir;
|
||||
ctx.input_stream = old_input_stream;
|
||||
ctx.input_stream_holder = std::move(old_input_stream_holder);
|
||||
ctx.str = old_curline;
|
||||
ctx.input_ptr = ctx.str.c_str() + old_curpos;
|
||||
ctx.word = old_word;
|
||||
--(ctx.include_depth);
|
||||
return restored = true;
|
||||
}
|
||||
|
||||
bool IntCtx::enter_ctx(std::string new_filename, std::string new_current_dir,
|
||||
std::unique_ptr<std::istream> new_input_stream) {
|
||||
if (!new_input_stream) {
|
||||
return false;
|
||||
}
|
||||
ctx_save_stack.emplace_back(*this, std::move(new_filename), std::move(new_current_dir), std::move(new_input_stream));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IntCtx::leave_ctx() {
|
||||
if (ctx_save_stack.empty()) {
|
||||
return false;
|
||||
}
|
||||
bool ok = ctx_save_stack.back().restore(*this);
|
||||
ctx_save_stack.pop_back();
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool IntCtx::top_ctx() {
|
||||
while (!ctx_save_stack.empty()) {
|
||||
if (!leave_ctx()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IntCtx::load_next_line() {
|
||||
|
@ -194,4 +230,99 @@ void IntCtx::check_int_exec() const {
|
|||
throw IntError{"internal interpret mode only"};
|
||||
}
|
||||
}
|
||||
|
||||
bool IntCtx::print_error_backtrace(std::ostream& os) const {
|
||||
if (exc_cont.is_null() && exc_next.is_null()) {
|
||||
os << "(no backtrace)\n";
|
||||
return false;
|
||||
}
|
||||
if (exc_cont.not_null()) {
|
||||
os << "top: ";
|
||||
exc_cont->dump(os, *this);
|
||||
}
|
||||
return print_backtrace(os, exc_next);
|
||||
}
|
||||
|
||||
bool IntCtx::print_backtrace(std::ostream& os, Ref<FiftCont> cont) const {
|
||||
for (int i = 1; cont.not_null() && i <= 16; i++) {
|
||||
os << "level " << i << ": ";
|
||||
cont->dump(os, *this);
|
||||
cont = cont->up();
|
||||
}
|
||||
if (cont.not_null()) {
|
||||
os << "... more levels ...\n";
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Ref<FiftCont> IntCtx::throw_exception(td::Status err, Ref<FiftCont> cur) {
|
||||
exc_cont = std::move(cur);
|
||||
exc_next = std::move(next);
|
||||
error = std::move(err);
|
||||
next.clear();
|
||||
auto cont = std::move(exc_handler);
|
||||
if (cont.is_null()) {
|
||||
return {}; // no Fift exception handler set
|
||||
} else if (cont.is_unique()) {
|
||||
return cont.unique_write().handle_modify(*this);
|
||||
} else {
|
||||
return cont->handle_tail(*this);
|
||||
}
|
||||
}
|
||||
|
||||
void IntCtx::clear_error() {
|
||||
error = td::Status::OK();
|
||||
exit_code = 0;
|
||||
}
|
||||
|
||||
td::Result<int> IntCtx::get_result() {
|
||||
if (error.is_error()) {
|
||||
return error.move_as_error();
|
||||
} else {
|
||||
return exit_code;
|
||||
}
|
||||
}
|
||||
|
||||
td::Status IntCtx::add_error_loc(td::Status err) const {
|
||||
if (err.is_error()) {
|
||||
std::ostringstream os;
|
||||
if (include_depth && line_no) {
|
||||
os << filename << ":" << line_no << ":\t";
|
||||
}
|
||||
if (!word.empty()) {
|
||||
os << word << ":";
|
||||
}
|
||||
return err.move_as_error_prefix(os.str());
|
||||
} else {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
td::Result<int> IntCtx::run(Ref<FiftCont> cont) {
|
||||
clear_error();
|
||||
while (cont.not_null()) {
|
||||
try {
|
||||
if (cont.is_unique()) {
|
||||
cont = cont.unique_write().run_modify(*this);
|
||||
} else {
|
||||
cont = cont->run_tail(*this);
|
||||
}
|
||||
} catch (IntError& err) {
|
||||
cont = throw_exception(td::Status::Error(err.msg), std::move(cont));
|
||||
} catch (vm::VmError& err) {
|
||||
cont = throw_exception(err.as_status(), std::move(cont));
|
||||
} catch (vm::VmVirtError& err) {
|
||||
cont = throw_exception(err.as_status(), std::move(cont));
|
||||
} catch (vm::CellBuilder::CellWriteError&) {
|
||||
cont = throw_exception(td::Status::Error("Cell builder write error"), std::move(cont));
|
||||
} catch (vm::VmFatal&) {
|
||||
cont = throw_exception(td::Status::Error("fatal vm error"), std::move(cont));
|
||||
}
|
||||
if (cont.is_null()) {
|
||||
cont = std::move(next);
|
||||
}
|
||||
}
|
||||
return get_result();
|
||||
}
|
||||
|
||||
} // namespace fift
|
||||
|
|
|
@ -22,6 +22,11 @@
|
|||
#include "crypto/vm/stack.hpp"
|
||||
#include "crypto/common/bitstring.h"
|
||||
|
||||
#include "td/utils/Status.h"
|
||||
|
||||
#include "Dictionary.h"
|
||||
#include "Continuation.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
|
@ -65,13 +70,18 @@ class CharClassifier {
|
|||
|
||||
struct IntCtx {
|
||||
vm::Stack stack;
|
||||
Ref<FiftCont> next, exc_handler;
|
||||
Ref<FiftCont> exc_cont, exc_next;
|
||||
int state{0};
|
||||
int include_depth{0};
|
||||
int line_no{0};
|
||||
int exit_code{0};
|
||||
td::Status error;
|
||||
bool need_line{true};
|
||||
std::string filename;
|
||||
std::string currentd_dir;
|
||||
std::istream* input_stream{nullptr};
|
||||
std::unique_ptr<std::istream> input_stream_holder;
|
||||
std::ostream* output_stream{nullptr};
|
||||
std::ostream* error_stream{nullptr};
|
||||
|
||||
|
@ -79,18 +89,50 @@ struct IntCtx {
|
|||
Dictionary* dictionary{nullptr};
|
||||
SourceLookup* source_lookup{nullptr};
|
||||
int* now{nullptr};
|
||||
std::string word;
|
||||
|
||||
private:
|
||||
std::string str;
|
||||
const char* input_ptr;
|
||||
const char* input_ptr = nullptr;
|
||||
|
||||
class Savepoint {
|
||||
IntCtx& ctx;
|
||||
int old_line_no;
|
||||
bool old_need_line;
|
||||
bool restored{false};
|
||||
std::string old_filename;
|
||||
std::string old_current_dir;
|
||||
std::istream* old_input_stream;
|
||||
std::unique_ptr<std::istream> old_input_stream_holder;
|
||||
std::string old_curline;
|
||||
std::ptrdiff_t old_curpos;
|
||||
std::string old_word;
|
||||
|
||||
public:
|
||||
Savepoint(IntCtx& _ctx, std::string new_filename, std::string new_current_dir,
|
||||
std::unique_ptr<std::istream> new_input_stream);
|
||||
bool restore(IntCtx& _ctx);
|
||||
};
|
||||
|
||||
std::vector<Savepoint> ctx_save_stack;
|
||||
|
||||
public:
|
||||
IntCtx() = default;
|
||||
IntCtx(std::istream& _istream, std::string _filename, std::string _curdir = "", int _depth = 0)
|
||||
: include_depth(_depth)
|
||||
, filename(std::move(_filename))
|
||||
, currentd_dir(std::move(_curdir))
|
||||
, input_stream(&_istream) {
|
||||
}
|
||||
|
||||
operator vm::Stack&() {
|
||||
return stack;
|
||||
}
|
||||
|
||||
bool enter_ctx(std::string new_filename, std::string new_current_dir, std::unique_ptr<std::istream> new_input_stream);
|
||||
bool leave_ctx();
|
||||
bool top_ctx();
|
||||
|
||||
td::Slice scan_word_to(char delim, bool err_endl = true);
|
||||
td::Slice scan_word();
|
||||
td::Slice scan_word_ext(const CharClassifier& classifier);
|
||||
|
@ -127,25 +169,29 @@ struct IntCtx {
|
|||
state = 0;
|
||||
stack.clear();
|
||||
}
|
||||
class Savepoint {
|
||||
IntCtx& ctx;
|
||||
int old_line_no;
|
||||
bool old_need_line;
|
||||
std::string old_filename;
|
||||
std::string old_current_dir;
|
||||
std::istream* old_input_stream;
|
||||
std::string old_curline;
|
||||
std::ptrdiff_t old_curpos;
|
||||
|
||||
public:
|
||||
Savepoint(IntCtx& _ctx, std::string new_filename, std::string new_current_dir, std::istream* new_input_stream);
|
||||
~Savepoint();
|
||||
};
|
||||
|
||||
void check_compile() const;
|
||||
void check_execute() const;
|
||||
void check_not_int_exec() const;
|
||||
void check_int_exec() const;
|
||||
|
||||
bool print_error_backtrace(std::ostream& os) const;
|
||||
bool print_backtrace(std::ostream& os, Ref<FiftCont> cont) const;
|
||||
|
||||
td::Status add_error_loc(td::Status err) const;
|
||||
|
||||
void set_exit_code(int err_code) {
|
||||
exit_code = err_code;
|
||||
}
|
||||
int get_exit_code() const {
|
||||
return exit_code;
|
||||
}
|
||||
|
||||
void clear_error();
|
||||
td::Result<int> get_result();
|
||||
|
||||
Ref<FiftCont> throw_exception(td::Status err, Ref<FiftCont> cur = {});
|
||||
td::Result<int> run(Ref<FiftCont> cont);
|
||||
};
|
||||
|
||||
td::StringBuilder& operator<<(td::StringBuilder& os, const IntCtx& ctx);
|
||||
|
|
|
@ -28,6 +28,12 @@ namespace {
|
|||
std::string fift_dir(std::string dir) {
|
||||
return dir.size() > 0 ? dir : td::PathView(td::realpath(__FILE__).move_as_ok()).parent_dir().str() + "lib/";
|
||||
}
|
||||
std::string smartcont_dir(std::string dir) {
|
||||
return dir.size() > 0
|
||||
? dir
|
||||
: td::PathView(td::PathView(td::realpath(__FILE__).move_as_ok()).parent_dir_noslash()).parent_dir().str() +
|
||||
"smartcont/";
|
||||
}
|
||||
td::Result<std::string> load_source(std::string name, std::string dir = "") {
|
||||
return td::read_file_str(fift_dir(dir) + name);
|
||||
}
|
||||
|
@ -49,6 +55,9 @@ td::Result<std::string> load_Lisp_fif(std::string dir = "") {
|
|||
td::Result<std::string> load_GetOpt_fif(std::string dir = "") {
|
||||
return load_source("GetOpt.fif", dir);
|
||||
}
|
||||
td::Result<std::string> load_wallet3_code_fif(std::string dir = "") {
|
||||
return td::read_file_str(smartcont_dir(dir) + "wallet-v3-code.fif");
|
||||
}
|
||||
|
||||
class MemoryFileLoader : public fift::FileLoader {
|
||||
public:
|
||||
|
@ -98,7 +107,7 @@ class MemoryFileLoader : public fift::FileLoader {
|
|||
|
||||
td::Result<fift::SourceLookup> create_source_lookup(std::string main, bool need_preamble = true, bool need_asm = true,
|
||||
bool need_ton_util = true, bool need_lisp = true,
|
||||
std::string dir = "") {
|
||||
bool need_w3_code = true, std::string dir = "") {
|
||||
auto loader = std::make_unique<MemoryFileLoader>();
|
||||
loader->add_file("/main.fif", std::move(main));
|
||||
if (need_preamble) {
|
||||
|
@ -127,6 +136,10 @@ td::Result<fift::SourceLookup> create_source_lookup(std::string main, bool need_
|
|||
TRY_RESULT(f, load_Lisp_fif(dir));
|
||||
loader->add_file("/Lisp.fif", std::move(f));
|
||||
}
|
||||
if (need_w3_code) {
|
||||
TRY_RESULT(f, load_wallet3_code_fif(dir));
|
||||
loader->add_file("/wallet-v3-code.fif", std::move(f));
|
||||
}
|
||||
auto res = fift::SourceLookup(std::move(loader));
|
||||
res.add_include_path("/");
|
||||
return std::move(res);
|
||||
|
@ -158,7 +171,7 @@ td::Result<fift::SourceLookup> run_fift(fift::SourceLookup source_lookup, std::o
|
|||
} // namespace
|
||||
td::Result<FiftOutput> mem_run_fift(std::string source, std::vector<std::string> args, std::string fift_dir) {
|
||||
std::stringstream ss;
|
||||
TRY_RESULT(source_lookup, create_source_lookup(source, true, true, true, true, fift_dir));
|
||||
TRY_RESULT(source_lookup, create_source_lookup(source, true, true, true, true, true, fift_dir));
|
||||
TRY_RESULT_ASSIGN(source_lookup, run_fift(std::move(source_lookup), &ss, true, std::move(args)));
|
||||
FiftOutput res;
|
||||
res.source_lookup = std::move(source_lookup);
|
||||
|
@ -174,8 +187,9 @@ td::Result<FiftOutput> mem_run_fift(SourceLookup source_lookup, std::vector<std:
|
|||
return std::move(res);
|
||||
}
|
||||
td::Result<fift::SourceLookup> create_mem_source_lookup(std::string main, std::string fift_dir, bool need_preamble,
|
||||
bool need_asm, bool need_ton_util, bool need_lisp) {
|
||||
return create_source_lookup(main, need_preamble, need_asm, need_ton_util, need_lisp, fift_dir);
|
||||
bool need_asm, bool need_ton_util, bool need_lisp,
|
||||
bool need_w3_code) {
|
||||
return create_source_lookup(main, need_preamble, need_asm, need_ton_util, need_lisp, need_w3_code, fift_dir);
|
||||
}
|
||||
|
||||
td::Result<td::Ref<vm::Cell>> compile_asm(td::Slice asm_code, std::string fift_dir, bool is_raw) {
|
||||
|
@ -183,7 +197,7 @@ td::Result<td::Ref<vm::Cell>> compile_asm(td::Slice asm_code, std::string fift_d
|
|||
TRY_RESULT(source_lookup,
|
||||
create_source_lookup(PSTRING() << "\"Asm.fif\" include\n " << (is_raw ? "<{" : "") << asm_code << "\n"
|
||||
<< (is_raw ? "}>c" : "") << " boc>B \"res\" B>file",
|
||||
true, true, true, false, fift_dir));
|
||||
true, true, true, false, false, fift_dir));
|
||||
TRY_RESULT(res, run_fift(std::move(source_lookup), &ss));
|
||||
TRY_RESULT(boc, res.read_file("res"));
|
||||
return vm::std_boc_deserialize(std::move(boc.data));
|
||||
|
|
|
@ -28,7 +28,8 @@ struct FiftOutput {
|
|||
};
|
||||
td::Result<fift::SourceLookup> create_mem_source_lookup(std::string main, std::string fift_dir = "",
|
||||
bool need_preamble = true, bool need_asm = true,
|
||||
bool need_ton_util = true, bool need_lisp = true);
|
||||
bool need_ton_util = true, bool need_lisp = true,
|
||||
bool need_w3_code = true);
|
||||
td::Result<FiftOutput> mem_run_fift(std::string source, std::vector<std::string> args = {}, std::string fift_dir = "");
|
||||
td::Result<FiftOutput> mem_run_fift(SourceLookup source_lookup, std::vector<std::string> args);
|
||||
td::Result<td::Ref<vm::Cell>> compile_asm(td::Slice asm_code, std::string fift_dir = "", bool is_raw = true);
|
||||
|
|
|
@ -62,7 +62,7 @@ void show_total_cells(std::ostream& stream) {
|
|||
stream << "total cells = " << vm::DataCell::get_total_data_cells() << std::endl;
|
||||
}
|
||||
|
||||
void do_compile(vm::Stack& stack, Ref<WordDef> word_def);
|
||||
void do_compile(vm::Stack& stack, Ref<FiftCont> word_def);
|
||||
void do_compile_literals(vm::Stack& stack, int count);
|
||||
|
||||
void interpret_dot(IntCtx& ctx, bool space_after) {
|
||||
|
@ -204,18 +204,6 @@ void interpret_negate(vm::Stack& stack) {
|
|||
stack.push_int(-stack.pop_int());
|
||||
}
|
||||
|
||||
void interpret_const(vm::Stack& stack, long long val) {
|
||||
stack.push_smallint(val);
|
||||
}
|
||||
|
||||
void interpret_big_const(vm::Stack& stack, td::RefInt256 val) {
|
||||
stack.push_int(std::move(val));
|
||||
}
|
||||
|
||||
void interpret_literal(vm::Stack& stack, vm::StackEntry se) {
|
||||
stack.push(std::move(se));
|
||||
}
|
||||
|
||||
void interpret_cmp(vm::Stack& stack, const char opt[3]) {
|
||||
auto y = stack.pop_int();
|
||||
auto x = stack.pop_int();
|
||||
|
@ -537,7 +525,8 @@ void interpret_hold(vm::Stack& stack) {
|
|||
stack.check_underflow(2);
|
||||
char buffer[8];
|
||||
unsigned len = make_utf8_char(buffer, stack.pop_smallint_range(0x10ffff, -128));
|
||||
std::string s = stack.pop_string() + std::string{buffer, len};
|
||||
std::string s = stack.pop_string();
|
||||
s.append(buffer, len);
|
||||
stack.push_string(std::move(s));
|
||||
}
|
||||
|
||||
|
@ -1567,78 +1556,183 @@ void interpret_dict_get(vm::Stack& stack, int sgnd, int mode) {
|
|||
}
|
||||
}
|
||||
|
||||
void interpret_dict_map(IntCtx& ctx, bool ext, bool sgnd) {
|
||||
auto func = pop_exec_token(ctx);
|
||||
int n = ctx.stack.pop_smallint_range(vm::Dictionary::max_key_bits);
|
||||
vm::Dictionary dict{ctx.stack.pop_maybe_cell(), n}, dict2{n};
|
||||
for (auto entry : dict.range(false, sgnd)) {
|
||||
ctx.stack.push_builder(Ref<vm::CellBuilder>{true});
|
||||
if (ext) {
|
||||
ctx.stack.push_int(dict.key_as_integer(entry.first, sgnd));
|
||||
}
|
||||
ctx.stack.push_cellslice(std::move(entry.second));
|
||||
func->run(ctx);
|
||||
if (ctx.stack.pop_bool()) {
|
||||
if (!dict2.set_builder(entry.first, n, ctx.stack.pop_builder())) {
|
||||
throw IntError{"cannot insert value into dictionary"};
|
||||
}
|
||||
}
|
||||
};
|
||||
ctx.stack.push_maybe_cell(std::move(dict2).extract_root_cell());
|
||||
class DictMapCont : public LoopCont {
|
||||
int n;
|
||||
bool ext;
|
||||
bool sgnd;
|
||||
vm::Dictionary dict, dict2;
|
||||
vm::DictIterator it;
|
||||
|
||||
public:
|
||||
DictMapCont(Ref<FiftCont> _func, Ref<FiftCont> _after, int _n, Ref<vm::Cell> dict_root, bool _ext, bool _sgnd)
|
||||
: LoopCont(std::move(_func), std::move(_after))
|
||||
, n(_n)
|
||||
, ext(_ext)
|
||||
, sgnd(_sgnd)
|
||||
, dict(std::move(dict_root), n)
|
||||
, dict2(n) {
|
||||
}
|
||||
DictMapCont(const DictMapCont&) = default;
|
||||
DictMapCont* make_copy() const override {
|
||||
return new DictMapCont(*this);
|
||||
}
|
||||
bool init(IntCtx& ctx) override {
|
||||
it = dict.init_iterator(false, sgnd);
|
||||
return true;
|
||||
}
|
||||
bool pre_exec(IntCtx& ctx) override;
|
||||
bool post_exec(IntCtx& ctx) override;
|
||||
bool finalize(IntCtx& ctx) override;
|
||||
};
|
||||
|
||||
bool DictMapCont::pre_exec(IntCtx& ctx) {
|
||||
if (it.eof()) {
|
||||
return false;
|
||||
}
|
||||
ctx.stack.push_builder(td::make_ref<vm::CellBuilder>());
|
||||
if (ext) {
|
||||
ctx.stack.push_int(dict.key_as_integer(it.cur_pos(), sgnd));
|
||||
}
|
||||
ctx.stack.push_cellslice(it.cur_value());
|
||||
return true;
|
||||
}
|
||||
|
||||
void interpret_dict_foreach(IntCtx& ctx, bool reverse, bool sgnd) {
|
||||
bool DictMapCont::post_exec(IntCtx& ctx) {
|
||||
if (ctx.stack.pop_bool()) {
|
||||
if (!dict2.set_builder(it.cur_pos(), n, ctx.stack.pop_builder())) {
|
||||
throw IntError{"cannot insert value into dictionary"};
|
||||
}
|
||||
}
|
||||
return !(++it).eof();
|
||||
}
|
||||
|
||||
bool DictMapCont::finalize(IntCtx& ctx) {
|
||||
ctx.stack.push_maybe_cell(std::move(dict2).extract_root_cell());
|
||||
return true;
|
||||
}
|
||||
|
||||
Ref<FiftCont> interpret_dict_map(IntCtx& ctx, bool ext, bool sgnd) {
|
||||
auto func = pop_exec_token(ctx);
|
||||
int n = ctx.stack.pop_smallint_range(vm::Dictionary::max_key_bits);
|
||||
vm::Dictionary dict{ctx.stack.pop_maybe_cell(), n};
|
||||
for (auto entry : dict.range(reverse, sgnd)) {
|
||||
ctx.stack.push_int(dict.key_as_integer(entry.first, sgnd));
|
||||
ctx.stack.push_cellslice(std::move(entry.second));
|
||||
func->run(ctx);
|
||||
if (!ctx.stack.pop_bool()) {
|
||||
ctx.stack.push_bool(false);
|
||||
return;
|
||||
}
|
||||
};
|
||||
ctx.stack.push_bool(true);
|
||||
return td::make_ref<DictMapCont>(std::move(func), std::move(ctx.next), n, ctx.stack.pop_maybe_cell(), ext, sgnd);
|
||||
}
|
||||
|
||||
class DictIterCont : public LoopCont {
|
||||
int n;
|
||||
bool reverse;
|
||||
bool sgnd;
|
||||
bool ok;
|
||||
bool inited{false};
|
||||
vm::Dictionary dict;
|
||||
vm::DictIterator it;
|
||||
|
||||
public:
|
||||
DictIterCont(Ref<FiftCont> _func, Ref<FiftCont> _after, int _n, Ref<vm::Cell> dict_root, bool _reverse, bool _sgnd)
|
||||
: LoopCont(std::move(_func), std::move(_after))
|
||||
, n(_n)
|
||||
, reverse(_reverse)
|
||||
, sgnd(_sgnd)
|
||||
, ok(true)
|
||||
, dict(std::move(dict_root), n) {
|
||||
}
|
||||
DictIterCont(const DictIterCont&) = default;
|
||||
DictIterCont* make_copy() const override {
|
||||
return new DictIterCont(*this);
|
||||
}
|
||||
bool do_init();
|
||||
bool init(IntCtx& ctx) override {
|
||||
return do_init();
|
||||
}
|
||||
bool pre_exec(IntCtx& ctx) override;
|
||||
bool post_exec(IntCtx& ctx) override;
|
||||
bool finalize(IntCtx& ctx) override;
|
||||
template <typename T>
|
||||
bool lookup(const T& key, bool strict, bool backw) {
|
||||
return do_init() && it.lookup(key, strict, backw);
|
||||
}
|
||||
};
|
||||
|
||||
bool DictIterCont::do_init() {
|
||||
if (!inited) {
|
||||
it = dict.init_iterator(reverse, sgnd);
|
||||
inited = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DictIterCont::pre_exec(IntCtx& ctx) {
|
||||
if (it.eof()) {
|
||||
return false;
|
||||
}
|
||||
ctx.stack.push_int(dict.key_as_integer(it.cur_pos(), sgnd));
|
||||
ctx.stack.push_cellslice(it.cur_value());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DictIterCont::post_exec(IntCtx& ctx) {
|
||||
ok = ctx.stack.pop_bool();
|
||||
return ok && !(++it).eof();
|
||||
}
|
||||
|
||||
bool DictIterCont::finalize(IntCtx& ctx) {
|
||||
ctx.stack.push_bool(ok);
|
||||
return true;
|
||||
}
|
||||
|
||||
Ref<FiftCont> interpret_dict_foreach(IntCtx& ctx, bool reverse, bool sgnd) {
|
||||
auto func = pop_exec_token(ctx);
|
||||
int n = ctx.stack.pop_smallint_range(vm::Dictionary::max_key_bits);
|
||||
return td::make_ref<DictIterCont>(std::move(func), std::move(ctx.next), n, ctx.stack.pop_maybe_cell(), reverse, sgnd);
|
||||
}
|
||||
|
||||
// mode: +1 = reverse, +2 = signed, +4 = strict, +8 = lookup backwards, +16 = with hint
|
||||
void interpret_dict_foreach_from(IntCtx& ctx, int mode) {
|
||||
Ref<FiftCont> interpret_dict_foreach_from(IntCtx& ctx, int mode) {
|
||||
if (mode < 0) {
|
||||
mode = ctx.stack.pop_smallint_range(31);
|
||||
}
|
||||
auto func = pop_exec_token(ctx);
|
||||
int n = ctx.stack.pop_smallint_range(vm::Dictionary::max_key_bits);
|
||||
vm::Dictionary dict{ctx.stack.pop_maybe_cell(), n};
|
||||
vm::DictIterator it{dict, mode & 3};
|
||||
unsigned char buffer[vm::Dictionary::max_key_bytes];
|
||||
auto it_cont = td::make_ref<DictIterCont>(std::move(func), std::move(ctx.next), n, ctx.stack.pop_maybe_cell(),
|
||||
mode & 1, mode & 2);
|
||||
for (int s = (mode >> 4) & 1; s >= 0; --s) {
|
||||
auto key = dict.integer_key(ctx.stack.pop_int(), n, mode & 2, buffer);
|
||||
unsigned char buffer[vm::Dictionary::max_key_bytes];
|
||||
auto key = vm::Dictionary::integer_key(ctx.stack.pop_int(), n, mode & 2, buffer);
|
||||
if (!key.is_valid()) {
|
||||
throw IntError{"not enough bits for a dictionary key"};
|
||||
}
|
||||
it.lookup(key, mode & 4, mode & 8);
|
||||
it_cont.write().lookup(key, mode & 4, mode & 8);
|
||||
}
|
||||
for (; !it.eof(); ++it) {
|
||||
ctx.stack.push_int(dict.key_as_integer(it.cur_pos(), mode & 2));
|
||||
ctx.stack.push_cellslice(it.cur_value());
|
||||
func->run(ctx);
|
||||
if (!ctx.stack.pop_bool()) {
|
||||
ctx.stack.push_bool(false);
|
||||
return;
|
||||
}
|
||||
};
|
||||
ctx.stack.push_bool(true);
|
||||
return it_cont;
|
||||
}
|
||||
|
||||
void interpret_dict_merge(IntCtx& ctx) {
|
||||
auto func = pop_exec_token(ctx);
|
||||
int n = ctx.stack.pop_smallint_range(vm::Dictionary::max_key_bits);
|
||||
vm::Dictionary dict2{ctx.stack.pop_maybe_cell(), n};
|
||||
vm::Dictionary dict1{ctx.stack.pop_maybe_cell(), n};
|
||||
vm::Dictionary dict3{n};
|
||||
auto it1 = dict1.begin(), it2 = dict2.begin();
|
||||
class DictMergeCont : public LoopCont {
|
||||
int n;
|
||||
vm::Dictionary dict1, dict2, dict3;
|
||||
vm::DictIterator it1, it2;
|
||||
|
||||
public:
|
||||
DictMergeCont(Ref<FiftCont> _func, Ref<FiftCont> _after, int _n, Ref<vm::Cell> dict1_root, Ref<vm::Cell> dict2_root)
|
||||
: LoopCont(std::move(_func), std::move(_after))
|
||||
, n(_n)
|
||||
, dict1(std::move(dict1_root), n)
|
||||
, dict2(std::move(dict2_root), n)
|
||||
, dict3(n) {
|
||||
}
|
||||
DictMergeCont(const DictMergeCont&) = default;
|
||||
DictMergeCont* make_copy() const override {
|
||||
return new DictMergeCont(*this);
|
||||
}
|
||||
bool init(IntCtx& ctx) override {
|
||||
it1 = dict1.begin();
|
||||
it2 = dict2.begin();
|
||||
return true;
|
||||
}
|
||||
bool pre_exec(IntCtx& ctx) override;
|
||||
bool post_exec(IntCtx& ctx) override;
|
||||
bool finalize(IntCtx& ctx) override;
|
||||
};
|
||||
|
||||
bool DictMergeCont::pre_exec(IntCtx& ctx) {
|
||||
while (!it1.eof() || !it2.eof()) {
|
||||
int c = it1.eof() ? 1 : (it2.eof() ? -1 : it1.cur_pos().compare(it2.cur_pos(), n));
|
||||
bool ok = true;
|
||||
|
@ -1652,29 +1746,67 @@ void interpret_dict_merge(IntCtx& ctx) {
|
|||
ctx.stack.push_builder(Ref<vm::CellBuilder>{true});
|
||||
ctx.stack.push_cellslice(it1.cur_value());
|
||||
ctx.stack.push_cellslice(it2.cur_value());
|
||||
func->run(ctx);
|
||||
if (ctx.stack.pop_bool()) {
|
||||
ok = dict3.set_builder(it1.cur_pos(), n, ctx.stack.pop_builder());
|
||||
}
|
||||
++it1;
|
||||
++it2;
|
||||
return true;
|
||||
}
|
||||
if (!ok) {
|
||||
throw IntError{"cannot insert value into dictionary"};
|
||||
}
|
||||
}
|
||||
ctx.stack.push_maybe_cell(std::move(dict3).extract_root_cell());
|
||||
return false;
|
||||
}
|
||||
|
||||
void interpret_dict_diff(IntCtx& ctx) {
|
||||
bool DictMergeCont::post_exec(IntCtx& ctx) {
|
||||
if (ctx.stack.pop_bool() && !dict3.set_builder(it1.cur_pos(), n, ctx.stack.pop_builder())) {
|
||||
throw IntError{"cannot insert value into dictionary"};
|
||||
}
|
||||
++it1;
|
||||
++it2;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DictMergeCont::finalize(IntCtx& ctx) {
|
||||
ctx.stack.push_maybe_cell(std::move(dict3).extract_root_cell());
|
||||
return true;
|
||||
}
|
||||
|
||||
Ref<FiftCont> interpret_dict_merge(IntCtx& ctx) {
|
||||
auto func = pop_exec_token(ctx);
|
||||
int n = ctx.stack.pop_smallint_range(vm::Dictionary::max_key_bits);
|
||||
vm::Dictionary dict2{ctx.stack.pop_maybe_cell(), n};
|
||||
vm::Dictionary dict1{ctx.stack.pop_maybe_cell(), n};
|
||||
auto it1 = dict1.begin(), it2 = dict2.begin();
|
||||
auto dict2_root = ctx.stack.pop_maybe_cell();
|
||||
return td::make_ref<DictMergeCont>(std::move(func), std::move(ctx.next), n, ctx.stack.pop_maybe_cell(),
|
||||
std::move(dict2_root));
|
||||
}
|
||||
|
||||
class DictDiffCont : public LoopCont {
|
||||
int n;
|
||||
bool ok{true};
|
||||
vm::Dictionary dict1, dict2;
|
||||
vm::DictIterator it1, it2;
|
||||
|
||||
public:
|
||||
DictDiffCont(Ref<FiftCont> _func, Ref<FiftCont> _after, int _n, Ref<vm::Cell> dict1_root, Ref<vm::Cell> dict2_root)
|
||||
: LoopCont(std::move(_func), std::move(_after))
|
||||
, n(_n)
|
||||
, dict1(std::move(dict1_root), n)
|
||||
, dict2(std::move(dict2_root), n) {
|
||||
}
|
||||
DictDiffCont(const DictDiffCont&) = default;
|
||||
DictDiffCont* make_copy() const override {
|
||||
return new DictDiffCont(*this);
|
||||
}
|
||||
bool init(IntCtx& ctx) override {
|
||||
it1 = dict1.begin();
|
||||
it2 = dict2.begin();
|
||||
return true;
|
||||
}
|
||||
bool pre_exec(IntCtx& ctx) override;
|
||||
bool post_exec(IntCtx& ctx) override;
|
||||
bool finalize(IntCtx& ctx) override;
|
||||
};
|
||||
|
||||
bool DictDiffCont::pre_exec(IntCtx& ctx) {
|
||||
while (!it1.eof() || !it2.eof()) {
|
||||
int c = it1.eof() ? 1 : (it2.eof() ? -1 : it1.cur_pos().compare(it2.cur_pos(), n));
|
||||
bool run = true;
|
||||
if (c < 0) {
|
||||
ctx.stack.push_int(dict1.key_as_integer(it1.cur_pos()));
|
||||
ctx.stack.push_cellslice(it1.cur_value());
|
||||
|
@ -1691,20 +1823,33 @@ void interpret_dict_diff(IntCtx& ctx) {
|
|||
ctx.stack.push_cellslice(it1.cur_value());
|
||||
ctx.stack.push_cellslice(it2.cur_value());
|
||||
} else {
|
||||
run = false;
|
||||
++it1;
|
||||
++it2;
|
||||
continue;
|
||||
}
|
||||
++it1;
|
||||
++it2;
|
||||
}
|
||||
if (run) {
|
||||
func->run(ctx);
|
||||
if (!ctx.stack.pop_bool()) {
|
||||
ctx.stack.push_bool(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
ctx.stack.push_bool(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DictDiffCont::post_exec(IntCtx& ctx) {
|
||||
return (ok = ctx.stack.pop_bool());
|
||||
}
|
||||
|
||||
bool DictDiffCont::finalize(IntCtx& ctx) {
|
||||
ctx.stack.push_bool(ok);
|
||||
return true;
|
||||
}
|
||||
|
||||
Ref<FiftCont> interpret_dict_diff(IntCtx& ctx) {
|
||||
auto func = pop_exec_token(ctx);
|
||||
int n = ctx.stack.pop_smallint_range(vm::Dictionary::max_key_bits);
|
||||
auto dict2_root = ctx.stack.pop_maybe_cell();
|
||||
return td::make_ref<DictDiffCont>(std::move(func), std::move(ctx.next), n, ctx.stack.pop_maybe_cell(),
|
||||
std::move(dict2_root));
|
||||
}
|
||||
|
||||
void interpret_pfx_dict_add(vm::Stack& stack, vm::Dictionary::SetMode mode, bool add_builder) {
|
||||
|
@ -1791,7 +1936,7 @@ void interpret_wordlist_begin(IntCtx& ctx) {
|
|||
void interpret_wordlist_end_aux(vm::Stack& stack) {
|
||||
Ref<WordList> wordlist_ref = pop_word_list(stack);
|
||||
wordlist_ref.write().close();
|
||||
stack.push({vm::from_object, Ref<WordDef>{wordlist_ref}});
|
||||
stack.push({vm::from_object, Ref<FiftCont>{wordlist_ref}});
|
||||
}
|
||||
|
||||
void interpret_wordlist_end(IntCtx& ctx) {
|
||||
|
@ -1846,7 +1991,7 @@ void interpret_create(IntCtx& ctx) {
|
|||
interpret_create_aux(ctx, 0);
|
||||
}
|
||||
|
||||
Ref<WordDef> create_aux_wd{Ref<CtxWord>{true, std::bind(interpret_create_aux, std::placeholders::_1, -1)}};
|
||||
Ref<FiftCont> create_aux_wd{Ref<CtxWord>{true, std::bind(interpret_create_aux, std::placeholders::_1, -1)}};
|
||||
|
||||
// { bl word <mode> 2 ' (create) } :: :
|
||||
void interpret_colon(IntCtx& ctx, int mode) {
|
||||
|
@ -2027,39 +2172,43 @@ void interpret_parse_hex_number(vm::Stack& stack) {
|
|||
}
|
||||
|
||||
void interpret_quit(IntCtx& ctx) {
|
||||
throw Quit{0};
|
||||
// TODO: change to correct behavior
|
||||
ctx.set_exit_code(0);
|
||||
ctx.next.clear();
|
||||
}
|
||||
|
||||
void interpret_bye(IntCtx& ctx) {
|
||||
throw Quit{-1};
|
||||
ctx.set_exit_code(-1);
|
||||
ctx.next.clear();
|
||||
}
|
||||
|
||||
void interpret_halt(vm::Stack& stack) {
|
||||
int code = stack.pop_smallint_range(255);
|
||||
throw Quit{~code};
|
||||
void interpret_halt(IntCtx& ctx) {
|
||||
ctx.set_exit_code(~ctx.stack.pop_smallint_range(255));
|
||||
ctx.next.clear();
|
||||
}
|
||||
|
||||
void interpret_abort(IntCtx& ctx) {
|
||||
throw IntError{ctx.stack.pop_string()};
|
||||
}
|
||||
|
||||
Ref<WordDef> interpret_execute(IntCtx& ctx) {
|
||||
Ref<FiftCont> interpret_execute(IntCtx& ctx) {
|
||||
return pop_exec_token(ctx);
|
||||
}
|
||||
|
||||
Ref<WordDef> interpret_execute_times(IntCtx& ctx) {
|
||||
Ref<FiftCont> interpret_execute_times(IntCtx& ctx) {
|
||||
int count = ctx.stack.pop_smallint_range(1000000000);
|
||||
auto wd_ref = pop_exec_token(ctx);
|
||||
if (!count) {
|
||||
auto body = pop_exec_token(ctx);
|
||||
if (count <= 0) {
|
||||
return {};
|
||||
}
|
||||
while (--count > 0) {
|
||||
wd_ref->run(ctx);
|
||||
if (count == 1) {
|
||||
return body;
|
||||
}
|
||||
return wd_ref;
|
||||
ctx.next = td::make_ref<TimesCont>(body, std::move(ctx.next), count - 1);
|
||||
return body;
|
||||
}
|
||||
|
||||
Ref<WordDef> interpret_if(IntCtx& ctx) {
|
||||
Ref<FiftCont> interpret_if(IntCtx& ctx) {
|
||||
auto true_ref = pop_exec_token(ctx);
|
||||
if (ctx.stack.pop_bool()) {
|
||||
return true_ref;
|
||||
|
@ -2068,7 +2217,7 @@ Ref<WordDef> interpret_if(IntCtx& ctx) {
|
|||
}
|
||||
}
|
||||
|
||||
Ref<WordDef> interpret_ifnot(IntCtx& ctx) {
|
||||
Ref<FiftCont> interpret_ifnot(IntCtx& ctx) {
|
||||
auto false_ref = pop_exec_token(ctx);
|
||||
if (ctx.stack.pop_bool()) {
|
||||
return {};
|
||||
|
@ -2077,7 +2226,7 @@ Ref<WordDef> interpret_ifnot(IntCtx& ctx) {
|
|||
}
|
||||
}
|
||||
|
||||
Ref<WordDef> interpret_cond(IntCtx& ctx) {
|
||||
Ref<FiftCont> interpret_cond(IntCtx& ctx) {
|
||||
auto false_ref = pop_exec_token(ctx);
|
||||
auto true_ref = pop_exec_token(ctx);
|
||||
if (ctx.stack.pop_bool()) {
|
||||
|
@ -2087,23 +2236,17 @@ Ref<WordDef> interpret_cond(IntCtx& ctx) {
|
|||
}
|
||||
}
|
||||
|
||||
void interpret_while(IntCtx& ctx) {
|
||||
auto body_ref = pop_exec_token(ctx);
|
||||
auto cond_ref = pop_exec_token(ctx);
|
||||
while (true) {
|
||||
cond_ref->run(ctx);
|
||||
if (!ctx.stack.pop_bool()) {
|
||||
break;
|
||||
}
|
||||
body_ref->run(ctx);
|
||||
}
|
||||
Ref<FiftCont> interpret_while(IntCtx& ctx) {
|
||||
auto body = pop_exec_token(ctx);
|
||||
auto cond = pop_exec_token(ctx);
|
||||
ctx.next = td::make_ref<WhileCont>(cond, std::move(body), std::move(ctx.next), true);
|
||||
return cond;
|
||||
}
|
||||
|
||||
void interpret_until(IntCtx& ctx) {
|
||||
auto body_ref = pop_exec_token(ctx);
|
||||
do {
|
||||
body_ref->run(ctx);
|
||||
} while (!ctx.stack.pop_bool());
|
||||
Ref<FiftCont> interpret_until(IntCtx& ctx) {
|
||||
auto body = pop_exec_token(ctx);
|
||||
ctx.next = td::make_ref<UntilCont>(body, std::move(ctx.next));
|
||||
return body;
|
||||
}
|
||||
|
||||
void interpret_tick(IntCtx& ctx) {
|
||||
|
@ -2133,25 +2276,39 @@ void interpret_find(IntCtx& ctx) {
|
|||
}
|
||||
}
|
||||
|
||||
void interpret_tick_nop(vm::Stack& stack) {
|
||||
stack.push({vm::from_object, Dictionary::nop_word_def});
|
||||
void interpret_leave_source(IntCtx& ctx) {
|
||||
if (!ctx.leave_ctx()) {
|
||||
throw IntError{"cannot leave included file interpretation context"};
|
||||
}
|
||||
}
|
||||
|
||||
void interpret_include(IntCtx& ctx) {
|
||||
Ref<FiftCont> interpret_include(IntCtx& ctx) {
|
||||
auto fname = ctx.stack.pop_string();
|
||||
auto r_file = ctx.source_lookup->lookup_source(fname, ctx.currentd_dir);
|
||||
if (r_file.is_error()) {
|
||||
throw IntError{"cannot locate file `" + fname + "`"};
|
||||
}
|
||||
auto file = r_file.move_as_ok();
|
||||
std::stringstream ss(std::move(file.data));
|
||||
IntCtx::Savepoint save{ctx, td::PathView(file.path).file_name().str(), td::PathView(file.path).parent_dir().str(),
|
||||
&ss};
|
||||
funny_interpret_loop(ctx);
|
||||
auto ss = std::make_unique<std::stringstream>(std::move(file.data));
|
||||
if (!ctx.enter_ctx(td::PathView(file.path).file_name().str(), td::PathView(file.path).parent_dir().str(),
|
||||
std::move(ss))) {
|
||||
throw IntError{"cannot enter included file interpretation context"};
|
||||
}
|
||||
ctx.next = SeqCont::seq(td::make_ref<CtxWord>(interpret_leave_source), std::move(ctx.next));
|
||||
return td::make_ref<InterpretCont>();
|
||||
}
|
||||
|
||||
void interpret_skip_source(vm::Stack& stack) {
|
||||
throw SkipToEof();
|
||||
td::Ref<vm::Box> exit_interpret{true};
|
||||
|
||||
Ref<FiftCont> interpret_skip_source(IntCtx& ctx) {
|
||||
auto cont = exit_interpret->get().as_object<FiftCont>();
|
||||
ctx.next.clear();
|
||||
/*
|
||||
if (cont.is_null()) {
|
||||
throw IntError{"no interpreter exit point set"};
|
||||
}
|
||||
*/
|
||||
return cont;
|
||||
}
|
||||
|
||||
void interpret_words(IntCtx& ctx) {
|
||||
|
@ -2161,6 +2318,14 @@ void interpret_words(IntCtx& ctx) {
|
|||
*ctx.output_stream << std::endl;
|
||||
}
|
||||
|
||||
void interpret_print_backtrace(IntCtx& ctx) {
|
||||
ctx.print_backtrace(*ctx.output_stream, ctx.next);
|
||||
}
|
||||
|
||||
void interpret_print_continuation(IntCtx& ctx) {
|
||||
ctx.print_backtrace(*ctx.output_stream, pop_exec_token(ctx));
|
||||
}
|
||||
|
||||
void interpret_pack_std_smc_addr(vm::Stack& stack) {
|
||||
block::StdAddress a;
|
||||
stack.check_underflow(3);
|
||||
|
@ -2450,17 +2615,17 @@ void interpret_get_fixed_cmdline_arg(vm::Stack& stack, int n) {
|
|||
}
|
||||
|
||||
// n -- executes $n
|
||||
void interpret_get_cmdline_arg(IntCtx& ctx) {
|
||||
Ref<FiftCont> interpret_get_cmdline_arg(IntCtx& ctx) {
|
||||
int n = ctx.stack.pop_smallint_range(999999);
|
||||
if (n) {
|
||||
interpret_get_fixed_cmdline_arg(ctx.stack, n);
|
||||
return;
|
||||
return {};
|
||||
}
|
||||
auto entry = ctx.dictionary->lookup("$0 ");
|
||||
if (!entry) {
|
||||
throw IntError{"-?"};
|
||||
} else {
|
||||
(*entry)(ctx);
|
||||
return entry->get_def();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2493,16 +2658,16 @@ void interpret_getenv_exists(vm::Stack& stack) {
|
|||
}
|
||||
|
||||
// x1 .. xn n 'w -->
|
||||
void interpret_execute_internal(IntCtx& ctx) {
|
||||
Ref<WordDef> word_def = pop_exec_token(ctx);
|
||||
Ref<FiftCont> interpret_execute_internal(IntCtx& ctx) {
|
||||
Ref<FiftCont> word_def = pop_exec_token(ctx);
|
||||
int count = ctx.stack.pop_smallint_range(255);
|
||||
ctx.stack.check_underflow(count);
|
||||
word_def->run(ctx);
|
||||
return word_def;
|
||||
}
|
||||
|
||||
// wl x1 .. xn n 'w --> wl'
|
||||
void interpret_compile_internal(vm::Stack& stack) {
|
||||
Ref<WordDef> word_def = pop_exec_token(stack);
|
||||
Ref<FiftCont> word_def = pop_exec_token(stack);
|
||||
int count = stack.pop_smallint_range(255);
|
||||
do_compile_literals(stack, count);
|
||||
if (word_def != Dictionary::nop_word_def) {
|
||||
|
@ -2510,12 +2675,14 @@ void interpret_compile_internal(vm::Stack& stack) {
|
|||
}
|
||||
}
|
||||
|
||||
void do_compile(vm::Stack& stack, Ref<WordDef> word_def) {
|
||||
void do_compile(vm::Stack& stack, Ref<FiftCont> word_def) {
|
||||
Ref<WordList> wl_ref = pop_word_list(stack);
|
||||
if (word_def != Dictionary::nop_word_def) {
|
||||
if ((td::uint64)word_def->list_size() <= 1) {
|
||||
auto list_size = word_def->list_size();
|
||||
if ((td::uint64)list_size <= 1) {
|
||||
// inline short definitions
|
||||
wl_ref.write().append(*(word_def->get_list()));
|
||||
auto list = word_def->get_list();
|
||||
wl_ref.write().append(list, list + list_size);
|
||||
} else {
|
||||
wl_ref.write().push_back(word_def);
|
||||
}
|
||||
|
@ -2524,19 +2691,7 @@ void do_compile(vm::Stack& stack, Ref<WordDef> word_def) {
|
|||
}
|
||||
|
||||
void compile_one_literal(WordList& wlist, vm::StackEntry val) {
|
||||
using namespace std::placeholders;
|
||||
if (val.type() == vm::StackEntry::t_int) {
|
||||
auto x = std::move(val).as_int();
|
||||
if (!x->signed_fits_bits(257)) {
|
||||
throw IntError{"invalid numeric literal"};
|
||||
} else if (x->signed_fits_bits(td::BigIntInfo::word_shift)) {
|
||||
wlist.push_back(Ref<StackWord>{true, std::bind(interpret_const, _1, x->to_long())});
|
||||
} else {
|
||||
wlist.push_back(Ref<StackWord>{true, std::bind(interpret_big_const, _1, std::move(x))});
|
||||
}
|
||||
} else {
|
||||
wlist.push_back(Ref<StackWord>{true, std::bind(interpret_literal, _1, std::move(val))});
|
||||
}
|
||||
wlist.push_back(LitCont::literal(std::move(val)));
|
||||
}
|
||||
|
||||
void do_compile_literals(vm::Stack& stack, int count) {
|
||||
|
@ -2548,8 +2703,16 @@ void do_compile_literals(vm::Stack& stack, int count) {
|
|||
if (wl_ref.is_null()) {
|
||||
throw IntError{"list of words expected"};
|
||||
}
|
||||
for (int i = count - 1; i >= 0; i--) {
|
||||
compile_one_literal(wl_ref.write(), std::move(stack[i]));
|
||||
if (count >= 2) {
|
||||
std::vector<vm::StackEntry> literals;
|
||||
for (int i = count - 1; i >= 0; i--) {
|
||||
literals.push_back(std::move(stack[i]));
|
||||
}
|
||||
wl_ref.write().push_back(td::make_ref<MultiLitCont>(std::move(literals)));
|
||||
} else {
|
||||
for (int i = count - 1; i >= 0; i--) {
|
||||
compile_one_literal(wl_ref.write(), std::move(stack[i]));
|
||||
}
|
||||
}
|
||||
stack.pop_many(count + 1);
|
||||
stack.push({vm::from_object, wl_ref});
|
||||
|
@ -2654,13 +2817,13 @@ void init_words_common(Dictionary& d) {
|
|||
d.def_stack_word("or ", interpret_or);
|
||||
d.def_stack_word("xor ", interpret_xor);
|
||||
// integer constants
|
||||
d.def_stack_word("false ", std::bind(interpret_const, _1, 0));
|
||||
d.def_stack_word("true ", std::bind(interpret_const, _1, -1));
|
||||
d.def_stack_word("0 ", std::bind(interpret_const, _1, 0));
|
||||
d.def_stack_word("1 ", std::bind(interpret_const, _1, 1));
|
||||
d.def_stack_word("2 ", std::bind(interpret_const, _1, 2));
|
||||
d.def_stack_word("-1 ", std::bind(interpret_const, _1, -1));
|
||||
d.def_stack_word("bl ", std::bind(interpret_const, _1, 32));
|
||||
d.def_word("false ", IntLitCont::literal(0));
|
||||
d.def_word("true ", IntLitCont::literal(-1));
|
||||
d.def_word("0 ", IntLitCont::literal(0));
|
||||
d.def_word("1 ", IntLitCont::literal(1));
|
||||
d.def_word("2 ", IntLitCont::literal(2));
|
||||
d.def_word("-1 ", IntLitCont::literal(-1));
|
||||
d.def_word("bl ", IntLitCont::literal(32));
|
||||
// integer comparison
|
||||
d.def_stack_word("cmp ", std::bind(interpret_cmp, _1, "\xff\x00\x01"));
|
||||
d.def_stack_word("= ", std::bind(interpret_cmp, _1, "\x00\xff\x00"));
|
||||
|
@ -2835,16 +2998,16 @@ void init_words_common(Dictionary& d) {
|
|||
d.def_stack_word("pfxdict!+ ", std::bind(interpret_pfx_dict_add, _1, vm::Dictionary::SetMode::Add, false));
|
||||
d.def_stack_word("pfxdict! ", std::bind(interpret_pfx_dict_add, _1, vm::Dictionary::SetMode::Set, false));
|
||||
d.def_stack_word("pfxdict@ ", interpret_pfx_dict_get);
|
||||
d.def_ctx_word("dictmap ", std::bind(interpret_dict_map, _1, false, false));
|
||||
d.def_ctx_word("dictmapext ", std::bind(interpret_dict_map, _1, true, false));
|
||||
d.def_ctx_word("idictmapext ", std::bind(interpret_dict_map, _1, true, true));
|
||||
d.def_ctx_word("dictforeach ", std::bind(interpret_dict_foreach, _1, false, false));
|
||||
d.def_ctx_word("idictforeach ", std::bind(interpret_dict_foreach, _1, false, true));
|
||||
d.def_ctx_word("dictforeachrev ", std::bind(interpret_dict_foreach, _1, true, false));
|
||||
d.def_ctx_word("idictforeachrev ", std::bind(interpret_dict_foreach, _1, true, true));
|
||||
d.def_ctx_word("dictforeachfromx ", std::bind(interpret_dict_foreach_from, _1, -1));
|
||||
d.def_ctx_word("dictmerge ", interpret_dict_merge);
|
||||
d.def_ctx_word("dictdiff ", interpret_dict_diff);
|
||||
d.def_ctx_tail_word("dictmap ", std::bind(interpret_dict_map, _1, false, false));
|
||||
d.def_ctx_tail_word("dictmapext ", std::bind(interpret_dict_map, _1, true, false));
|
||||
d.def_ctx_tail_word("idictmapext ", std::bind(interpret_dict_map, _1, true, true));
|
||||
d.def_ctx_tail_word("dictforeach ", std::bind(interpret_dict_foreach, _1, false, false));
|
||||
d.def_ctx_tail_word("idictforeach ", std::bind(interpret_dict_foreach, _1, false, true));
|
||||
d.def_ctx_tail_word("dictforeachrev ", std::bind(interpret_dict_foreach, _1, true, false));
|
||||
d.def_ctx_tail_word("idictforeachrev ", std::bind(interpret_dict_foreach, _1, true, true));
|
||||
d.def_ctx_tail_word("dictforeachfromx ", std::bind(interpret_dict_foreach_from, _1, -1));
|
||||
d.def_ctx_tail_word("dictmerge ", interpret_dict_merge);
|
||||
d.def_ctx_tail_word("dictdiff ", interpret_dict_diff);
|
||||
// slice/bitstring constants
|
||||
d.def_active_word("x{", interpret_bitstring_hex_literal);
|
||||
d.def_active_word("b{", interpret_bitstring_binary_literal);
|
||||
|
@ -2880,8 +3043,8 @@ void init_words_common(Dictionary& d) {
|
|||
d.def_ctx_tail_word("if ", interpret_if);
|
||||
d.def_ctx_tail_word("ifnot ", interpret_ifnot);
|
||||
d.def_ctx_tail_word("cond ", interpret_cond);
|
||||
d.def_ctx_word("while ", interpret_while);
|
||||
d.def_ctx_word("until ", interpret_until);
|
||||
d.def_ctx_tail_word("while ", interpret_while);
|
||||
d.def_ctx_tail_word("until ", interpret_until);
|
||||
// compiler control
|
||||
d.def_active_word("[ ", interpret_internal_interpret_begin);
|
||||
d.def_active_word("] ", interpret_internal_interpret_end);
|
||||
|
@ -2890,9 +3053,9 @@ void init_words_common(Dictionary& d) {
|
|||
d.def_stack_word("({) ", interpret_wordlist_begin_aux);
|
||||
d.def_stack_word("(}) ", interpret_wordlist_end_aux);
|
||||
d.def_stack_word("(compile) ", interpret_compile_internal);
|
||||
d.def_ctx_word("(execute) ", interpret_execute_internal);
|
||||
d.def_ctx_tail_word("(execute) ", interpret_execute_internal);
|
||||
d.def_active_word("' ", interpret_tick);
|
||||
d.def_stack_word("'nop ", interpret_tick_nop);
|
||||
d.def_word("'nop ", LitCont::literal({vm::from_object, Dictionary::nop_word_def}));
|
||||
// dictionary manipulation
|
||||
d.def_ctx_word("find ", interpret_find);
|
||||
d.def_ctx_word("create ", interpret_create);
|
||||
|
@ -2904,20 +3067,23 @@ void init_words_common(Dictionary& d) {
|
|||
d.def_ctx_word("(forget) ", interpret_forget_aux);
|
||||
d.def_ctx_word("forget ", interpret_forget);
|
||||
d.def_ctx_word("words ", interpret_words);
|
||||
d.def_ctx_word(".bt ", interpret_print_backtrace);
|
||||
d.def_ctx_word("cont. ", interpret_print_continuation);
|
||||
// input parse
|
||||
d.def_ctx_word("word ", interpret_word);
|
||||
d.def_ctx_word("(word) ", interpret_word_ext);
|
||||
d.def_ctx_word("skipspc ", interpret_skipspc);
|
||||
d.def_ctx_word("include ", interpret_include);
|
||||
d.def_stack_word("skip-to-eof ", interpret_skip_source);
|
||||
d.def_ctx_tail_word("include ", interpret_include);
|
||||
d.def_ctx_tail_word("skip-to-eof ", interpret_skip_source);
|
||||
d.def_word("'exit-interpret ", LitCont::literal(exit_interpret));
|
||||
d.def_ctx_word("abort ", interpret_abort);
|
||||
d.def_ctx_word("quit ", interpret_quit);
|
||||
d.def_ctx_word("bye ", interpret_bye);
|
||||
d.def_stack_word("halt ", interpret_halt);
|
||||
d.def_ctx_word("halt ", interpret_halt);
|
||||
// cmdline args
|
||||
d.def_stack_word("$* ", std::bind(interpret_literal, _1, vm::StackEntry{cmdline_args}));
|
||||
d.def_word("$* ", LitCont::literal(cmdline_args));
|
||||
d.def_stack_word("$# ", interpret_get_cmdline_arg_count);
|
||||
d.def_ctx_word("$() ", interpret_get_cmdline_arg);
|
||||
d.def_ctx_tail_word("$() ", interpret_get_cmdline_arg);
|
||||
}
|
||||
|
||||
void init_words_ton(Dictionary& d) {
|
||||
|
@ -2934,7 +3100,7 @@ void init_words_vm(Dictionary& d, bool enable_debug) {
|
|||
using namespace std::placeholders;
|
||||
vm::init_op_cp0(enable_debug);
|
||||
// vm run
|
||||
d.def_stack_word("vmlibs ", std::bind(interpret_literal, _1, vm::StackEntry{vm_libraries}));
|
||||
d.def_word("vmlibs ", LitCont::literal(vm_libraries));
|
||||
// d.def_ctx_word("runvmcode ", std::bind(interpret_run_vm, _1, 0x40));
|
||||
// d.def_ctx_word("runvm ", std::bind(interpret_run_vm, _1, 0x45));
|
||||
d.def_ctx_word("runvmx ", std::bind(interpret_run_vm, _1, -1));
|
||||
|
@ -2947,7 +3113,7 @@ void init_words_vm(Dictionary& d, bool enable_debug) {
|
|||
void import_cmdline_args(Dictionary& d, std::string arg0, int n, const char* const argv[]) {
|
||||
using namespace std::placeholders;
|
||||
LOG(DEBUG) << "import_cmdlist_args(" << arg0 << "," << n << ")";
|
||||
d.def_stack_word("$0 ", std::bind(interpret_literal, _1, vm::StackEntry{arg0}));
|
||||
d.def_word("$0 ", LitCont::literal(arg0));
|
||||
vm::StackEntry list;
|
||||
for (int i = n - 1; i >= 0; i--) {
|
||||
list = vm::StackEntry::cons(vm::StackEntry{argv[i]}, list);
|
||||
|
@ -2978,104 +3144,81 @@ td::RefInt256 numeric_value(std::string s) {
|
|||
return num;
|
||||
}
|
||||
|
||||
int funny_interpret_loop(IntCtx& ctx) {
|
||||
while (ctx.load_next_line()) {
|
||||
if (ctx.is_sb()) {
|
||||
continue;
|
||||
}
|
||||
std::ostringstream errs;
|
||||
bool ok = true;
|
||||
while (ok) {
|
||||
Ref<FiftCont> interpret_compile_execute(IntCtx& ctx) {
|
||||
if (ctx.state > 0) {
|
||||
interpret_compile_internal(ctx.stack);
|
||||
return {};
|
||||
} else {
|
||||
return interpret_execute_internal(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
Ref<FiftCont> InterpretCont::run_tail(IntCtx& ctx) const {
|
||||
if (!ctx.get_input() && !ctx.load_next_line()) {
|
||||
return {};
|
||||
}
|
||||
while (true) {
|
||||
if (!ctx.is_sb()) {
|
||||
ctx.skipspc();
|
||||
const char* ptr = ctx.get_input();
|
||||
if (!*ptr) {
|
||||
if (*ctx.get_input()) {
|
||||
break;
|
||||
}
|
||||
std::string Word;
|
||||
Word.reserve(128);
|
||||
auto entry = ctx.dictionary->lookup("");
|
||||
std::string entry_word;
|
||||
const char* ptr_end = ptr;
|
||||
while (*ptr && *ptr != ' ' && *ptr != '\t') {
|
||||
Word += *ptr++;
|
||||
auto cur = ctx.dictionary->lookup(Word);
|
||||
if (cur) {
|
||||
entry = cur;
|
||||
entry_word = Word;
|
||||
ptr_end = ptr;
|
||||
}
|
||||
}
|
||||
auto cur = ctx.dictionary->lookup(Word + " ");
|
||||
if (cur || !entry) {
|
||||
entry = std::move(cur);
|
||||
ctx.set_input(ptr);
|
||||
ctx.skipspc();
|
||||
} else {
|
||||
Word = entry_word;
|
||||
ctx.set_input(ptr_end);
|
||||
}
|
||||
try {
|
||||
if (entry) {
|
||||
if (entry->is_active()) {
|
||||
(*entry)(ctx);
|
||||
} else {
|
||||
ctx.stack.push_smallint(0);
|
||||
ctx.stack.push({vm::from_object, entry->get_def()});
|
||||
}
|
||||
} else {
|
||||
auto res = numeric_value_ext(Word);
|
||||
ctx.stack.push(std::move(res.first));
|
||||
if (res.second.not_null()) {
|
||||
ctx.stack.push(std::move(res.second));
|
||||
push_argcount(ctx, 2);
|
||||
} else {
|
||||
push_argcount(ctx, 1);
|
||||
}
|
||||
}
|
||||
if (ctx.state > 0) {
|
||||
interpret_compile_internal(ctx.stack);
|
||||
} else {
|
||||
interpret_execute_internal(ctx);
|
||||
}
|
||||
} catch (IntError& ab) {
|
||||
errs << ctx << Word << ": " << ab.msg;
|
||||
ok = false;
|
||||
} catch (vm::VmError& ab) {
|
||||
errs << ctx << Word << ": " << ab.get_msg();
|
||||
ok = false;
|
||||
} catch (vm::CellBuilder::CellWriteError) {
|
||||
errs << ctx << Word << ": Cell builder write error";
|
||||
ok = false;
|
||||
} catch (vm::VmFatal) {
|
||||
errs << ctx << Word << ": fatal vm error";
|
||||
ok = false;
|
||||
} catch (Quit& q) {
|
||||
if (ctx.include_depth) {
|
||||
throw;
|
||||
}
|
||||
if (!q.res) {
|
||||
ok = false;
|
||||
} else {
|
||||
return q.res;
|
||||
}
|
||||
} catch (SkipToEof) {
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
if (!ok) {
|
||||
auto err_msg = errs.str();
|
||||
if (!err_msg.empty()) {
|
||||
LOG(ERROR) << err_msg;
|
||||
}
|
||||
ctx.clear();
|
||||
if (ctx.include_depth) {
|
||||
throw IntError{"error interpreting included file `" + ctx.filename + "` : " + err_msg};
|
||||
}
|
||||
} else if (!ctx.state && !ctx.include_depth) {
|
||||
}
|
||||
if (!ctx.state && !ctx.include_depth) {
|
||||
*ctx.output_stream << " ok" << std::endl;
|
||||
}
|
||||
if (!ctx.load_next_line()) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
const char* ptr = ctx.get_input();
|
||||
std::string Word;
|
||||
Word.reserve(128);
|
||||
auto entry = ctx.dictionary->lookup("");
|
||||
std::string entry_word;
|
||||
const char* ptr_end = ptr;
|
||||
while (*ptr && *ptr != ' ' && *ptr != '\t') {
|
||||
Word += *ptr++;
|
||||
auto cur = ctx.dictionary->lookup(Word);
|
||||
if (cur) {
|
||||
entry = cur;
|
||||
entry_word = Word;
|
||||
ptr_end = ptr;
|
||||
}
|
||||
}
|
||||
auto cur = ctx.dictionary->lookup(Word + " ");
|
||||
if (cur || !entry) {
|
||||
entry = std::move(cur);
|
||||
ctx.set_input(ptr);
|
||||
ctx.skipspc();
|
||||
} else {
|
||||
Word = entry_word;
|
||||
ctx.set_input(ptr_end);
|
||||
}
|
||||
ctx.word = Word;
|
||||
static Ref<FiftCont> compile_exec_ref = td::make_ref<CtxTailWord>(interpret_compile_execute);
|
||||
if (!entry) {
|
||||
// numbers
|
||||
auto res = numeric_value_ext(Word);
|
||||
ctx.stack.push(std::move(res.first));
|
||||
if (res.second.not_null()) {
|
||||
ctx.stack.push(std::move(res.second));
|
||||
push_argcount(ctx, 2);
|
||||
} else {
|
||||
push_argcount(ctx, 1);
|
||||
}
|
||||
} else if (!entry->is_active()) {
|
||||
// ordinary word
|
||||
ctx.stack.push_smallint(0);
|
||||
ctx.stack.push({vm::from_object, entry->get_def()});
|
||||
} else {
|
||||
// active word
|
||||
ctx.next = SeqCont::seq(compile_exec_ref, SeqCont::seq(self(), std::move(ctx.next)));
|
||||
return entry->get_def();
|
||||
}
|
||||
exit_interpret->set({vm::from_object, ctx.next});
|
||||
ctx.next = SeqCont::seq(self(), std::move(ctx.next));
|
||||
return compile_exec_ref;
|
||||
}
|
||||
|
||||
} // namespace fift
|
||||
|
|
|
@ -21,23 +21,10 @@
|
|||
|
||||
namespace fift {
|
||||
|
||||
// thrown by 'quit', 'bye' and 'halt' for exiting to top level
|
||||
struct Quit {
|
||||
int res;
|
||||
Quit() : res(0) {
|
||||
}
|
||||
Quit(int _res) : res(_res) {
|
||||
}
|
||||
};
|
||||
|
||||
struct SkipToEof {};
|
||||
|
||||
void init_words_common(Dictionary& dictionary);
|
||||
void init_words_vm(Dictionary& dictionary, bool debug_enabled = false);
|
||||
void init_words_ton(Dictionary& dictionary);
|
||||
|
||||
void import_cmdline_args(Dictionary& d, std::string arg0, int n, const char* const argv[]);
|
||||
|
||||
int funny_interpret_loop(IntCtx& ctx);
|
||||
|
||||
} // namespace fift
|
||||
|
|
4
crypto/func/test/a12.fc
Normal file
4
crypto/func/test/a12.fc
Normal file
|
@ -0,0 +1,4 @@
|
|||
_ f(int a, int b, int c, int d, int e, int f, int g, int h, int i, int j, int k, int l, int m, int n, int o) {
|
||||
return (a, i, o, j, e, f, g, h, k, l, m, d, b, c, n);
|
||||
;; optimal code is 6-byte: s11 s12 XCHG2 2 5 BLKSWAP s13 s13 s11 XCHG3
|
||||
}
|
3
crypto/func/test/a12_1.fc
Normal file
3
crypto/func/test/a12_1.fc
Normal file
|
@ -0,0 +1,3 @@
|
|||
_ f(int a, int b, int c, int d, int e, int f, int g, int h, int i, int j) {
|
||||
return (f, j, a, b, i, g, d, e, h, c);
|
||||
}
|
3
crypto/func/test/a12_2.fc
Normal file
3
crypto/func/test/a12_2.fc
Normal file
|
@ -0,0 +1,3 @@
|
|||
_ f(int a, int b, int c, int d, int e, int f, int g, int h, int i, int j) {
|
||||
return (f, d, j, a, i, g, b, e, h, c);
|
||||
}
|
3
crypto/func/test/a12_3.fc
Normal file
3
crypto/func/test/a12_3.fc
Normal file
|
@ -0,0 +1,3 @@
|
|||
_ f(int a, int b, int c, int d, int e, int f, int g, int h, int i, int j, int k) {
|
||||
return (e, h, d, a, c, g, f, b, i, j, k); ;; optimal code is 4 ops, 8 bytes
|
||||
}
|
3
crypto/func/test/a12_4.fc
Normal file
3
crypto/func/test/a12_4.fc
Normal file
|
@ -0,0 +1,3 @@
|
|||
_ f(int a, int b, int c, int d, int e, int f, int g, int h, int i, int j, int k) {
|
||||
return (b, a, c, e, g, d, f, k, i, j, h); ;; optimal code is 4 ops, 8 bytes
|
||||
}
|
3
crypto/func/test/a12_5.fc
Normal file
3
crypto/func/test/a12_5.fc
Normal file
|
@ -0,0 +1,3 @@
|
|||
_ f(int a, int b, int c, int d, int e, int f, int g, int h, int i, int j, int k) {
|
||||
return (c, g, d, k, a, f, e, h, i, j, b); ;; optimal code is 6 ops, 6 bytes
|
||||
}
|
3
crypto/func/test/a12_6.fc
Normal file
3
crypto/func/test/a12_6.fc
Normal file
|
@ -0,0 +1,3 @@
|
|||
_ f(int a, int b, int c, int d, int e, int f, int g, int h, int i, int j, int k) {
|
||||
return (h, e, d, j, k, f, i, a, b, c, g); ;; optimal code is 3 ops, 6 bytes
|
||||
}
|
4
crypto/func/test/a12_7.fc
Normal file
4
crypto/func/test/a12_7.fc
Normal file
|
@ -0,0 +1,4 @@
|
|||
_ f(int a, int b, int c, int d, int e, int f, int g, int h, int i, int j, int k, int l, int m, int n, int o, int p) {
|
||||
return (m, f, l, j, b, e, a, d, p, k, c, n, g, i, h, o); ;; optimal code is 6 ops, 11 bytes
|
||||
;; s12 s0 s14 XCHG3 s0 s5 XCHG 6 8 BLKSWAP s3 s3 s9 XCHG3 s10 s14 s15 XCHG3 s13 s9 s9 XCHG3
|
||||
}
|
|
@ -46,7 +46,8 @@ bool unpack_grams(td::Ref<vm::CellSlice> cs, td::uint64& amount) {
|
|||
}
|
||||
} // namespace smc
|
||||
|
||||
td::Ref<vm::Cell> GenericAccount::get_init_state(td::Ref<vm::Cell> code, td::Ref<vm::Cell> data) noexcept {
|
||||
td::Ref<vm::Cell> GenericAccount::get_init_state(const td::Ref<vm::Cell>& code,
|
||||
const td::Ref<vm::Cell>& data) noexcept {
|
||||
return vm::CellBuilder()
|
||||
.store_zeroes(2)
|
||||
.store_ones(2)
|
||||
|
@ -136,4 +137,23 @@ td::Result<td::Ed25519::PublicKey> GenericAccount::get_public_key(const SmartCon
|
|||
};
|
||||
return TRY_VM(do_get_public_key());
|
||||
}
|
||||
|
||||
td::Result<td::uint32> GenericAccount::get_seqno(const SmartContract& sc) {
|
||||
return TRY_VM([&]() -> td::Result<td::uint32> {
|
||||
auto answer = sc.run_get_method("seqno");
|
||||
if (!answer.success) {
|
||||
return td::Status::Error("seqno get method failed");
|
||||
}
|
||||
return static_cast<td::uint32>(answer.stack.write().pop_long_range(std::numeric_limits<td::uint32>::max()));
|
||||
}());
|
||||
}
|
||||
td::Result<td::uint32> GenericAccount::get_wallet_id(const SmartContract& sc) {
|
||||
return TRY_VM([&]() -> td::Result<td::uint32> {
|
||||
auto answer = sc.run_get_method("wallet_id");
|
||||
if (!answer.success) {
|
||||
return td::Status::Error("seqno get method failed");
|
||||
}
|
||||
return static_cast<td::uint32>(answer.stack.write().pop_long_range(std::numeric_limits<td::uint32>::max()));
|
||||
}());
|
||||
}
|
||||
} // namespace ton
|
||||
|
|
|
@ -29,12 +29,17 @@ bool unpack_grams(td::Ref<vm::CellSlice> cs, td::uint64& amount);
|
|||
} // namespace smc
|
||||
class GenericAccount {
|
||||
public:
|
||||
static td::Ref<vm::Cell> get_init_state(td::Ref<vm::Cell> code, td::Ref<vm::Cell> data) noexcept;
|
||||
static td::Ref<vm::Cell> get_init_state(const td::Ref<vm::Cell>& code, const td::Ref<vm::Cell>& data) noexcept;
|
||||
static td::Ref<vm::Cell> get_init_state(const SmartContract::State& state) noexcept {
|
||||
return get_init_state(state.code, state.data);
|
||||
}
|
||||
static block::StdAddress get_address(ton::WorkchainId workchain_id, const td::Ref<vm::Cell>& init_state) noexcept;
|
||||
static td::Ref<vm::Cell> create_ext_message(const block::StdAddress& address, td::Ref<vm::Cell> new_state,
|
||||
td::Ref<vm::Cell> body) noexcept;
|
||||
static void store_int_message(vm::CellBuilder& cb, const block::StdAddress& dest_address, td::int64 gramms);
|
||||
|
||||
static td::Result<td::Ed25519::PublicKey> get_public_key(const SmartContract& sc);
|
||||
static td::Result<td::uint32> get_seqno(const SmartContract& sc);
|
||||
static td::Result<td::uint32> get_wallet_id(const SmartContract& sc);
|
||||
};
|
||||
} // namespace ton
|
||||
|
|
|
@ -27,48 +27,12 @@
|
|||
#include <limits>
|
||||
|
||||
namespace ton {
|
||||
td::optional<td::int32> HighloadWallet::guess_revision(const vm::Cell::Hash& code_hash) {
|
||||
for (td::int32 i = 1; i <= 2; i++) {
|
||||
if (get_init_code(i)->get_hash() == code_hash) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
td::optional<td::int32> HighloadWallet::guess_revision(const block::StdAddress& address,
|
||||
const td::Ed25519::PublicKey& public_key, td::uint32 wallet_id) {
|
||||
for (td::int32 i = 1; i <= 2; i++) {
|
||||
if (GenericAccount::get_address(address.workchain, get_init_state(public_key, wallet_id, i)) == address) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
td::Ref<vm::Cell> HighloadWallet::get_init_state(const td::Ed25519::PublicKey& public_key, td::uint32 wallet_id,
|
||||
td::int32 revision) noexcept {
|
||||
auto code = get_init_code(revision);
|
||||
auto data = get_init_data(public_key, wallet_id);
|
||||
return GenericAccount::get_init_state(std::move(code), std::move(data));
|
||||
}
|
||||
|
||||
td::Ref<vm::Cell> HighloadWallet::get_init_message(const td::Ed25519::PrivateKey& private_key,
|
||||
td::uint32 wallet_id) noexcept {
|
||||
td::uint32 seqno = 0;
|
||||
td::uint32 valid_until = std::numeric_limits<td::uint32>::max();
|
||||
auto append_message = [&](auto&& cb) -> vm::CellBuilder& {
|
||||
cb.store_long(wallet_id, 32).store_long(valid_until, 32).store_long(seqno, 32);
|
||||
CHECK(cb.store_maybe_ref({}));
|
||||
return cb;
|
||||
};
|
||||
auto signature = private_key.sign(append_message(vm::CellBuilder()).finalize()->get_hash().as_slice()).move_as_ok();
|
||||
|
||||
return append_message(vm::CellBuilder().store_bytes(signature)).finalize();
|
||||
}
|
||||
|
||||
td::Ref<vm::Cell> HighloadWallet::make_a_gift_message(const td::Ed25519::PrivateKey& private_key, td::uint32 wallet_id,
|
||||
td::uint32 seqno, td::uint32 valid_until,
|
||||
td::Span<Gift> gifts) noexcept {
|
||||
CHECK(gifts.size() <= max_gifts_size);
|
||||
td::Result<td::Ref<vm::Cell>> HighloadWallet::make_a_gift_message(const td::Ed25519::PrivateKey& private_key,
|
||||
td::uint32 valid_until, td::Span<Gift> gifts) const {
|
||||
TRY_RESULT(wallet_id, get_wallet_id());
|
||||
TRY_RESULT(seqno, get_seqno());
|
||||
CHECK(gifts.size() <= get_max_gifts_size());
|
||||
vm::Dictionary messages(16);
|
||||
for (size_t i = 0; i < gifts.size(); i++) {
|
||||
auto& gift = gifts[i];
|
||||
|
@ -91,63 +55,36 @@ td::Ref<vm::Cell> HighloadWallet::make_a_gift_message(const td::Ed25519::Private
|
|||
return vm::CellBuilder().store_bytes(signature).append_cellslice(vm::load_cell_slice(message_outer)).finalize();
|
||||
}
|
||||
|
||||
td::Ref<vm::Cell> HighloadWallet::get_init_code(td::int32 revision) noexcept {
|
||||
return SmartContractCode::get_code(SmartContractCode::HighloadWalletV1, revision);
|
||||
}
|
||||
|
||||
vm::CellHash HighloadWallet::get_init_code_hash() noexcept {
|
||||
return get_init_code(0)->get_hash();
|
||||
}
|
||||
|
||||
td::Ref<vm::Cell> HighloadWallet::get_init_data(const td::Ed25519::PublicKey& public_key,
|
||||
td::uint32 wallet_id) noexcept {
|
||||
td::Ref<vm::Cell> HighloadWallet::get_init_data(const InitData& init_data) noexcept {
|
||||
return vm::CellBuilder()
|
||||
.store_long(0, 32)
|
||||
.store_long(wallet_id, 32)
|
||||
.store_bytes(public_key.as_octet_string())
|
||||
.store_long(init_data.seqno, 32)
|
||||
.store_long(init_data.wallet_id, 32)
|
||||
.store_bytes(init_data.public_key)
|
||||
.finalize();
|
||||
}
|
||||
|
||||
td::Result<td::uint32> HighloadWallet::get_seqno() const {
|
||||
return TRY_VM(get_seqno_or_throw());
|
||||
}
|
||||
|
||||
td::Result<td::uint32> HighloadWallet::get_seqno_or_throw() const {
|
||||
if (state_.data.is_null()) {
|
||||
return 0;
|
||||
}
|
||||
//FIXME use get method
|
||||
return static_cast<td::uint32>(vm::load_cell_slice(state_.data).fetch_ulong(32));
|
||||
}
|
||||
|
||||
td::Result<td::uint32> HighloadWallet::get_wallet_id() const {
|
||||
return TRY_VM(get_wallet_id_or_throw());
|
||||
}
|
||||
|
||||
td::Result<td::uint32> HighloadWallet::get_wallet_id_or_throw() const {
|
||||
if (state_.data.is_null()) {
|
||||
return 0;
|
||||
}
|
||||
//FIXME use get method
|
||||
auto cs = vm::load_cell_slice(state_.data);
|
||||
cs.skip_first(32);
|
||||
return static_cast<td::uint32>(cs.fetch_ulong(32));
|
||||
return TRY_VM([&]() -> td::Result<td::uint32> {
|
||||
if (state_.data.is_null()) {
|
||||
return 0;
|
||||
}
|
||||
auto cs = vm::load_cell_slice(state_.data);
|
||||
cs.skip_first(32);
|
||||
return static_cast<td::uint32>(cs.fetch_ulong(32));
|
||||
}());
|
||||
}
|
||||
|
||||
td::Result<td::Ed25519::PublicKey> HighloadWallet::get_public_key() const {
|
||||
return TRY_VM(get_public_key_or_throw());
|
||||
}
|
||||
|
||||
td::Result<td::Ed25519::PublicKey> HighloadWallet::get_public_key_or_throw() const {
|
||||
if (state_.data.is_null()) {
|
||||
return td::Status::Error("data is null");
|
||||
}
|
||||
//FIXME use get method
|
||||
auto cs = vm::load_cell_slice(state_.data);
|
||||
cs.skip_first(64);
|
||||
td::SecureString res(td::Ed25519::PublicKey::LENGTH);
|
||||
cs.fetch_bytes(res.as_mutable_slice().ubegin(), td::narrow_cast<td::int32>(res.size()));
|
||||
return td::Ed25519::PublicKey(std::move(res));
|
||||
return TRY_VM([&]() -> td::Result<td::Ed25519::PublicKey> {
|
||||
if (state_.data.is_null()) {
|
||||
return td::Status::Error("data is null");
|
||||
}
|
||||
auto cs = vm::load_cell_slice(state_.data);
|
||||
cs.skip_first(64);
|
||||
td::SecureString res(td::Ed25519::PublicKey::LENGTH);
|
||||
cs.fetch_bytes(res.as_mutable_slice().ubegin(), td::narrow_cast<td::int32>(res.size()));
|
||||
return td::Ed25519::PublicKey(std::move(res));
|
||||
}());
|
||||
}
|
||||
|
||||
} // namespace ton
|
||||
|
|
|
@ -26,41 +26,24 @@
|
|||
#include "vm/cells/CellString.h"
|
||||
|
||||
namespace ton {
|
||||
class HighloadWallet : public ton::SmartContract, public WalletInterface {
|
||||
public:
|
||||
explicit HighloadWallet(State state) : ton::SmartContract(std::move(state)) {
|
||||
}
|
||||
struct HighloadWalletTraits {
|
||||
using InitData = WalletInterface::DefaultInitData;
|
||||
|
||||
static constexpr unsigned max_message_size = vm::CellString::max_bytes;
|
||||
static constexpr unsigned max_gifts_size = 254;
|
||||
static td::Ref<vm::Cell> get_init_state(const td::Ed25519::PublicKey& public_key, td::uint32 wallet_id,
|
||||
td::int32 revision) noexcept;
|
||||
static td::Ref<vm::Cell> get_init_message(const td::Ed25519::PrivateKey& private_key, td::uint32 wallet_id) noexcept;
|
||||
static td::Ref<vm::Cell> make_a_gift_message(const td::Ed25519::PrivateKey& private_key, td::uint32 wallet_id,
|
||||
td::uint32 seqno, td::uint32 valid_until, td::Span<Gift> gifts) noexcept;
|
||||
|
||||
static td::Ref<vm::Cell> get_init_code(td::int32 revision) noexcept;
|
||||
static vm::CellHash get_init_code_hash() noexcept;
|
||||
static td::Ref<vm::Cell> get_init_data(const td::Ed25519::PublicKey& public_key, td::uint32 wallet_id) noexcept;
|
||||
static td::optional<td::int32> guess_revision(const vm::Cell::Hash& code_hash);
|
||||
static td::optional<td::int32> guess_revision(const block::StdAddress& address,
|
||||
const td::Ed25519::PublicKey& public_key, td::uint32 wallet_id);
|
||||
td::Result<td::uint32> get_seqno() const;
|
||||
td::Result<td::uint32> get_wallet_id() const;
|
||||
static constexpr auto code_type = SmartContractCode::HighloadWalletV1;
|
||||
};
|
||||
|
||||
class HighloadWallet : public WalletBase<HighloadWallet, HighloadWalletTraits> {
|
||||
public:
|
||||
explicit HighloadWallet(State state) : WalletBase(std::move(state)) {
|
||||
}
|
||||
td::Result<td::Ref<vm::Cell>> make_a_gift_message(const td::Ed25519::PrivateKey& private_key, td::uint32 valid_until,
|
||||
td::Span<Gift> gifts) const override {
|
||||
TRY_RESULT(seqno, get_seqno());
|
||||
TRY_RESULT(wallet_id, get_wallet_id());
|
||||
return make_a_gift_message(private_key, wallet_id, seqno, valid_until, gifts);
|
||||
}
|
||||
size_t get_max_gifts_size() const override {
|
||||
return max_gifts_size;
|
||||
}
|
||||
td::Result<td::Ed25519::PublicKey> get_public_key() const override;
|
||||
td::Span<Gift> gifts) const override;
|
||||
static td::Ref<vm::Cell> get_init_data(const InitData& init_data) noexcept;
|
||||
|
||||
private:
|
||||
td::Result<td::uint32> get_seqno_or_throw() const;
|
||||
td::Result<td::uint32> get_wallet_id_or_throw() const;
|
||||
td::Result<td::Ed25519::PublicKey> get_public_key_or_throw() const;
|
||||
// can't use get methods for compatibility with old revisions
|
||||
td::Result<td::uint32> get_wallet_id() const override;
|
||||
td::Result<td::Ed25519::PublicKey> get_public_key() const override;
|
||||
};
|
||||
} // namespace ton
|
||||
|
|
|
@ -27,33 +27,10 @@
|
|||
#include <limits>
|
||||
|
||||
namespace ton {
|
||||
td::optional<td::int32> HighloadWalletV2::guess_revision(const vm::Cell::Hash& code_hash) {
|
||||
for (td::int32 i = 1; i <= 2; i++) {
|
||||
if (get_init_code(i)->get_hash() == code_hash) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
td::optional<td::int32> HighloadWalletV2::guess_revision(const block::StdAddress& address,
|
||||
const td::Ed25519::PublicKey& public_key,
|
||||
td::uint32 wallet_id) {
|
||||
for (td::int32 i = 1; i <= 2; i++) {
|
||||
if (GenericAccount::get_address(address.workchain, get_init_state(public_key, wallet_id, i)) == address) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
td::Ref<vm::Cell> HighloadWalletV2::get_init_state(const td::Ed25519::PublicKey& public_key, td::uint32 wallet_id,
|
||||
td::int32 revision) noexcept {
|
||||
auto code = get_init_code(revision);
|
||||
auto data = get_init_data(public_key, wallet_id);
|
||||
return GenericAccount::get_init_state(std::move(code), std::move(data));
|
||||
}
|
||||
|
||||
td::Ref<vm::Cell> HighloadWalletV2::get_init_message(const td::Ed25519::PrivateKey& private_key, td::uint32 wallet_id,
|
||||
td::uint32 valid_until) noexcept {
|
||||
td::Result<td::Ref<vm::Cell>> HighloadWalletV2::get_init_message(const td::Ed25519::PrivateKey& private_key,
|
||||
td::uint32 valid_until) const noexcept {
|
||||
TRY_RESULT(wallet_id, get_wallet_id());
|
||||
td::uint32 id = -1;
|
||||
auto append_message = [&](auto&& cb) -> vm::CellBuilder& {
|
||||
cb.store_long(wallet_id, 32).store_long(valid_until, 32).store_long(id, 32);
|
||||
|
@ -65,10 +42,11 @@ td::Ref<vm::Cell> HighloadWalletV2::get_init_message(const td::Ed25519::PrivateK
|
|||
return append_message(vm::CellBuilder().store_bytes(signature)).finalize();
|
||||
}
|
||||
|
||||
td::Ref<vm::Cell> HighloadWalletV2::make_a_gift_message(const td::Ed25519::PrivateKey& private_key,
|
||||
td::uint32 wallet_id, td::uint32 valid_until,
|
||||
td::Span<Gift> gifts) noexcept {
|
||||
CHECK(gifts.size() <= max_gifts_size);
|
||||
td::Result<td::Ref<vm::Cell>> HighloadWalletV2::make_a_gift_message(const td::Ed25519::PrivateKey& private_key,
|
||||
td::uint32 valid_until,
|
||||
td::Span<Gift> gifts) const {
|
||||
TRY_RESULT(wallet_id, get_wallet_id());
|
||||
CHECK(gifts.size() <= get_max_gifts_size());
|
||||
vm::Dictionary messages(16);
|
||||
for (size_t i = 0; i < gifts.size(); i++) {
|
||||
auto& gift = gifts[i];
|
||||
|
@ -96,49 +74,34 @@ td::Ref<vm::Cell> HighloadWalletV2::make_a_gift_message(const td::Ed25519::Priva
|
|||
return vm::CellBuilder().store_bytes(signature).append_cellslice(vm::load_cell_slice(message_outer)).finalize();
|
||||
}
|
||||
|
||||
td::Ref<vm::Cell> HighloadWalletV2::get_init_code(td::int32 revision) noexcept {
|
||||
return SmartContractCode::get_code(SmartContractCode::HighloadWalletV2, revision);
|
||||
}
|
||||
|
||||
vm::CellHash HighloadWalletV2::get_init_code_hash() noexcept {
|
||||
return get_init_code(0)->get_hash();
|
||||
}
|
||||
|
||||
td::Ref<vm::Cell> HighloadWalletV2::get_init_data(const td::Ed25519::PublicKey& public_key,
|
||||
td::uint32 wallet_id) noexcept {
|
||||
td::Ref<vm::Cell> HighloadWalletV2::get_init_data(const InitData& init_data) noexcept {
|
||||
vm::CellBuilder cb;
|
||||
cb.store_long(wallet_id, 32).store_long(0, 64).store_bytes(public_key.as_octet_string());
|
||||
cb.store_long(init_data.wallet_id, 32).store_long(init_data.seqno, 64).store_bytes(init_data.public_key);
|
||||
CHECK(cb.store_maybe_ref({}));
|
||||
return cb.finalize();
|
||||
}
|
||||
|
||||
td::Result<td::uint32> HighloadWalletV2::get_wallet_id() const {
|
||||
return TRY_VM(get_wallet_id_or_throw());
|
||||
}
|
||||
|
||||
td::Result<td::uint32> HighloadWalletV2::get_wallet_id_or_throw() const {
|
||||
if (state_.data.is_null()) {
|
||||
return 0;
|
||||
}
|
||||
//FIXME use get method
|
||||
auto cs = vm::load_cell_slice(state_.data);
|
||||
return static_cast<td::uint32>(cs.fetch_ulong(32));
|
||||
return TRY_VM([&]() -> td::Result<td::uint32> {
|
||||
if (state_.data.is_null()) {
|
||||
return 0;
|
||||
}
|
||||
auto cs = vm::load_cell_slice(state_.data);
|
||||
return static_cast<td::uint32>(cs.fetch_ulong(32));
|
||||
}());
|
||||
}
|
||||
|
||||
td::Result<td::Ed25519::PublicKey> HighloadWalletV2::get_public_key() const {
|
||||
return TRY_VM(get_public_key_or_throw());
|
||||
}
|
||||
|
||||
td::Result<td::Ed25519::PublicKey> HighloadWalletV2::get_public_key_or_throw() const {
|
||||
if (state_.data.is_null()) {
|
||||
return td::Status::Error("data is null");
|
||||
}
|
||||
//FIXME use get method
|
||||
auto cs = vm::load_cell_slice(state_.data);
|
||||
cs.skip_first(96);
|
||||
td::SecureString res(td::Ed25519::PublicKey::LENGTH);
|
||||
cs.fetch_bytes(res.as_mutable_slice().ubegin(), td::narrow_cast<td::int32>(res.size()));
|
||||
return td::Ed25519::PublicKey(std::move(res));
|
||||
return TRY_VM([&]() -> td::Result<td::Ed25519::PublicKey> {
|
||||
if (state_.data.is_null()) {
|
||||
return td::Status::Error("data is null");
|
||||
}
|
||||
auto cs = vm::load_cell_slice(state_.data);
|
||||
cs.skip_first(96);
|
||||
td::SecureString res(td::Ed25519::PublicKey::LENGTH);
|
||||
cs.fetch_bytes(res.as_mutable_slice().ubegin(), td::narrow_cast<td::int32>(res.size()));
|
||||
return td::Ed25519::PublicKey(std::move(res));
|
||||
}());
|
||||
}
|
||||
|
||||
} // namespace ton
|
||||
|
|
|
@ -26,41 +26,26 @@
|
|||
#include "vm/cells/CellString.h"
|
||||
|
||||
namespace ton {
|
||||
class HighloadWalletV2 : public ton::SmartContract, public WalletInterface {
|
||||
public:
|
||||
explicit HighloadWalletV2(State state) : ton::SmartContract(std::move(state)) {
|
||||
}
|
||||
struct HighloadWalletV2Traits {
|
||||
using InitData = WalletInterface::DefaultInitData;
|
||||
|
||||
static constexpr unsigned max_message_size = vm::CellString::max_bytes;
|
||||
static constexpr unsigned max_gifts_size = 254;
|
||||
static td::Ref<vm::Cell> get_init_state(const td::Ed25519::PublicKey& public_key, td::uint32 wallet_id,
|
||||
td::int32 revision) noexcept;
|
||||
static td::Ref<vm::Cell> get_init_message(const td::Ed25519::PrivateKey& private_key, td::uint32 wallet_id,
|
||||
td::uint32 valid_until) noexcept;
|
||||
|
||||
static td::Ref<vm::Cell> make_a_gift_message(const td::Ed25519::PrivateKey& private_key, td::uint32 wallet_id,
|
||||
td::uint32 valid_until, td::Span<Gift> gifts) noexcept;
|
||||
|
||||
static td::Ref<vm::Cell> get_init_code(td::int32 revision) noexcept;
|
||||
static vm::CellHash get_init_code_hash() noexcept;
|
||||
static td::Ref<vm::Cell> get_init_data(const td::Ed25519::PublicKey& public_key, td::uint32 wallet_id) noexcept;
|
||||
static td::optional<td::int32> guess_revision(const vm::Cell::Hash& code_hash);
|
||||
static td::optional<td::int32> guess_revision(const block::StdAddress& address,
|
||||
const td::Ed25519::PublicKey& public_key, td::uint32 wallet_id);
|
||||
|
||||
td::Result<td::uint32> get_wallet_id() const;
|
||||
|
||||
static constexpr auto code_type = SmartContractCode::HighloadWalletV2;
|
||||
};
|
||||
class HighloadWalletV2 : public WalletBase<HighloadWalletV2, HighloadWalletV2Traits> {
|
||||
public:
|
||||
explicit HighloadWalletV2(State state) : WalletBase(std::move(state)) {
|
||||
}
|
||||
td::Result<td::Ref<vm::Cell>> make_a_gift_message(const td::Ed25519::PrivateKey& private_key, td::uint32 valid_until,
|
||||
td::Span<Gift> gifts) const override {
|
||||
TRY_RESULT(wallet_id, get_wallet_id());
|
||||
return make_a_gift_message(private_key, wallet_id, valid_until, gifts);
|
||||
}
|
||||
size_t get_max_gifts_size() const override {
|
||||
return max_gifts_size;
|
||||
}
|
||||
td::Result<td::Ed25519::PublicKey> get_public_key() const override;
|
||||
td::Span<Gift> gifts) const override;
|
||||
static td::Ref<vm::Cell> get_init_data(const InitData& init_data) noexcept;
|
||||
td::Result<td::Ref<vm::Cell>> get_init_message(const td::Ed25519::PrivateKey& private_key,
|
||||
td::uint32 valid_until = std::numeric_limits<td::uint32>::max()) const
|
||||
noexcept;
|
||||
|
||||
private:
|
||||
td::Result<td::uint32> get_wallet_id_or_throw() const;
|
||||
td::Result<td::Ed25519::PublicKey> get_public_key_or_throw() const;
|
||||
// can't use get methods for compatibility with old revisions
|
||||
td::Result<td::uint32> get_wallet_id() const override;
|
||||
td::Result<td::Ed25519::PublicKey> get_public_key() const override;
|
||||
};
|
||||
} // namespace ton
|
||||
|
|
|
@ -30,6 +30,14 @@
|
|||
#include "td/utils/crypto.h"
|
||||
|
||||
namespace ton {
|
||||
int SmartContract::Answer::output_actions_count(td::Ref<vm::Cell> list) {
|
||||
int i = -1;
|
||||
do {
|
||||
++i;
|
||||
list = load_cell_slice(std::move(list)).prefetch_ref();
|
||||
} while (list.not_null());
|
||||
return i;
|
||||
}
|
||||
namespace {
|
||||
|
||||
td::Ref<vm::Stack> prepare_vm_stack(td::RefInt256 amount, td::Ref<vm::CellSlice> body) {
|
||||
|
@ -66,15 +74,6 @@ td::Ref<vm::Tuple> prepare_vm_c7(td::uint32 now, td::uint64 balance) {
|
|||
return vm::make_tuple_ref(std::move(tuple));
|
||||
}
|
||||
|
||||
static int output_actions_count(td::Ref<vm::Cell> list) {
|
||||
int i = -1;
|
||||
do {
|
||||
++i;
|
||||
list = load_cell_slice(std::move(list)).prefetch_ref();
|
||||
} while (list.not_null());
|
||||
return i;
|
||||
}
|
||||
|
||||
SmartContract::Answer run_smartcont(SmartContract::State state, td::Ref<vm::Stack> stack, td::Ref<vm::Tuple> c7,
|
||||
vm::GasLimits gas, bool ignore_chksig) {
|
||||
auto gas_credit = gas.gas_credit;
|
||||
|
@ -133,7 +132,7 @@ SmartContract::Answer run_smartcont(SmartContract::State state, td::Ref<vm::Stac
|
|||
res.new_state.data = vm.get_c4();
|
||||
res.actions = vm.get_d(5);
|
||||
LOG(DEBUG) << "output actions:\n"
|
||||
<< block::gen::OutList{output_actions_count(res.actions)}.as_string_ref(res.actions);
|
||||
<< block::gen::OutList{res.output_actions_count(res.actions)}.as_string_ref(res.actions);
|
||||
}
|
||||
LOG_IF(ERROR, gas_credit != 0 && (res.accepted && !res.success))
|
||||
<< "Accepted but failed with code " << res.code << "\n"
|
||||
|
|
|
@ -48,6 +48,7 @@ class SmartContract : public td::CntObject {
|
|||
td::Ref<vm::Cell> actions;
|
||||
td::int32 code;
|
||||
td::int64 gas_used;
|
||||
static int output_actions_count(td::Ref<vm::Cell> list);
|
||||
};
|
||||
|
||||
struct Args {
|
||||
|
|
|
@ -38,8 +38,6 @@ const auto& get_map() {
|
|||
map[name] = vm::std_boc_deserialize(td::base64_decode(code_str).move_as_ok()).move_as_ok();
|
||||
};
|
||||
#include "smartcont/auto/multisig-code.cpp"
|
||||
#include "smartcont/auto/simple-wallet-ext-code.cpp"
|
||||
#include "smartcont/auto/simple-wallet-code.cpp"
|
||||
#include "smartcont/auto/wallet-code.cpp"
|
||||
#include "smartcont/auto/highload-wallet-code.cpp"
|
||||
#include "smartcont/auto/highload-wallet-v2-code.cpp"
|
||||
|
@ -65,18 +63,6 @@ const auto& get_map() {
|
|||
"QE0VNggED0Dm+hMfJgUXO68qIH+QFUEIf5EPKjAvQE0fgAf44WIYAQ9HhvpSCYAtMH1DAB+wCRMuIBs+"
|
||||
"ZbgyWhyEA0gED0Q4rmMcgSyx8Tyz/L//QAye1UCAAE0DACASAGBwAXvZznaiaGmvmOuF/8AEG+X5dqJoaY+Y6Z/p/"
|
||||
"5j6AmipEEAgegc30JjJLb/JXdHxQANCCAQPSWb6UyURCUMFMDud4gkzM2AZIyMOKz");
|
||||
with_tvm_code("simple-wallet-r1",
|
||||
"te6ccgEEAQEAAAAAUwAAov8AIN0gggFMl7qXMO1E0NcLH+Ck8mCBAgDXGCDXCx/tRNDTH9P/"
|
||||
"0VESuvKhIvkBVBBE+RDyovgAAdMfMSDXSpbTB9QC+wDe0aTIyx/L/8ntVA==");
|
||||
with_tvm_code("simple-wallet-r2",
|
||||
"te6ccgEBAQEAXwAAuv8AIN0gggFMl7ohggEznLqxnHGw7UTQ0x/XC//jBOCk8mCBAgDXGCDXCx/tRNDTH9P/"
|
||||
"0VESuvKhIvkBVBBE+RDyovgAAdMfMSDXSpbTB9QC+wDe0aTIyx/L/8ntVA==");
|
||||
with_tvm_code("wallet-r1",
|
||||
"te6ccgEBAQEAVwAAqv8AIN0gggFMl7qXMO1E0NcLH+Ck8mCDCNcYINMf0x8B+CO78mPtRNDTH9P/0VExuvKhA/"
|
||||
"kBVBBC+RDyovgAApMg10qW0wfUAvsA6NGkyMsfy//J7VQ=");
|
||||
with_tvm_code("wallet-r2",
|
||||
"te6ccgEBAQEAYwAAwv8AIN0gggFMl7ohggEznLqxnHGw7UTQ0x/XC//jBOCk8mCDCNcYINMf0x8B+CO78mPtRNDTH9P/"
|
||||
"0VExuvKhA/kBVBBC+RDyovgAApMg10qW0wfUAvsA6NGkyMsfy//J7VQ=");
|
||||
with_tvm_code("wallet3-r1",
|
||||
"te6ccgEBAQEAYgAAwP8AIN0gggFMl7qXMO1E0NcLH+Ck8mCDCNcYINMf0x/TH/gjE7vyY+1E0NMf0x/T/"
|
||||
"9FRMrryoVFEuvKiBPkBVBBV+RDyo/gAkyDXSpbTB9QC+wDo0QGkyMsfyx/L/8ntVA==");
|
||||
|
@ -123,22 +109,10 @@ td::Result<td::Ref<vm::Cell>> SmartContractCode::load(td::Slice name) {
|
|||
|
||||
td::Span<int> SmartContractCode::get_revisions(Type type) {
|
||||
switch (type) {
|
||||
case Type::WalletV1: {
|
||||
static int res[] = {1, 2};
|
||||
return res;
|
||||
}
|
||||
case Type::WalletV2: {
|
||||
static int res[] = {1, 2};
|
||||
return res;
|
||||
}
|
||||
case Type::WalletV3: {
|
||||
static int res[] = {1, 2};
|
||||
return res;
|
||||
}
|
||||
case Type::WalletV1Ext: {
|
||||
static int res[] = {-1};
|
||||
return res;
|
||||
}
|
||||
case Type::HighloadWalletV1: {
|
||||
static int res[] = {-1, 1, 2};
|
||||
return res;
|
||||
|
@ -191,14 +165,8 @@ td::Ref<vm::Cell> SmartContractCode::get_code(Type type, int ext_revision) {
|
|||
auto revision = validate_revision(type, ext_revision).move_as_ok();
|
||||
auto basename = [](Type type) -> td::Slice {
|
||||
switch (type) {
|
||||
case Type::WalletV1:
|
||||
return "simple-wallet";
|
||||
case Type::WalletV2:
|
||||
return "wallet";
|
||||
case Type::WalletV3:
|
||||
return "wallet3";
|
||||
case Type::WalletV1Ext:
|
||||
return "simple-wallet-ext";
|
||||
case Type::HighloadWalletV1:
|
||||
return "highload-wallet";
|
||||
case Type::HighloadWalletV2:
|
||||
|
|
|
@ -26,18 +26,7 @@ class SmartContractCode {
|
|||
public:
|
||||
static td::Result<td::Ref<vm::Cell>> load(td::Slice name);
|
||||
|
||||
enum Type {
|
||||
WalletV1 = 1,
|
||||
WalletV1Ext,
|
||||
WalletV2,
|
||||
WalletV3,
|
||||
HighloadWalletV1,
|
||||
HighloadWalletV2,
|
||||
ManualDns,
|
||||
Multisig,
|
||||
PaymentChannel,
|
||||
RestrictedWallet
|
||||
};
|
||||
enum Type { WalletV3 = 4, HighloadWalletV1, HighloadWalletV2, ManualDns, Multisig, PaymentChannel, RestrictedWallet };
|
||||
static td::Span<int> get_revisions(Type type);
|
||||
static td::Result<int> validate_revision(Type type, int revision);
|
||||
static td::Ref<vm::Cell> get_code(Type type, int revision = 0);
|
||||
|
|
77
crypto/smc-envelope/WalletInterface.cpp
Normal file
77
crypto/smc-envelope/WalletInterface.cpp
Normal file
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
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-2020 Telegram Systems LLP
|
||||
*/
|
||||
#include "WalletInterface.h"
|
||||
|
||||
namespace ton {
|
||||
td::Result<td::uint64> WalletInterface::get_balance(td::uint64 account_balance, td::uint32 now) const {
|
||||
return TRY_VM([&]() -> td::Result<td::uint64> {
|
||||
Answer answer = this->run_get_method(Args().set_method_id("balance").set_balance(account_balance).set_now(now));
|
||||
if (!answer.success) {
|
||||
return td::Status::Error("balance get method failed");
|
||||
}
|
||||
return static_cast<td::uint64>(answer.stack.write().pop_long());
|
||||
}());
|
||||
}
|
||||
|
||||
td::Result<td::Ed25519::PublicKey> WalletInterface::get_public_key() const {
|
||||
return GenericAccount::get_public_key(*this);
|
||||
};
|
||||
|
||||
td::Result<td::uint32> WalletInterface::get_seqno() const {
|
||||
return GenericAccount::get_seqno(*this);
|
||||
}
|
||||
|
||||
td::Result<td::uint32> WalletInterface::get_wallet_id() const {
|
||||
return GenericAccount::get_wallet_id(*this);
|
||||
}
|
||||
|
||||
td::Result<td::Ref<vm::Cell>> WalletInterface::get_init_message(const td::Ed25519::PrivateKey &private_key,
|
||||
td::uint32 valid_until) const {
|
||||
return make_a_gift_message(private_key, valid_until, {});
|
||||
}
|
||||
|
||||
td::Ref<vm::Cell> WalletInterface::create_int_message(const Gift &gift) {
|
||||
vm::CellBuilder cbi;
|
||||
GenericAccount::store_int_message(cbi, gift.destination, gift.gramms < 0 ? 0 : gift.gramms);
|
||||
if (gift.init_state.not_null()) {
|
||||
cbi.store_ones(2);
|
||||
cbi.store_ref(gift.init_state);
|
||||
} else {
|
||||
cbi.store_zeroes(1);
|
||||
}
|
||||
cbi.store_zeroes(1);
|
||||
store_gift_message(cbi, gift);
|
||||
return cbi.finalize();
|
||||
}
|
||||
void WalletInterface::store_gift_message(vm::CellBuilder &cb, const Gift &gift) {
|
||||
if (gift.body.not_null()) {
|
||||
auto body = vm::load_cell_slice(gift.body);
|
||||
//TODO: handle error
|
||||
CHECK(cb.append_cellslice_bool(body));
|
||||
return;
|
||||
}
|
||||
|
||||
if (gift.is_encrypted) {
|
||||
cb.store_long(1, 32);
|
||||
} else {
|
||||
cb.store_long(0, 32);
|
||||
}
|
||||
vm::CellString::store(cb, gift.message, 35 * 8).ensure();
|
||||
}
|
||||
} // namespace ton
|
|
@ -21,13 +21,17 @@
|
|||
#include "td/utils/common.h"
|
||||
#include "Ed25519.h"
|
||||
#include "block/block.h"
|
||||
#include "block/block-parse.h"
|
||||
#include "vm/cells/CellString.h"
|
||||
|
||||
#include "SmartContract.h"
|
||||
#include "SmartContractCode.h"
|
||||
#include "GenericAccount.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace ton {
|
||||
class WalletInterface {
|
||||
class WalletInterface : public SmartContract {
|
||||
public:
|
||||
struct Gift {
|
||||
block::StdAddress destination;
|
||||
|
@ -39,49 +43,91 @@ class WalletInterface {
|
|||
td::Ref<vm::Cell> body;
|
||||
td::Ref<vm::Cell> init_state;
|
||||
};
|
||||
struct DefaultInitData {
|
||||
td::SecureString public_key;
|
||||
td::uint32 wallet_id{0};
|
||||
td::uint32 seqno{0};
|
||||
DefaultInitData() = default;
|
||||
DefaultInitData(td::Slice key, td::uint32 wallet_id) : public_key(key), wallet_id(wallet_id) {
|
||||
}
|
||||
};
|
||||
|
||||
WalletInterface(State state) : SmartContract(std::move(state)) {
|
||||
}
|
||||
|
||||
virtual ~WalletInterface() {
|
||||
}
|
||||
|
||||
virtual size_t get_max_gifts_size() const = 0;
|
||||
virtual size_t get_max_message_size() const = 0;
|
||||
virtual td::Result<td::Ref<vm::Cell>> make_a_gift_message(const td::Ed25519::PrivateKey &private_key,
|
||||
td::uint32 valid_until, td::Span<Gift> gifts) const = 0;
|
||||
virtual td::Result<td::Ed25519::PublicKey> get_public_key() const {
|
||||
return td::Status::Error("Unsupported");
|
||||
|
||||
virtual td::Result<td::uint32> get_seqno() const;
|
||||
virtual td::Result<td::uint32> get_wallet_id() const;
|
||||
virtual td::Result<td::uint64> get_balance(td::uint64 account_balance, td::uint32 now) const;
|
||||
virtual td::Result<td::Ed25519::PublicKey> get_public_key() const;
|
||||
|
||||
td::Result<td::Ref<vm::Cell>> get_init_message(const td::Ed25519::PrivateKey &private_key,
|
||||
td::uint32 valid_until = std::numeric_limits<td::uint32>::max()) const;
|
||||
|
||||
static td::Ref<vm::Cell> create_int_message(const Gift &gift);
|
||||
static void store_gift_message(vm::CellBuilder &cb, const Gift &gift);
|
||||
};
|
||||
|
||||
template <class WalletT, class TraitsT>
|
||||
class WalletBase : public WalletInterface {
|
||||
public:
|
||||
using Traits = TraitsT;
|
||||
using InitData = typename Traits::InitData;
|
||||
|
||||
explicit WalletBase(State state) : WalletInterface(std::move(state)) {
|
||||
}
|
||||
|
||||
td::Result<td::Ref<vm::Cell>> get_init_message(
|
||||
const td::Ed25519::PrivateKey &private_key,
|
||||
td::uint32 valid_until = std::numeric_limits<td::uint32>::max()) const {
|
||||
return make_a_gift_message(private_key, valid_until, {});
|
||||
size_t get_max_gifts_size() const override {
|
||||
return Traits::max_gifts_size;
|
||||
}
|
||||
static td::Ref<vm::Cell> create_int_message(const Gift &gift) {
|
||||
vm::CellBuilder cbi;
|
||||
GenericAccount::store_int_message(cbi, gift.destination, gift.gramms < 0 ? 0 : gift.gramms);
|
||||
if (gift.init_state.not_null()) {
|
||||
cbi.store_ones(2);
|
||||
cbi.store_ref(gift.init_state);
|
||||
} else {
|
||||
cbi.store_zeroes(1);
|
||||
}
|
||||
cbi.store_zeroes(1);
|
||||
store_gift_message(cbi, gift);
|
||||
return cbi.finalize();
|
||||
size_t get_max_message_size() const override {
|
||||
return Traits::max_message_size;
|
||||
}
|
||||
static void store_gift_message(vm::CellBuilder &cb, const Gift &gift) {
|
||||
if (gift.body.not_null()) {
|
||||
auto body = vm::load_cell_slice(gift.body);
|
||||
//TODO: handle error
|
||||
CHECK(cb.append_cellslice_bool(body));
|
||||
return;
|
||||
}
|
||||
|
||||
if (gift.is_encrypted) {
|
||||
cb.store_long(1, 32);
|
||||
} else {
|
||||
cb.store_long(0, 32);
|
||||
static td::Ref<WalletT> create(State state) {
|
||||
return td::Ref<WalletT>(true, std::move(state));
|
||||
}
|
||||
static td::Ref<vm::Cell> get_init_code(int revision) {
|
||||
return SmartContractCode::get_code(get_code_type(), revision);
|
||||
};
|
||||
static State get_init_state(int revision, const InitData &init_data) {
|
||||
return {get_init_code(revision), WalletT::get_init_data(init_data)};
|
||||
}
|
||||
static SmartContractCode::Type get_code_type() {
|
||||
return Traits::code_type;
|
||||
}
|
||||
static td::optional<td::int32> guess_revision(const vm::Cell::Hash &code_hash) {
|
||||
for (auto revision : ton::SmartContractCode::get_revisions(get_code_type())) {
|
||||
auto code = get_init_code(revision);
|
||||
if (code->get_hash() == code_hash) {
|
||||
return revision;
|
||||
}
|
||||
}
|
||||
vm::CellString::store(cb, gift.message, 35 * 8).ensure();
|
||||
return {};
|
||||
}
|
||||
static td::Span<td::int32> get_revisions() {
|
||||
return ton::SmartContractCode::get_revisions(get_code_type());
|
||||
}
|
||||
static td::optional<td::int32> guess_revision(block::StdAddress &address, const InitData &init_data) {
|
||||
for (auto revision : get_revisions()) {
|
||||
if (WalletT(get_init_state(revision, init_data)).get_address(address.workchain) == address) {
|
||||
return revision;
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
static td::Ref<WalletT> create(const InitData &init_data, int revision) {
|
||||
return td::Ref<WalletT>(true, State{get_init_code(revision), WalletT::get_init_data(init_data)});
|
||||
}
|
||||
CntObject *make_copy() const override {
|
||||
return new WalletT(get_state());
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -27,36 +27,11 @@
|
|||
#include <limits>
|
||||
|
||||
namespace ton {
|
||||
td::Ref<vm::Cell> WalletV3::get_init_state(const td::Ed25519::PublicKey& public_key, td::uint32 wallet_id,
|
||||
td::int32 revision) noexcept {
|
||||
auto code = get_init_code(revision);
|
||||
auto data = get_init_data(public_key, wallet_id);
|
||||
return GenericAccount::get_init_state(std::move(code), std::move(data));
|
||||
}
|
||||
|
||||
td::optional<td::int32> WalletV3::guess_revision(const vm::Cell::Hash& code_hash) {
|
||||
for (td::int32 i = 1; i <= 2; i++) {
|
||||
if (get_init_code(i)->get_hash() == code_hash) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
td::optional<td::int32> WalletV3::guess_revision(const block::StdAddress& address,
|
||||
const td::Ed25519::PublicKey& public_key, td::uint32 wallet_id) {
|
||||
for (td::int32 i = 1; i <= 2; i++) {
|
||||
if (GenericAccount::get_address(address.workchain, get_init_state(public_key, wallet_id, i)) == address) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
td::Ref<vm::Cell> WalletV3::make_a_gift_message(const td::Ed25519::PrivateKey& private_key, td::uint32 wallet_id,
|
||||
td::uint32 seqno, td::uint32 valid_until,
|
||||
td::Span<Gift> gifts) noexcept {
|
||||
CHECK(gifts.size() <= max_gifts_size);
|
||||
|
||||
td::Result<td::Ref<vm::Cell>> WalletV3::make_a_gift_message(const td::Ed25519::PrivateKey& private_key,
|
||||
td::uint32 valid_until, td::Span<Gift> gifts) const {
|
||||
CHECK(gifts.size() <= get_max_gifts_size());
|
||||
TRY_RESULT(seqno, get_seqno());
|
||||
TRY_RESULT(wallet_id, get_wallet_id());
|
||||
vm::CellBuilder cb;
|
||||
cb.store_long(wallet_id, 32).store_long(valid_until, 32).store_long(seqno, 32);
|
||||
|
||||
|
@ -73,63 +48,36 @@ td::Ref<vm::Cell> WalletV3::make_a_gift_message(const td::Ed25519::PrivateKey& p
|
|||
return vm::CellBuilder().store_bytes(signature).append_cellslice(vm::load_cell_slice(message_outer)).finalize();
|
||||
}
|
||||
|
||||
td::Ref<vm::Cell> WalletV3::get_init_code(td::int32 revision) noexcept {
|
||||
return SmartContractCode::get_code(ton::SmartContractCode::WalletV3, revision);
|
||||
}
|
||||
|
||||
vm::CellHash WalletV3::get_init_code_hash() noexcept {
|
||||
return get_init_code()->get_hash();
|
||||
}
|
||||
|
||||
td::Ref<vm::Cell> WalletV3::get_init_data(const td::Ed25519::PublicKey& public_key, td::uint32 wallet_id,
|
||||
td::uint32 seqno) noexcept {
|
||||
td::Ref<vm::Cell> WalletV3::get_init_data(const InitData& init_data) noexcept {
|
||||
return vm::CellBuilder()
|
||||
.store_long(seqno, 32)
|
||||
.store_long(wallet_id, 32)
|
||||
.store_bytes(public_key.as_octet_string())
|
||||
.store_long(init_data.seqno, 32)
|
||||
.store_long(init_data.wallet_id, 32)
|
||||
.store_bytes(init_data.public_key)
|
||||
.finalize();
|
||||
}
|
||||
|
||||
td::Result<td::uint32> WalletV3::get_seqno() const {
|
||||
return TRY_VM(get_seqno_or_throw());
|
||||
}
|
||||
|
||||
td::Result<td::uint32> WalletV3::get_seqno_or_throw() const {
|
||||
if (state_.data.is_null()) {
|
||||
return 0;
|
||||
}
|
||||
//FIXME use get method
|
||||
return static_cast<td::uint32>(vm::load_cell_slice(state_.data).fetch_ulong(32));
|
||||
}
|
||||
|
||||
td::Result<td::uint32> WalletV3::get_wallet_id() const {
|
||||
return TRY_VM(get_wallet_id_or_throw());
|
||||
}
|
||||
|
||||
td::Result<td::uint32> WalletV3::get_wallet_id_or_throw() const {
|
||||
if (state_.data.is_null()) {
|
||||
return 0;
|
||||
}
|
||||
//FIXME use get method
|
||||
auto cs = vm::load_cell_slice(state_.data);
|
||||
cs.skip_first(32);
|
||||
return static_cast<td::uint32>(cs.fetch_ulong(32));
|
||||
return TRY_VM([&]() -> td::Result<td::uint32> {
|
||||
if (state_.data.is_null()) {
|
||||
return 0;
|
||||
}
|
||||
auto cs = vm::load_cell_slice(state_.data);
|
||||
cs.skip_first(32);
|
||||
return static_cast<td::uint32>(cs.fetch_ulong(32));
|
||||
}());
|
||||
}
|
||||
|
||||
td::Result<td::Ed25519::PublicKey> WalletV3::get_public_key() const {
|
||||
return TRY_VM(get_public_key_or_throw());
|
||||
}
|
||||
|
||||
td::Result<td::Ed25519::PublicKey> WalletV3::get_public_key_or_throw() const {
|
||||
if (state_.data.is_null()) {
|
||||
return td::Status::Error("data is null");
|
||||
}
|
||||
//FIXME use get method
|
||||
auto cs = vm::load_cell_slice(state_.data);
|
||||
cs.skip_first(64);
|
||||
td::SecureString res(td::Ed25519::PublicKey::LENGTH);
|
||||
cs.fetch_bytes(res.as_mutable_slice().ubegin(), td::narrow_cast<td::int32>(res.size()));
|
||||
return td::Ed25519::PublicKey(std::move(res));
|
||||
return TRY_VM([&]() -> td::Result<td::Ed25519::PublicKey> {
|
||||
if (state_.data.is_null()) {
|
||||
return td::Status::Error("data is null");
|
||||
}
|
||||
auto cs = vm::load_cell_slice(state_.data);
|
||||
cs.skip_first(64);
|
||||
td::SecureString res(td::Ed25519::PublicKey::LENGTH);
|
||||
cs.fetch_bytes(res.as_mutable_slice().ubegin(), td::narrow_cast<td::int32>(res.size()));
|
||||
return td::Ed25519::PublicKey(std::move(res));
|
||||
}());
|
||||
}
|
||||
|
||||
} // namespace ton
|
||||
|
|
|
@ -26,135 +26,30 @@
|
|||
#include "vm/cells/CellString.h"
|
||||
|
||||
namespace ton {
|
||||
class WalletV3 : public ton::SmartContract, public WalletInterface {
|
||||
public:
|
||||
explicit WalletV3(State state) : ton::SmartContract(std::move(state)) {
|
||||
}
|
||||
explicit WalletV3(const td::Ed25519::PublicKey& public_key, td::uint32 wallet_id, td::uint32 seqno = 0)
|
||||
: WalletV3(State{get_init_code(), get_init_data(public_key, wallet_id, seqno)}) {
|
||||
}
|
||||
|
||||
struct WalletV3Traits {
|
||||
using InitData = WalletInterface::DefaultInitData;
|
||||
|
||||
static constexpr unsigned max_message_size = vm::CellString::max_bytes;
|
||||
static constexpr unsigned max_gifts_size = 4;
|
||||
static constexpr auto code_type = SmartContractCode::WalletV3;
|
||||
};
|
||||
|
||||
static td::optional<td::int32> guess_revision(const vm::Cell::Hash& code_hash);
|
||||
static td::optional<td::int32> guess_revision(const block::StdAddress& address,
|
||||
const td::Ed25519::PublicKey& public_key, td::uint32 wallet_id);
|
||||
static td::Ref<vm::Cell> get_init_state(const td::Ed25519::PublicKey& public_key, td::uint32 wallet_id,
|
||||
td::int32 revision = 0) noexcept;
|
||||
static td::Ref<vm::Cell> make_a_gift_message(const td::Ed25519::PrivateKey& private_key, td::uint32 wallet_id,
|
||||
td::uint32 seqno, td::uint32 valid_until, td::Span<Gift> gifts) noexcept;
|
||||
|
||||
static td::Ref<vm::Cell> get_init_code(td::int32 revision = 0) noexcept;
|
||||
static vm::CellHash get_init_code_hash() noexcept;
|
||||
static td::Ref<vm::Cell> get_init_data(const td::Ed25519::PublicKey& public_key, td::uint32 wallet_id,
|
||||
td::uint32 seqno = 0) noexcept;
|
||||
|
||||
td::Result<td::uint32> get_seqno() const;
|
||||
td::Result<td::uint32> get_wallet_id() const;
|
||||
|
||||
using WalletInterface::get_init_message;
|
||||
class WalletV3 : public WalletBase<WalletV3, WalletV3Traits> {
|
||||
public:
|
||||
explicit WalletV3(State state) : WalletBase(std::move(state)) {
|
||||
}
|
||||
td::Result<td::Ref<vm::Cell>> make_a_gift_message(const td::Ed25519::PrivateKey& private_key, td::uint32 valid_until,
|
||||
td::Span<Gift> gifts) const override {
|
||||
TRY_RESULT(seqno, get_seqno());
|
||||
TRY_RESULT(wallet_id, get_wallet_id());
|
||||
return make_a_gift_message(private_key, wallet_id, seqno, valid_until, gifts);
|
||||
}
|
||||
size_t get_max_gifts_size() const override {
|
||||
return max_gifts_size;
|
||||
}
|
||||
td::Result<td::Ed25519::PublicKey> get_public_key() const override;
|
||||
td::Span<Gift> gifts) const override;
|
||||
static td::Ref<vm::Cell> get_init_data(const InitData& init_data) noexcept;
|
||||
|
||||
private:
|
||||
td::Result<td::uint32> get_seqno_or_throw() const;
|
||||
td::Result<td::uint32> get_wallet_id_or_throw() const;
|
||||
td::Result<td::Ed25519::PublicKey> get_public_key_or_throw() const;
|
||||
// can't use get methods for compatibility with old revisions
|
||||
td::Result<td::uint32> get_wallet_id() const override;
|
||||
td::Result<td::Ed25519::PublicKey> get_public_key() const override;
|
||||
};
|
||||
} // namespace ton
|
||||
|
||||
#include "smc-envelope/SmartContractCode.h"
|
||||
#include "smc-envelope/GenericAccount.h"
|
||||
#include "block/block-parse.h"
|
||||
#include <algorithm>
|
||||
namespace ton {
|
||||
template <class WalletT, class TraitsT>
|
||||
class WalletBase : public SmartContract, public WalletInterface {
|
||||
public:
|
||||
using Traits = TraitsT;
|
||||
using InitData = typename Traits::InitData;
|
||||
|
||||
explicit WalletBase(State state) : SmartContract(std::move(state)) {
|
||||
}
|
||||
static td::Ref<WalletT> create(State state) {
|
||||
return td::Ref<WalletT>(true, std::move(state));
|
||||
}
|
||||
static td::Ref<vm::Cell> get_init_code(int revision) {
|
||||
return SmartContractCode::get_code(get_code_type(), revision);
|
||||
};
|
||||
size_t get_max_gifts_size() const override {
|
||||
return Traits::max_gifts_size;
|
||||
}
|
||||
static SmartContractCode::Type get_code_type() {
|
||||
return Traits::code_type;
|
||||
}
|
||||
static td::optional<td::int32> guess_revision(const vm::Cell::Hash& code_hash) {
|
||||
for (auto i : ton::SmartContractCode::get_revisions(get_code_type())) {
|
||||
auto code = SmartContractCode::get_code(get_code_type(), i);
|
||||
if (code->get_hash() == code_hash) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
static td::Ref<WalletT> create(const InitData& init_data, int revision) {
|
||||
return td::Ref<WalletT>(true, State{get_init_code(revision), WalletT::get_init_data(init_data)});
|
||||
}
|
||||
|
||||
td::Result<td::uint32> get_seqno() const {
|
||||
return TRY_VM([&]() -> td::Result<td::uint32> {
|
||||
Answer answer = this->run_get_method("seqno");
|
||||
if (!answer.success) {
|
||||
return td::Status::Error("seqno get method failed");
|
||||
}
|
||||
return static_cast<td::uint32>(answer.stack.write().pop_long_range(std::numeric_limits<td::uint32>::max()));
|
||||
}());
|
||||
}
|
||||
td::Result<td::uint32> get_wallet_id() const {
|
||||
return TRY_VM([&]() -> td::Result<td::uint32> {
|
||||
Answer answer = this->run_get_method("wallet_id");
|
||||
if (!answer.success) {
|
||||
return td::Status::Error("seqno get method failed");
|
||||
}
|
||||
return static_cast<td::uint32>(answer.stack.write().pop_long_range(std::numeric_limits<td::uint32>::max()));
|
||||
}());
|
||||
}
|
||||
|
||||
td::Result<td::uint64> get_balance(td::uint64 account_balance, td::uint32 now) const {
|
||||
return TRY_VM([&]() -> td::Result<td::uint64> {
|
||||
Answer answer = this->run_get_method(Args().set_method_id("balance").set_balance(account_balance).set_now(now));
|
||||
if (!answer.success) {
|
||||
return td::Status::Error("balance get method failed");
|
||||
}
|
||||
return static_cast<td::uint64>(answer.stack.write().pop_long());
|
||||
}());
|
||||
}
|
||||
|
||||
td::Result<td::Ed25519::PublicKey> get_public_key() const override {
|
||||
return TRY_VM([&]() -> td::Result<td::Ed25519::PublicKey> {
|
||||
Answer answer = this->run_get_method("get_public_key");
|
||||
if (!answer.success) {
|
||||
return td::Status::Error("get_public_key get method failed");
|
||||
}
|
||||
auto key_int = answer.stack.write().pop_int();
|
||||
LOG(ERROR) << key_int->bit_size(false);
|
||||
td::SecureString bytes(32);
|
||||
if (!key_int->export_bytes(bytes.as_mutable_slice().ubegin(), bytes.size(), false)) {
|
||||
return td::Status::Error("not a public key");
|
||||
}
|
||||
return td::Ed25519::PublicKey(std::move(bytes));
|
||||
}());
|
||||
};
|
||||
};
|
||||
|
||||
struct RestrictedWalletTraits {
|
||||
struct InitData {
|
||||
|
|
|
@ -20,9 +20,9 @@
|
|||
#include "vm/cellslice.h"
|
||||
#include "vm/cells.h"
|
||||
#include "common/AtomicRef.h"
|
||||
#include "vm/cells/CellString.h"
|
||||
#include "vm/cells/MerkleProof.h"
|
||||
#include "vm/cells/MerkleUpdate.h"
|
||||
#include "vm/db/BlobView.h"
|
||||
#include "vm/db/CellStorage.h"
|
||||
#include "vm/db/CellHashTable.h"
|
||||
#include "vm/db/TonDb.h"
|
||||
|
@ -33,6 +33,7 @@
|
|||
#include "td/utils/crypto.h"
|
||||
#include "td/utils/Random.h"
|
||||
#include "td/utils/Slice.h"
|
||||
#include "td/utils/Span.h"
|
||||
#include "td/utils/Status.h"
|
||||
#include "td/utils/Timer.h"
|
||||
#include "td/utils/filesystem.h"
|
||||
|
@ -44,8 +45,12 @@
|
|||
#include "td/utils/tl_parsers.h"
|
||||
#include "td/utils/tl_helpers.h"
|
||||
|
||||
#include "td/db/utils/BlobView.h"
|
||||
#include "td/db/RocksDb.h"
|
||||
#include "td/db/MemoryKeyValue.h"
|
||||
#include "td/db/utils/CyclicBuffer.h"
|
||||
|
||||
#include "td/fec/fec.h"
|
||||
|
||||
#include <set>
|
||||
#include <map>
|
||||
|
@ -433,17 +438,10 @@ class RandomBagOfCells {
|
|||
};
|
||||
};
|
||||
|
||||
template <class T>
|
||||
void random_shuffle(td::MutableSpan<T> v, td::Random::Xorshift128plus &rnd) {
|
||||
for (std::size_t i = 1; i < v.size(); i++) {
|
||||
auto pos = static_cast<std::size_t>(rnd() % (i + 1));
|
||||
std::swap(v[i], v[pos]);
|
||||
}
|
||||
}
|
||||
Ref<Cell> gen_random_cell(int size, td::Random::Xorshift128plus &rnd, bool with_prunned_branches = true,
|
||||
std::vector<Ref<Cell>> cells = {}) {
|
||||
if (!cells.empty()) {
|
||||
random_shuffle(td::MutableSpan<Ref<Cell>>(cells), rnd);
|
||||
td::random_shuffle(as_mutable_span(cells), rnd);
|
||||
cells.resize(cells.size() % rnd());
|
||||
}
|
||||
return RandomBagOfCells(size, rnd, with_prunned_branches, std::move(cells)).get_root();
|
||||
|
@ -451,7 +449,7 @@ Ref<Cell> gen_random_cell(int size, td::Random::Xorshift128plus &rnd, bool with_
|
|||
std::vector<Ref<Cell>> gen_random_cells(int roots, int size, td::Random::Xorshift128plus &rnd,
|
||||
bool with_prunned_branches = true, std::vector<Ref<Cell>> cells = {}) {
|
||||
if (!cells.empty()) {
|
||||
random_shuffle(td::MutableSpan<Ref<Cell>>(cells), rnd);
|
||||
td::random_shuffle(as_mutable_span(cells), rnd);
|
||||
cells.resize(cells.size() % rnd());
|
||||
}
|
||||
return RandomBagOfCells(size, rnd, with_prunned_branches, std::move(cells)).get_random_roots(roots, rnd);
|
||||
|
@ -788,7 +786,7 @@ TEST(TonDb, DynamicBoc) {
|
|||
old_root_serialization = serialize_boc(cell);
|
||||
|
||||
// Check that DynamicBagOfCells properly loads cells
|
||||
cell = vm::StaticBagOfCellsDbLazy::create(vm::BufferSliceBlobView::create(td::BufferSlice(old_root_serialization)))
|
||||
cell = vm::StaticBagOfCellsDbLazy::create(td::BufferSliceBlobView::create(td::BufferSlice(old_root_serialization)))
|
||||
.move_as_ok()
|
||||
->get_root_cell(0)
|
||||
.move_as_ok();
|
||||
|
@ -1599,11 +1597,11 @@ class BenchBocDeserializer : public td::Benchmark {
|
|||
auto blob = [&] {
|
||||
switch (config_.blob_type) {
|
||||
case BenchBocDeserializerConfig::File:
|
||||
return vm::FileBlobView::create("serialization").move_as_ok();
|
||||
return td::FileBlobView::create("serialization").move_as_ok();
|
||||
case BenchBocDeserializerConfig::Memory:
|
||||
return vm::BufferSliceBlobView::create(serialization_.clone());
|
||||
return td::BufferSliceBlobView::create(serialization_.clone());
|
||||
case BenchBocDeserializerConfig::FileMemoryMap:
|
||||
return vm::FileMemoryMappingBlobView::create("serialization").move_as_ok();
|
||||
return td::FileMemoryMappingBlobView::create("serialization").move_as_ok();
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
@ -2083,222 +2081,6 @@ TEST(Ref, AtomicRef) {
|
|||
LOG(ERROR) << String::total_strings.sum();
|
||||
}
|
||||
|
||||
class FileMerkleTree {
|
||||
public:
|
||||
FileMerkleTree(size_t chunks_count, td::Ref<vm::Cell> root = {}) {
|
||||
log_n_ = 0;
|
||||
while ((size_t(1) << log_n_) < chunks_count) {
|
||||
log_n_++;
|
||||
}
|
||||
n_ = size_t(1) << log_n_;
|
||||
mark_.resize(n_ * 2);
|
||||
proof_.resize(n_ * 2);
|
||||
|
||||
CHECK(n_ == chunks_count); // TODO: support other chunks_count
|
||||
//auto x = vm::CellBuilder().finalize();
|
||||
root_ = std::move(root);
|
||||
}
|
||||
|
||||
struct Chunk {
|
||||
td::size_t index{0};
|
||||
td::Slice hash;
|
||||
};
|
||||
|
||||
void remove_chunk(td::size_t index) {
|
||||
CHECK(index < n_);
|
||||
index += n_;
|
||||
while (proof_[index].not_null()) {
|
||||
proof_[index] = {};
|
||||
index /= 2;
|
||||
}
|
||||
}
|
||||
|
||||
bool has_chunk(td::size_t index) const {
|
||||
CHECK(index < n_);
|
||||
index += n_;
|
||||
return proof_[index].not_null();
|
||||
}
|
||||
|
||||
void add_chunk(td::size_t index, td::Slice hash) {
|
||||
CHECK(hash.size() == 32);
|
||||
CHECK(index < n_);
|
||||
index += n_;
|
||||
auto cell = vm::CellBuilder().store_bytes(hash).finalize();
|
||||
CHECK(proof_[index].is_null());
|
||||
proof_[index] = std::move(cell);
|
||||
for (index /= 2; index != 0; index /= 2) {
|
||||
CHECK(proof_[index].is_null());
|
||||
auto &left = proof_[index * 2];
|
||||
auto &right = proof_[index * 2 + 1];
|
||||
if (left.not_null() && right.not_null()) {
|
||||
proof_[index] = vm::CellBuilder().store_ref(left).store_ref(right).finalize();
|
||||
} else {
|
||||
mark_[index] = mark_id_;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
td::Status validate_proof(td::Ref<vm::Cell> new_root) {
|
||||
// TODO: check structure
|
||||
return td::Status::OK();
|
||||
}
|
||||
|
||||
td::Status add_proof(td::Ref<vm::Cell> new_root) {
|
||||
TRY_STATUS(validate_proof(new_root));
|
||||
auto combined = vm::MerkleProof::combine_fast_raw(root_, new_root);
|
||||
if (combined.is_null()) {
|
||||
return td::Status::Error("Can't combine proofs");
|
||||
}
|
||||
root_ = std::move(combined);
|
||||
return td::Status::OK();
|
||||
}
|
||||
|
||||
td::Status try_add_chunks(td::Span<Chunk> chunks) {
|
||||
for (auto chunk : chunks) {
|
||||
if (has_chunk(chunk.index)) {
|
||||
return td::Status::Error("Already has chunk");
|
||||
}
|
||||
}
|
||||
mark_id_++;
|
||||
for (auto chunk : chunks) {
|
||||
add_chunk(chunk.index, chunk.hash);
|
||||
}
|
||||
auto r_new_root = merge(root_, 1);
|
||||
if (r_new_root.is_error()) {
|
||||
for (auto chunk : chunks) {
|
||||
remove_chunk(chunk.index);
|
||||
}
|
||||
return r_new_root.move_as_error();
|
||||
}
|
||||
root_ = r_new_root.move_as_ok();
|
||||
return td::Status::OK();
|
||||
}
|
||||
|
||||
td::Result<td::Ref<vm::Cell>> merge(td::Ref<vm::Cell> root, size_t index) {
|
||||
const auto &down = proof_[index];
|
||||
if (down.not_null()) {
|
||||
if (down->get_hash() != root->get_hash(0)) {
|
||||
return td::Status::Error("Hash mismatch");
|
||||
}
|
||||
return down;
|
||||
}
|
||||
|
||||
if (mark_[index] != mark_id_) {
|
||||
return root;
|
||||
}
|
||||
|
||||
vm::CellSlice cs(vm::NoVm(), root);
|
||||
if (cs.is_special()) {
|
||||
return td::Status::Error("Proof is not enough to validate chunks");
|
||||
}
|
||||
|
||||
CHECK(cs.size_refs() == 2);
|
||||
vm::CellBuilder cb;
|
||||
cb.store_bits(cs.fetch_bits(cs.size()));
|
||||
TRY_RESULT(left, merge(cs.fetch_ref(), index * 2));
|
||||
TRY_RESULT(right, merge(cs.fetch_ref(), index * 2 + 1));
|
||||
cb.store_ref(std::move(left)).store_ref(std::move(right));
|
||||
return cb.finalize();
|
||||
}
|
||||
|
||||
void init_proof() {
|
||||
CHECK(proof_[1].not_null());
|
||||
root_ = proof_[1];
|
||||
}
|
||||
|
||||
td::Result<td::Ref<vm::Cell>> gen_proof(size_t l, size_t r) {
|
||||
auto usage_tree = std::make_shared<vm::CellUsageTree>();
|
||||
auto usage_cell = vm::UsageCell::create(root_, usage_tree->root_ptr());
|
||||
TRY_STATUS(do_gen_proof(std::move(usage_cell), 0, n_ - 1, l, r));
|
||||
auto res = vm::MerkleProof::generate_raw(root_, usage_tree.get());
|
||||
CHECK(res.not_null());
|
||||
return res;
|
||||
}
|
||||
|
||||
private:
|
||||
td::size_t n_; // n = 2^log_n
|
||||
td::size_t log_n_;
|
||||
td::size_t mark_id_{0};
|
||||
std::vector<td::size_t> mark_; // n_ * 2
|
||||
std::vector<td::Ref<vm::Cell>> proof_; // n_ * 2
|
||||
td::Ref<vm::Cell> root_;
|
||||
|
||||
td::Status do_gen_proof(td::Ref<vm::Cell> node, size_t il, size_t ir, size_t l, size_t r) {
|
||||
if (ir < l || il > r) {
|
||||
return td::Status::OK();
|
||||
}
|
||||
if (l <= il && ir <= r) {
|
||||
return td::Status::OK();
|
||||
}
|
||||
vm::CellSlice cs(vm::NoVm(), std::move(node));
|
||||
if (cs.is_special()) {
|
||||
return td::Status::Error("Can't generate a proof");
|
||||
}
|
||||
CHECK(cs.size_refs() == 2);
|
||||
auto ic = (il + ir) / 2;
|
||||
TRY_STATUS(do_gen_proof(cs.fetch_ref(), il, ic, l, r));
|
||||
TRY_STATUS(do_gen_proof(cs.fetch_ref(), ic + 1, ir, l, r));
|
||||
return td::Status::OK();
|
||||
}
|
||||
};
|
||||
|
||||
TEST(FileMerkleTree, Manual) {
|
||||
// create big random file
|
||||
size_t chunk_size = 768;
|
||||
// for simplicity numer of chunks in a file is a power of two
|
||||
size_t chunks_count = 1 << 16;
|
||||
size_t file_size = chunk_size * chunks_count;
|
||||
td::Timer timer;
|
||||
LOG(INFO) << "Generate random string";
|
||||
const auto file = td::rand_string('a', 'z', td::narrow_cast<int>(file_size));
|
||||
LOG(INFO) << timer;
|
||||
|
||||
timer = {};
|
||||
LOG(INFO) << "Calculate all hashes";
|
||||
std::vector<td::UInt256> hashes(chunks_count);
|
||||
for (size_t i = 0; i < chunks_count; i++) {
|
||||
td::sha256(td::Slice(file).substr(i * chunk_size, chunk_size), hashes[i].as_slice());
|
||||
}
|
||||
LOG(INFO) << timer;
|
||||
|
||||
timer = {};
|
||||
LOG(INFO) << "Init merkle tree";
|
||||
FileMerkleTree tree(chunks_count);
|
||||
for (size_t i = 0; i < chunks_count; i++) {
|
||||
tree.add_chunk(i, hashes[i].as_slice());
|
||||
}
|
||||
tree.init_proof();
|
||||
LOG(INFO) << timer;
|
||||
|
||||
auto root_proof = tree.gen_proof(0, chunks_count - 1).move_as_ok();
|
||||
|
||||
// first download each chunk one by one
|
||||
|
||||
for (size_t stride : {1 << 6, 1}) {
|
||||
timer = {};
|
||||
LOG(INFO) << "Gen all proofs, stride = " << stride;
|
||||
for (size_t i = 0; i < chunks_count; i += stride) {
|
||||
tree.gen_proof(i, i + stride - 1).move_as_ok();
|
||||
}
|
||||
LOG(INFO) << timer;
|
||||
timer = {};
|
||||
LOG(INFO) << "Proof size: " << vm::std_boc_serialize(tree.gen_proof(0, stride - 1).move_as_ok()).ok().size();
|
||||
LOG(INFO) << "Download file, stride = " << stride;
|
||||
{
|
||||
FileMerkleTree new_tree(chunks_count, root_proof);
|
||||
for (size_t i = 0; i < chunks_count; i += stride) {
|
||||
new_tree.add_proof(tree.gen_proof(i, i + stride - 1).move_as_ok()).ensure();
|
||||
std::vector<FileMerkleTree::Chunk> chunks;
|
||||
for (size_t j = 0; j < stride; j++) {
|
||||
chunks.push_back({i + j, hashes[i + j].as_slice()});
|
||||
}
|
||||
new_tree.try_add_chunks(chunks).ensure();
|
||||
}
|
||||
}
|
||||
LOG(INFO) << timer;
|
||||
}
|
||||
}
|
||||
|
||||
//TEST(Tmp, Boc) {
|
||||
//LOG(ERROR) << "A";
|
||||
//auto data = td::read_file("boc");
|
||||
|
|
|
@ -34,9 +34,6 @@
|
|||
#include "smc-envelope/MultisigWallet.h"
|
||||
#include "smc-envelope/SmartContract.h"
|
||||
#include "smc-envelope/SmartContractCode.h"
|
||||
#include "smc-envelope/TestGiver.h"
|
||||
#include "smc-envelope/TestWallet.h"
|
||||
#include "smc-envelope/Wallet.h"
|
||||
#include "smc-envelope/WalletV3.h"
|
||||
#include "smc-envelope/HighloadWallet.h"
|
||||
#include "smc-envelope/HighloadWalletV2.h"
|
||||
|
@ -66,62 +63,6 @@ std::string load_source(std::string name) {
|
|||
return td::read_file_str(current_dir() + "../../crypto/" + name).move_as_ok();
|
||||
}
|
||||
|
||||
td::Ref<vm::Cell> get_test_wallet_source() {
|
||||
std::string code = R"ABCD(
|
||||
SETCP0 DUP IFNOTRET // return if recv_internal
|
||||
DUP 85143 INT EQUAL OVER 78748 INT EQUAL OR IFJMP:<{ // "seqno" and "get_public_key" get-methods
|
||||
1 INT AND c4 PUSHCTR CTOS 32 LDU 256 PLDU CONDSEL // cnt or pubk
|
||||
}>
|
||||
INC 32 THROWIF // fail unless recv_external
|
||||
512 INT LDSLICEX DUP 32 PLDU // sign cs cnt
|
||||
c4 PUSHCTR CTOS 32 LDU 256 LDU ENDS // sign cs cnt cnt' pubk
|
||||
s1 s2 XCPU // sign cs cnt pubk cnt' cnt
|
||||
EQUAL 33 THROWIFNOT // ( seqno mismatch? )
|
||||
s2 PUSH HASHSU // sign cs cnt pubk hash
|
||||
s0 s4 s4 XC2PU // pubk cs cnt hash sign pubk
|
||||
CHKSIGNU // pubk cs cnt ?
|
||||
34 THROWIFNOT // signature mismatch
|
||||
ACCEPT
|
||||
SWAP 32 LDU NIP
|
||||
DUP SREFS IF:<{
|
||||
// 3 INT 35 LSHIFT# 3 INT RAWRESERVE // reserve all but 103 Grams from the balance
|
||||
8 LDU LDREF // pubk cnt mode msg cs
|
||||
s0 s2 XCHG SENDRAWMSG // pubk cnt cs ; ( message sent )
|
||||
}>
|
||||
ENDS
|
||||
INC NEWC 32 STU 256 STU ENDC c4 POPCTR
|
||||
)ABCD";
|
||||
return fift::compile_asm(code).move_as_ok();
|
||||
}
|
||||
|
||||
td::Ref<vm::Cell> get_wallet_source() {
|
||||
std::string code = R"ABCD(
|
||||
SETCP0 DUP IFNOTRET // return if recv_internal
|
||||
DUP 85143 INT EQUAL OVER 78748 INT EQUAL OR IFJMP:<{ // "seqno" and "get_public_key" get-methods
|
||||
1 INT AND c4 PUSHCTR CTOS 32 LDU 256 PLDU CONDSEL // cnt or pubk
|
||||
}>
|
||||
INC 32 THROWIF // fail unless recv_external
|
||||
9 PUSHPOW2 LDSLICEX DUP 32 LDU 32 LDU // signature in_msg msg_seqno valid_until cs
|
||||
SWAP NOW LEQ 35 THROWIF // signature in_msg msg_seqno cs
|
||||
c4 PUSH CTOS 32 LDU 256 LDU ENDS // signature in_msg msg_seqno cs stored_seqno public_key
|
||||
s3 s1 XCPU // signature in_msg public_key cs stored_seqno msg_seqno stored_seqno
|
||||
EQUAL 33 THROWIFNOT // signature in_msg public_key cs stored_seqno
|
||||
s0 s3 XCHG HASHSU // signature stored_seqno public_key cs hash
|
||||
s0 s4 s2 XC2PU CHKSIGNU 34 THROWIFNOT // cs stored_seqno public_key
|
||||
ACCEPT
|
||||
s0 s2 XCHG // public_key stored_seqno cs
|
||||
WHILE:<{
|
||||
DUP SREFS // public_key stored_seqno cs _40
|
||||
}>DO<{ // public_key stored_seqno cs
|
||||
// 3 INT 35 LSHIFT# 3 INT RAWRESERVE // reserve all but 103 Grams from the balance
|
||||
8 LDU LDREF s0 s2 XCHG // public_key stored_seqno cs _45 mode
|
||||
SENDRAWMSG // public_key stored_seqno cs
|
||||
}>
|
||||
ENDS INC // public_key seqno'
|
||||
NEWC 32 STU 256 STU ENDC c4 POP
|
||||
)ABCD";
|
||||
return fift::compile_asm(code).move_as_ok();
|
||||
}
|
||||
td::Ref<vm::Cell> get_wallet_v3_source() {
|
||||
std::string code = R"ABCD(
|
||||
SETCP0 DUP IFNOTRET // return if recv_internal
|
||||
|
@ -150,140 +91,37 @@ SETCP0 DUP IFNOTRET // return if recv_internal
|
|||
return fift::compile_asm(code).move_as_ok();
|
||||
}
|
||||
|
||||
TEST(Tonlib, TestWallet) {
|
||||
LOG(ERROR) << td::base64_encode(std_boc_serialize(get_test_wallet_source()).move_as_ok());
|
||||
CHECK(get_test_wallet_source()->get_hash() == ton::TestWallet::get_init_code()->get_hash());
|
||||
auto fift_output = fift::mem_run_fift(load_source("smartcont/new-wallet.fif"), {"aba", "0"}).move_as_ok();
|
||||
|
||||
auto new_wallet_pk = fift_output.source_lookup.read_file("new-wallet.pk").move_as_ok().data;
|
||||
auto new_wallet_query = fift_output.source_lookup.read_file("new-wallet-query.boc").move_as_ok().data;
|
||||
auto new_wallet_addr = fift_output.source_lookup.read_file("new-wallet.addr").move_as_ok().data;
|
||||
|
||||
td::Ed25519::PrivateKey priv_key{td::SecureString{new_wallet_pk}};
|
||||
auto pub_key = priv_key.get_public_key().move_as_ok();
|
||||
auto init_state = ton::TestWallet::get_init_state(pub_key);
|
||||
auto init_message = ton::TestWallet::get_init_message_new(priv_key);
|
||||
auto address = ton::GenericAccount::get_address(0, init_state);
|
||||
|
||||
CHECK(address.addr.as_slice() == td::Slice(new_wallet_addr).substr(0, 32));
|
||||
|
||||
td::Ref<vm::Cell> res = ton::GenericAccount::create_ext_message(address, init_state, init_message);
|
||||
|
||||
LOG(ERROR) << "-------";
|
||||
vm::load_cell_slice(res).print_rec(std::cerr);
|
||||
LOG(ERROR) << "-------";
|
||||
vm::load_cell_slice(vm::std_boc_deserialize(new_wallet_query).move_as_ok()).print_rec(std::cerr);
|
||||
CHECK(vm::std_boc_deserialize(new_wallet_query).move_as_ok()->get_hash() == res->get_hash());
|
||||
|
||||
fift_output.source_lookup.write_file("/main.fif", load_source("smartcont/wallet.fif")).ensure();
|
||||
auto dest = block::StdAddress::parse("Ef9Tj6fMJP+OqhAdhKXxq36DL+HYSzCc3+9O6UNzqsgPfYFX").move_as_ok();
|
||||
fift_output = fift::mem_run_fift(std::move(fift_output.source_lookup),
|
||||
{"aba", "new-wallet", "Ef9Tj6fMJP+OqhAdhKXxq36DL+HYSzCc3+9O6UNzqsgPfYFX", "123",
|
||||
"321", "-C", "TEST"})
|
||||
.move_as_ok();
|
||||
auto wallet_query = fift_output.source_lookup.read_file("wallet-query.boc").move_as_ok().data;
|
||||
ton::TestWallet::Gift gift;
|
||||
gift.destination = dest;
|
||||
gift.message = "TEST";
|
||||
gift.gramms = 321000000000ll;
|
||||
ton::TestWallet wallet(priv_key.get_public_key().move_as_ok(), 123);
|
||||
ASSERT_EQ(123u, wallet.get_seqno().ok());
|
||||
CHECK(priv_key.get_public_key().ok().as_octet_string() == wallet.get_public_key().ok().as_octet_string());
|
||||
auto gift_message = ton::GenericAccount::create_ext_message(
|
||||
address, {}, wallet.make_a_gift_message(priv_key, 0, {gift}).move_as_ok());
|
||||
LOG(ERROR) << "-------";
|
||||
vm::load_cell_slice(gift_message).print_rec(std::cerr);
|
||||
LOG(ERROR) << "-------";
|
||||
vm::load_cell_slice(vm::std_boc_deserialize(wallet_query).move_as_ok()).print_rec(std::cerr);
|
||||
CHECK(vm::std_boc_deserialize(wallet_query).move_as_ok()->get_hash() == gift_message->get_hash());
|
||||
}
|
||||
|
||||
td::Ref<vm::Cell> get_wallet_source_fc() {
|
||||
return fift::compile_asm(load_source("smartcont/wallet-code.fif"), "", false).move_as_ok();
|
||||
}
|
||||
|
||||
TEST(Tonlib, Wallet) {
|
||||
LOG(ERROR) << td::base64_encode(std_boc_serialize(get_wallet_source()).move_as_ok());
|
||||
CHECK(get_wallet_source()->get_hash() == ton::Wallet::get_init_code()->get_hash());
|
||||
|
||||
auto fift_output = fift::mem_run_fift(load_source("smartcont/new-wallet-v2.fif"), {"aba", "0"}).move_as_ok();
|
||||
|
||||
auto new_wallet_pk = fift_output.source_lookup.read_file("new-wallet.pk").move_as_ok().data;
|
||||
auto new_wallet_query = fift_output.source_lookup.read_file("new-wallet-query.boc").move_as_ok().data;
|
||||
auto new_wallet_addr = fift_output.source_lookup.read_file("new-wallet.addr").move_as_ok().data;
|
||||
|
||||
td::Ed25519::PrivateKey priv_key{td::SecureString{new_wallet_pk}};
|
||||
auto pub_key = priv_key.get_public_key().move_as_ok();
|
||||
auto init_state = ton::Wallet::get_init_state(pub_key);
|
||||
auto init_message = ton::Wallet::get_init_message_new(priv_key);
|
||||
auto address = ton::GenericAccount::get_address(0, init_state);
|
||||
|
||||
CHECK(address.addr.as_slice() == td::Slice(new_wallet_addr).substr(0, 32));
|
||||
|
||||
td::Ref<vm::Cell> res = ton::GenericAccount::create_ext_message(address, init_state, init_message);
|
||||
|
||||
LOG(ERROR) << "-------";
|
||||
vm::load_cell_slice(res).print_rec(std::cerr);
|
||||
LOG(ERROR) << "-------";
|
||||
vm::load_cell_slice(vm::std_boc_deserialize(new_wallet_query).move_as_ok()).print_rec(std::cerr);
|
||||
CHECK(vm::std_boc_deserialize(new_wallet_query).move_as_ok()->get_hash() == res->get_hash());
|
||||
|
||||
fift_output.source_lookup.write_file("/main.fif", load_source("smartcont/wallet-v2.fif")).ensure();
|
||||
class ZeroOsTime : public fift::OsTime {
|
||||
public:
|
||||
td::uint32 now() override {
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
fift_output.source_lookup.set_os_time(std::make_unique<ZeroOsTime>());
|
||||
auto dest = block::StdAddress::parse("Ef9Tj6fMJP+OqhAdhKXxq36DL+HYSzCc3+9O6UNzqsgPfYFX").move_as_ok();
|
||||
fift_output = fift::mem_run_fift(std::move(fift_output.source_lookup),
|
||||
{"aba", "new-wallet", "-C", "TESTv2",
|
||||
"Ef9Tj6fMJP+OqhAdhKXxq36DL+HYSzCc3+9O6UNzqsgPfYFX", "123", "321"})
|
||||
.move_as_ok();
|
||||
auto wallet_query = fift_output.source_lookup.read_file("wallet-query.boc").move_as_ok().data;
|
||||
ton::TestWallet::Gift gift;
|
||||
gift.destination = dest;
|
||||
gift.message = "TESTv2";
|
||||
gift.gramms = 321000000000ll;
|
||||
ton::Wallet wallet(priv_key.get_public_key().move_as_ok(), 123);
|
||||
ASSERT_EQ(123u, wallet.get_seqno().ok());
|
||||
CHECK(priv_key.get_public_key().ok().as_octet_string() == wallet.get_public_key().ok().as_octet_string());
|
||||
auto gift_message = ton::GenericAccount::create_ext_message(
|
||||
address, {}, wallet.make_a_gift_message(priv_key, 60, {gift}).move_as_ok());
|
||||
LOG(ERROR) << "-------";
|
||||
vm::load_cell_slice(gift_message).print_rec(std::cerr);
|
||||
LOG(ERROR) << "-------";
|
||||
vm::load_cell_slice(vm::std_boc_deserialize(wallet_query).move_as_ok()).print_rec(std::cerr);
|
||||
CHECK(vm::std_boc_deserialize(wallet_query).move_as_ok()->get_hash() == gift_message->get_hash());
|
||||
}
|
||||
|
||||
TEST(Tonlib, WalletV3) {
|
||||
LOG(ERROR) << td::base64_encode(std_boc_serialize(get_wallet_v3_source()).move_as_ok());
|
||||
CHECK(get_wallet_v3_source()->get_hash() == ton::WalletV3::get_init_code()->get_hash());
|
||||
CHECK(get_wallet_v3_source()->get_hash() == ton::WalletV3::get_init_code(2)->get_hash());
|
||||
|
||||
auto fift_output = fift::mem_run_fift(load_source("smartcont/new-wallet-v3.fif"), {"aba", "0", "239"}).move_as_ok();
|
||||
|
||||
auto new_wallet_pk = fift_output.source_lookup.read_file("new-wallet.pk").move_as_ok().data;
|
||||
auto new_wallet_query = fift_output.source_lookup.read_file("new-wallet-query.boc").move_as_ok().data;
|
||||
auto new_wallet_addr = fift_output.source_lookup.read_file("new-wallet.addr").move_as_ok().data;
|
||||
|
||||
td::Ed25519::PrivateKey priv_key{td::SecureString{new_wallet_pk}};
|
||||
auto pub_key = priv_key.get_public_key().move_as_ok();
|
||||
auto init_state = ton::WalletV3::get_init_state(pub_key, 239);
|
||||
auto init_message =
|
||||
ton::WalletV3(priv_key.get_public_key().move_as_ok(), 239).get_init_message(priv_key).move_as_ok();
|
||||
auto address = ton::GenericAccount::get_address(0, init_state);
|
||||
ton::WalletV3::InitData init_data;
|
||||
init_data.public_key = pub_key.as_octet_string();
|
||||
init_data.wallet_id = 239;
|
||||
auto wallet = ton::WalletV3::create(init_data, 2);
|
||||
ASSERT_EQ(239u, wallet->get_wallet_id().ok());
|
||||
ASSERT_EQ(0u, wallet->get_seqno().ok());
|
||||
|
||||
auto address = wallet->get_address();
|
||||
CHECK(address.addr.as_slice() == td::Slice(new_wallet_addr).substr(0, 32));
|
||||
|
||||
td::Ref<vm::Cell> res = ton::GenericAccount::create_ext_message(address, init_state, init_message);
|
||||
|
||||
auto init_message = wallet->get_init_message(priv_key).move_as_ok();
|
||||
td::Ref<vm::Cell> ext_init_message = ton::GenericAccount::create_ext_message(
|
||||
address, ton::GenericAccount::get_init_state(wallet->get_state()), init_message);
|
||||
LOG(ERROR) << "-------";
|
||||
vm::load_cell_slice(res).print_rec(std::cerr);
|
||||
vm::load_cell_slice(ext_init_message).print_rec(std::cerr);
|
||||
LOG(ERROR) << "-------";
|
||||
vm::load_cell_slice(vm::std_boc_deserialize(new_wallet_query).move_as_ok()).print_rec(std::cerr);
|
||||
CHECK(vm::std_boc_deserialize(new_wallet_query).move_as_ok()->get_hash() == res->get_hash());
|
||||
CHECK(vm::std_boc_deserialize(new_wallet_query).move_as_ok()->get_hash() == ext_init_message->get_hash());
|
||||
|
||||
CHECK(wallet.write().send_external_message(init_message).success);
|
||||
|
||||
fift_output.source_lookup.write_file("/main.fif", load_source("smartcont/wallet-v3.fif")).ensure();
|
||||
class ZeroOsTime : public fift::OsTime {
|
||||
|
@ -296,7 +134,7 @@ TEST(Tonlib, WalletV3) {
|
|||
auto dest = block::StdAddress::parse("Ef9Tj6fMJP+OqhAdhKXxq36DL+HYSzCc3+9O6UNzqsgPfYFX").move_as_ok();
|
||||
fift_output = fift::mem_run_fift(std::move(fift_output.source_lookup),
|
||||
{"aba", "new-wallet", "-C", "TESTv3",
|
||||
"Ef9Tj6fMJP+OqhAdhKXxq36DL+HYSzCc3+9O6UNzqsgPfYFX", "239", "123", "321"})
|
||||
"Ef9Tj6fMJP+OqhAdhKXxq36DL+HYSzCc3+9O6UNzqsgPfYFX", "239", "1", "321"})
|
||||
.move_as_ok();
|
||||
auto wallet_query = fift_output.source_lookup.read_file("wallet-query.boc").move_as_ok().data;
|
||||
|
||||
|
@ -305,15 +143,14 @@ TEST(Tonlib, WalletV3) {
|
|||
gift.message = "TESTv3";
|
||||
gift.gramms = 321000000000ll;
|
||||
|
||||
ton::WalletV3 wallet(priv_key.get_public_key().move_as_ok(), 239, 123);
|
||||
ASSERT_EQ(239u, wallet.get_wallet_id().ok());
|
||||
ASSERT_EQ(123u, wallet.get_seqno().ok());
|
||||
CHECK(priv_key.get_public_key().ok().as_octet_string() == wallet.get_public_key().ok().as_octet_string());
|
||||
ASSERT_EQ(239u, wallet->get_wallet_id().ok());
|
||||
ASSERT_EQ(1u, wallet->get_seqno().ok());
|
||||
CHECK(priv_key.get_public_key().ok().as_octet_string() == wallet->get_public_key().ok().as_octet_string());
|
||||
CHECK(priv_key.get_public_key().ok().as_octet_string() ==
|
||||
ton::GenericAccount::get_public_key(wallet).ok().as_octet_string());
|
||||
ton::GenericAccount::get_public_key(*wallet).ok().as_octet_string());
|
||||
|
||||
auto gift_message = ton::GenericAccount::create_ext_message(
|
||||
address, {}, wallet.make_a_gift_message(priv_key, 60, {gift}).move_as_ok());
|
||||
address, {}, wallet->make_a_gift_message(priv_key, 60, {gift}).move_as_ok());
|
||||
LOG(ERROR) << "-------";
|
||||
vm::load_cell_slice(gift_message).print_rec(std::cerr);
|
||||
LOG(ERROR) << "-------";
|
||||
|
@ -334,20 +171,21 @@ TEST(Tonlib, HighloadWallet) {
|
|||
|
||||
td::Ed25519::PrivateKey priv_key{td::SecureString{new_wallet_pk}};
|
||||
auto pub_key = priv_key.get_public_key().move_as_ok();
|
||||
auto init_state = ton::HighloadWallet::get_init_state(pub_key, 239, -1);
|
||||
auto init_message = ton::HighloadWallet::get_init_message(priv_key, 239);
|
||||
auto address = ton::GenericAccount::get_address(0, init_state);
|
||||
ton::HighloadWallet::InitData init_data(pub_key.as_octet_string(), 239);
|
||||
|
||||
ton::HighloadWallet wallet(
|
||||
{ton::HighloadWallet::get_init_code(-1), ton::HighloadWallet::get_init_data(pub_key, 239)});
|
||||
ASSERT_EQ(239u, wallet.get_wallet_id().ok());
|
||||
ASSERT_EQ(0u, wallet.get_seqno().ok());
|
||||
CHECK(pub_key.as_octet_string() == wallet.get_public_key().ok().as_octet_string());
|
||||
CHECK(pub_key.as_octet_string() == ton::GenericAccount::get_public_key(wallet).ok().as_octet_string());
|
||||
auto wallet = ton::HighloadWallet::create(init_data, -1);
|
||||
auto address = wallet->get_address();
|
||||
CHECK(address.addr.as_slice() == td::Slice(new_wallet_addr).substr(0, 32));
|
||||
ASSERT_EQ(239u, wallet->get_wallet_id().ok());
|
||||
ASSERT_EQ(0u, wallet->get_seqno().ok());
|
||||
CHECK(pub_key.as_octet_string() == wallet->get_public_key().ok().as_octet_string());
|
||||
CHECK(pub_key.as_octet_string() == ton::GenericAccount::get_public_key(*wallet).ok().as_octet_string());
|
||||
|
||||
CHECK(address.addr.as_slice() == td::Slice(new_wallet_addr).substr(0, 32));
|
||||
|
||||
td::Ref<vm::Cell> res = ton::GenericAccount::create_ext_message(address, init_state, init_message);
|
||||
auto init_message = wallet->get_init_message(priv_key).move_as_ok();
|
||||
td::Ref<vm::Cell> res = ton::GenericAccount::create_ext_message(
|
||||
address, ton::GenericAccount::get_init_state(wallet->get_state()), init_message);
|
||||
|
||||
LOG(ERROR) << "---smc-envelope----";
|
||||
vm::load_cell_slice(res).print_rec(std::cerr);
|
||||
|
@ -382,12 +220,14 @@ TEST(Tonlib, HighloadWallet) {
|
|||
return 0;
|
||||
}
|
||||
};
|
||||
init_data.seqno = 123;
|
||||
wallet = ton::HighloadWallet::create(init_data, -1);
|
||||
fift_output.source_lookup.set_os_time(std::make_unique<ZeroOsTime>());
|
||||
fift_output = fift::mem_run_fift(std::move(fift_output.source_lookup), {"aba", "new-wallet", "239", "123", "order"})
|
||||
.move_as_ok();
|
||||
auto wallet_query = fift_output.source_lookup.read_file("wallet-query.boc").move_as_ok().data;
|
||||
auto gift_message = ton::GenericAccount::create_ext_message(
|
||||
address, {}, ton::HighloadWallet::make_a_gift_message(priv_key, 239, 123, 60, gifts));
|
||||
address, {}, wallet->make_a_gift_message(priv_key, 60, gifts).move_as_ok());
|
||||
LOG(ERROR) << "---smc-envelope----";
|
||||
vm::load_cell_slice(gift_message).print_rec(std::cerr);
|
||||
LOG(ERROR) << "---fift scripts----";
|
||||
|
@ -416,19 +256,21 @@ TEST(Tonlib, HighloadWalletV2) {
|
|||
|
||||
td::Ed25519::PrivateKey priv_key{td::SecureString{new_wallet_pk}};
|
||||
auto pub_key = priv_key.get_public_key().move_as_ok();
|
||||
auto init_state = ton::HighloadWalletV2::get_init_state(pub_key, 239, -1);
|
||||
auto init_message = ton::HighloadWalletV2::get_init_message(priv_key, 239, 65535);
|
||||
auto address = ton::GenericAccount::get_address(0, init_state);
|
||||
ton::HighloadWalletV2::InitData init_data(pub_key.as_octet_string(), 239);
|
||||
|
||||
ton::HighloadWalletV2 wallet(
|
||||
{ton::HighloadWalletV2::get_init_code(-1), ton::HighloadWalletV2::get_init_data(pub_key, 239)});
|
||||
ASSERT_EQ(239u, wallet.get_wallet_id().ok());
|
||||
CHECK(pub_key.as_octet_string() == wallet.get_public_key().ok().as_octet_string());
|
||||
CHECK(pub_key.as_octet_string() == ton::GenericAccount::get_public_key(wallet).ok().as_octet_string());
|
||||
auto wallet = ton::HighloadWalletV2::create(init_data, -1);
|
||||
auto address = wallet->get_address();
|
||||
|
||||
ASSERT_EQ(239u, wallet->get_wallet_id().ok());
|
||||
wallet->get_seqno().ensure_error();
|
||||
CHECK(pub_key.as_octet_string() == wallet->get_public_key().ok().as_octet_string());
|
||||
CHECK(pub_key.as_octet_string() == ton::GenericAccount::get_public_key(*wallet).ok().as_octet_string());
|
||||
|
||||
CHECK(address.addr.as_slice() == td::Slice(new_wallet_addr).substr(0, 32));
|
||||
|
||||
td::Ref<vm::Cell> res = ton::GenericAccount::create_ext_message(address, init_state, init_message);
|
||||
auto init_message = wallet->get_init_message(priv_key, 65535).move_as_ok();
|
||||
td::Ref<vm::Cell> res = ton::GenericAccount::create_ext_message(
|
||||
address, ton::GenericAccount::get_init_state(wallet->get_state()), init_message);
|
||||
|
||||
LOG(ERROR) << "---smc-envelope----";
|
||||
vm::load_cell_slice(res).print_rec(std::cerr);
|
||||
|
@ -462,7 +304,7 @@ TEST(Tonlib, HighloadWalletV2) {
|
|||
fift::mem_run_fift(std::move(fift_output.source_lookup), {"aba", "new-wallet", "239", "order"}).move_as_ok();
|
||||
auto wallet_query = fift_output.source_lookup.read_file("wallet-query.boc").move_as_ok().data;
|
||||
auto gift_message = ton::GenericAccount::create_ext_message(
|
||||
address, {}, ton::HighloadWalletV2::make_a_gift_message(priv_key, 239, 60, gifts));
|
||||
address, {}, wallet->make_a_gift_message(priv_key, 60, gifts).move_as_ok());
|
||||
LOG(ERROR) << "---smc-envelope----";
|
||||
vm::load_cell_slice(gift_message).print_rec(std::cerr);
|
||||
LOG(ERROR) << "---fift scripts----";
|
||||
|
@ -470,28 +312,6 @@ TEST(Tonlib, HighloadWalletV2) {
|
|||
CHECK(vm::std_boc_deserialize(wallet_query).move_as_ok()->get_hash() == gift_message->get_hash());
|
||||
}
|
||||
|
||||
TEST(Tonlib, TestGiver) {
|
||||
auto address =
|
||||
block::StdAddress::parse("-1:60c04141c6a7b96d68615e7a91d265ad0f3a9a922e9ae9c901d4fa83f5d3c0d0").move_as_ok();
|
||||
LOG(ERROR) << address.bounceable;
|
||||
auto fift_output = fift::mem_run_fift(load_source("smartcont/testgiver.fif"),
|
||||
{"aba", address.rserialize(), "0", "6.666", "wallet-query"})
|
||||
.move_as_ok();
|
||||
LOG(ERROR) << fift_output.output;
|
||||
|
||||
auto wallet_query = fift_output.source_lookup.read_file("wallet-query.boc").move_as_ok().data;
|
||||
|
||||
ton::TestGiver::Gift gift;
|
||||
gift.gramms = 1000000000ll * 6666 / 1000;
|
||||
gift.message = "GIFT";
|
||||
gift.destination = address;
|
||||
td::Ed25519::PrivateKey key{td::SecureString()};
|
||||
auto res = ton::GenericAccount::create_ext_message(ton::TestGiver::address(), {},
|
||||
ton::TestGiver().make_a_gift_message(key, 0, {gift}).move_as_ok());
|
||||
vm::CellSlice(vm::NoVm(), res).print_rec(std::cerr);
|
||||
CHECK(vm::std_boc_deserialize(wallet_query).move_as_ok()->get_hash() == res->get_hash());
|
||||
}
|
||||
|
||||
TEST(Tonlib, RestrictedWallet) {
|
||||
//auto source_lookup = fift::create_mem_source_lookup(load_source("smartcont/new-restricted-wallet2.fif")).move_as_ok();
|
||||
//source_lookup
|
||||
|
@ -585,74 +405,130 @@ TEST(Tonlib, RestrictedWallet3) {
|
|||
CHECK(wallet->get_seqno().move_as_ok() == 2);
|
||||
}
|
||||
|
||||
class SimpleWallet : public ton::SmartContract {
|
||||
template <class T>
|
||||
void check_wallet_seqno(td::Ref<T> wallet, td::uint32 seqno) {
|
||||
ASSERT_EQ(seqno, wallet->get_seqno().ok());
|
||||
}
|
||||
void check_wallet_seqno(td::Ref<ton::HighloadWalletV2> wallet, td::uint32 seqno) {
|
||||
}
|
||||
void check_wallet_seqno(td::Ref<ton::WalletInterface> wallet, td::uint32 seqno) {
|
||||
}
|
||||
template <class T>
|
||||
void check_wallet_state(td::Ref<T> wallet, td::uint32 seqno, td::uint32 wallet_id, td::Slice public_key) {
|
||||
ASSERT_EQ(wallet_id, wallet->get_wallet_id().ok());
|
||||
ASSERT_EQ(public_key, wallet->get_public_key().ok().as_octet_string().as_slice());
|
||||
check_wallet_seqno(wallet, seqno);
|
||||
}
|
||||
|
||||
struct CreatedWallet {
|
||||
td::optional<td::Ed25519::PrivateKey> priv_key;
|
||||
block::StdAddress address;
|
||||
td::Ref<ton::WalletInterface> wallet;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
class InitWallet {
|
||||
public:
|
||||
SimpleWallet(State state) : SmartContract(std::move(state)) {
|
||||
}
|
||||
CreatedWallet operator()(int revision) const {
|
||||
ton::WalletInterface::DefaultInitData init_data;
|
||||
auto priv_key = td::Ed25519::generate_private_key().move_as_ok();
|
||||
auto pub_key = priv_key.get_public_key().move_as_ok();
|
||||
|
||||
const State& get_state() const {
|
||||
return state_;
|
||||
}
|
||||
SimpleWallet* make_copy() const override {
|
||||
return new SimpleWallet{state_};
|
||||
}
|
||||
init_data.seqno = 0;
|
||||
init_data.wallet_id = 123;
|
||||
init_data.public_key = pub_key.as_octet_string();
|
||||
|
||||
static td::Ref<SimpleWallet> create_empty() {
|
||||
return td::Ref<SimpleWallet>(true,
|
||||
State{ton::SmartContractCode::get_code(ton::SmartContractCode::WalletV1Ext), {}});
|
||||
}
|
||||
static td::Ref<SimpleWallet> create(td::Ref<vm::Cell> data) {
|
||||
return td::Ref<SimpleWallet>(
|
||||
true, State{ton::SmartContractCode::get_code(ton::SmartContractCode::WalletV1Ext), std::move(data)});
|
||||
}
|
||||
static td::Ref<SimpleWallet> create_fast(td::Ref<vm::Cell> data) {
|
||||
return td::Ref<SimpleWallet>(
|
||||
true, State{ton::SmartContractCode::get_code(ton::SmartContractCode::WalletV1), std::move(data)});
|
||||
}
|
||||
auto wallet = T::create(init_data, revision);
|
||||
auto address = wallet->get_address();
|
||||
check_wallet_state(wallet, 0, 123, init_data.public_key);
|
||||
CHECK(wallet.write().send_external_message(wallet->get_init_message(priv_key).move_as_ok()).success);
|
||||
|
||||
td::int32 seqno() const {
|
||||
auto res = run_get_method("seqno");
|
||||
return res.stack.write().pop_smallint_range(1000000000);
|
||||
}
|
||||
|
||||
td::Ref<vm::Cell> create_init_state(td::Slice public_key) const {
|
||||
td::RefInt256 pk{true};
|
||||
pk.write().import_bytes(public_key.ubegin(), public_key.size(), false);
|
||||
auto res = run_get_method("create_init_state", {pk});
|
||||
return res.stack.write().pop_cell();
|
||||
}
|
||||
|
||||
td::Ref<vm::Cell> prepare_send_message(td::Ref<vm::Cell> msg, td::int8 mode = 3) const {
|
||||
auto res = run_get_method("prepare_send_message", {td::make_refint(mode), msg});
|
||||
return res.stack.write().pop_cell();
|
||||
}
|
||||
|
||||
static td::Ref<vm::Cell> sign_message(vm::Ref<vm::Cell> body, const td::Ed25519::PrivateKey& pk) {
|
||||
auto signature = pk.sign(body->get_hash().as_slice()).move_as_ok();
|
||||
return vm::CellBuilder().store_bytes(signature.as_slice()).append_cellslice(vm::load_cell_slice(body)).finalize();
|
||||
CreatedWallet res;
|
||||
res.wallet = std::move(wallet);
|
||||
res.address = std::move(address);
|
||||
res.priv_key = std::move(priv_key);
|
||||
return res;
|
||||
}
|
||||
};
|
||||
|
||||
TEST(Smartcon, Simple) {
|
||||
auto private_key = td::Ed25519::generate_private_key().move_as_ok();
|
||||
auto public_key = private_key.get_public_key().move_as_ok().as_octet_string();
|
||||
template <>
|
||||
CreatedWallet InitWallet<ton::RestrictedWallet>::operator()(int revision) const {
|
||||
auto init_priv_key = td::Ed25519::generate_private_key().move_as_ok();
|
||||
auto init_pub_key = init_priv_key.get_public_key().move_as_ok();
|
||||
auto priv_key = td::Ed25519::generate_private_key().move_as_ok();
|
||||
auto pub_key = priv_key.get_public_key().move_as_ok();
|
||||
|
||||
auto w_lib = SimpleWallet::create_empty();
|
||||
auto init_data = w_lib->create_init_state(public_key);
|
||||
ton::RestrictedWallet::InitData init_data;
|
||||
init_data.init_key = init_pub_key.as_octet_string();
|
||||
init_data.main_key = pub_key.as_octet_string();
|
||||
init_data.wallet_id = 123;
|
||||
auto wallet = ton::RestrictedWallet::create(init_data, 1);
|
||||
check_wallet_state(wallet, 0, 123, init_data.init_key);
|
||||
|
||||
auto w = SimpleWallet::create(init_data);
|
||||
LOG(ERROR) << w->code_size();
|
||||
auto fw = SimpleWallet::create_fast(init_data);
|
||||
LOG(ERROR) << fw->code_size();
|
||||
LOG(ERROR) << w->seqno();
|
||||
auto address = wallet->get_address();
|
||||
|
||||
for (int i = 0; i < 20; i++) {
|
||||
auto msg = w->sign_message(w->prepare_send_message(vm::CellBuilder().finalize()), private_key);
|
||||
w.write().send_external_message(msg);
|
||||
fw.write().send_external_message(msg);
|
||||
td::uint64 x = 100 * 1000000000ull;
|
||||
ton::RestrictedWallet::Config config;
|
||||
config.start_at = 1;
|
||||
config.limits = {{-32768, x}, {92, x * 3 / 4}, {183, x * 1 / 2}, {366, x * 1 / 4}, {548, 0}};
|
||||
CHECK(wallet.write().send_external_message(wallet->get_init_message(init_priv_key, 10, config).move_as_ok()).success);
|
||||
CHECK(wallet->get_seqno().move_as_ok() == 1);
|
||||
|
||||
CreatedWallet res;
|
||||
res.wallet = std::move(wallet);
|
||||
res.address = std::move(address);
|
||||
res.priv_key = std::move(priv_key);
|
||||
return res;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void do_test_wallet(int revision) {
|
||||
auto res = InitWallet<T>()(revision);
|
||||
auto priv_key = res.priv_key.unwrap();
|
||||
auto address = std::move(res.address);
|
||||
auto iwallet = std::move(res.wallet);
|
||||
auto public_key = priv_key.get_public_key().move_as_ok().as_octet_string();
|
||||
;
|
||||
check_wallet_state(iwallet, 1, 123, public_key);
|
||||
|
||||
// lets send a lot of messages
|
||||
std::vector<ton::WalletInterface::Gift> gifts;
|
||||
for (size_t i = 0; i < iwallet->get_max_gifts_size(); i++) {
|
||||
ton::WalletInterface::Gift gift;
|
||||
gift.gramms = 1;
|
||||
gift.destination = address;
|
||||
gift.message = std::string(iwallet->get_max_message_size(), 'z');
|
||||
gifts.push_back(gift);
|
||||
}
|
||||
ASSERT_EQ(20, w->seqno());
|
||||
CHECK(w->get_state().data->get_hash() == fw->get_state().data->get_hash());
|
||||
|
||||
td::uint32 valid_until = 10000;
|
||||
auto send_gifts = iwallet->make_a_gift_message(priv_key, valid_until, gifts).move_as_ok();
|
||||
|
||||
{
|
||||
auto cwallet = iwallet;
|
||||
CHECK(!cwallet.write()
|
||||
.send_external_message(send_gifts, ton::SmartContract::Args().set_now(valid_until + 1))
|
||||
.success);
|
||||
}
|
||||
//TODO: make wallet work (or not) with now == valid_until
|
||||
auto ans = iwallet.write().send_external_message(send_gifts, ton::SmartContract::Args().set_now(valid_until - 1));
|
||||
CHECK(ans.success);
|
||||
CHECK((int)gifts.size() <= ans.output_actions_count(ans.actions));
|
||||
check_wallet_state(iwallet, 2, 123, public_key);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void do_test_wallet() {
|
||||
for (auto revision : T::get_revisions()) {
|
||||
do_test_wallet<T>(revision);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Tonlib, Wallet) {
|
||||
do_test_wallet<ton::WalletV3>();
|
||||
do_test_wallet<ton::HighloadWallet>();
|
||||
do_test_wallet<ton::HighloadWalletV2>();
|
||||
do_test_wallet<ton::RestrictedWallet>();
|
||||
}
|
||||
|
||||
namespace std { // ouch
|
||||
|
@ -1157,7 +1033,7 @@ class CheckedDns {
|
|||
}
|
||||
}
|
||||
void update(const Action& action) {
|
||||
return update(td::Span<Action>(&action, 1));
|
||||
return update(td::span_one(action));
|
||||
}
|
||||
|
||||
std::vector<Entry> resolve(td::Slice name, td::int16 category) {
|
||||
|
@ -1337,16 +1213,13 @@ TEST(Smartcont, DnsManual) {
|
|||
{
|
||||
CheckedDns::Action e[4] = {CheckedDns::Action{"", 0, ""}, CheckedDns::Action{"a.b.c", 1, "hello"},
|
||||
CheckedDns::Action{"a.b.c", 2, "world"}, CheckedDns::Action{"x.y.z", 3, "abc"}};
|
||||
dns.update(td::Span<CheckedDns::Action>(e, 4));
|
||||
dns.update(td::span(e, 4));
|
||||
}
|
||||
dns.resolve("a.b.c", 1);
|
||||
dns.resolve("a.b.c", 2);
|
||||
dns.resolve("x.y.z", 3);
|
||||
|
||||
{
|
||||
CheckedDns::Action e[1] = {CheckedDns::Action{"x.y.z", 0, ""}};
|
||||
dns.update(td::Span<CheckedDns::Action>(e, 1));
|
||||
}
|
||||
dns.update(td::span_one(CheckedDns::Action{"x.y.z", 0, ""}));
|
||||
|
||||
dns.resolve("a.b.c", 1);
|
||||
dns.resolve("a.b.c", 2);
|
||||
|
@ -1355,7 +1228,7 @@ TEST(Smartcont, DnsManual) {
|
|||
{
|
||||
CheckedDns::Action e[3] = {CheckedDns::Action{"x.y.z", 0, ""}, CheckedDns::Action{"x.y.z", 1, "xxx"},
|
||||
CheckedDns::Action{"x.y.z", 2, "yyy"}};
|
||||
dns.update(td::Span<CheckedDns::Action>(e, 3));
|
||||
dns.update(td::span(e, 3));
|
||||
}
|
||||
dns.resolve("a.b.c", 1);
|
||||
dns.resolve("a.b.c", 2);
|
||||
|
|
|
@ -48,7 +48,7 @@ Cell::LoadedCell load_cell_nothrow(const Ref<Cell>& ref) {
|
|||
auto res = ref->load_cell();
|
||||
if (res.is_ok()) {
|
||||
auto ld = res.move_as_ok();
|
||||
CHECK(ld.virt.get_virtualization() == 0 || ld.data_cell->special_type() != Cell::SpecialType::PrunnedBranch);
|
||||
//CHECK(ld.virt.get_virtualization() == 0 || ld.data_cell->special_type() != Cell::SpecialType::PrunnedBranch);
|
||||
return ld;
|
||||
}
|
||||
return {};
|
||||
|
@ -58,7 +58,7 @@ Cell::LoadedCell load_cell_nothrow(const Ref<Cell>& ref, int mode) {
|
|||
auto res = ref->load_cell();
|
||||
if (res.is_ok()) {
|
||||
auto ld = res.move_as_ok();
|
||||
CHECK(ld.virt.get_virtualization() == 0 || ld.data_cell->special_type() != Cell::SpecialType::PrunnedBranch);
|
||||
//CHECK(ld.virt.get_virtualization() == 0 || ld.data_cell->special_type() != Cell::SpecialType::PrunnedBranch);
|
||||
if ((mode >> (ld.data_cell->is_special() ? 1 : 0)) & 1) {
|
||||
return ld;
|
||||
}
|
||||
|
|
|
@ -79,6 +79,19 @@ td::Result<td::string> CellString::load(CellSlice &cs, unsigned int top_bits) {
|
|||
CHECK(to.offs == (int)size);
|
||||
return res;
|
||||
}
|
||||
td::Result<td::Ref<vm::Cell>> CellString::create(td::Slice slice, unsigned int top_bits) {
|
||||
vm::CellBuilder cb;
|
||||
TRY_STATUS(store(cb, slice, top_bits));
|
||||
return cb.finalize();
|
||||
}
|
||||
bool CellString::fetch_to(CellSlice &cs, std::string &res, unsigned int top_bits) {
|
||||
auto r_str = load(cs, top_bits);
|
||||
if (r_str.is_error()) {
|
||||
return false;
|
||||
}
|
||||
res = r_str.move_as_ok();
|
||||
return true;
|
||||
}
|
||||
|
||||
td::Status CellText::store(CellBuilder &cb, td::Slice slice, unsigned int top_bits) {
|
||||
td::uint32 size = td::narrow_cast<td::uint32>(slice.size() * 8);
|
||||
|
@ -154,4 +167,17 @@ td::Result<td::string> CellText::load(CellSlice &cs) {
|
|||
CHECK(to.offs == (int)size);
|
||||
return res;
|
||||
}
|
||||
td::Result<td::Ref<vm::Cell>> CellText::create(td::Slice slice, unsigned int top_bits) {
|
||||
vm::CellBuilder cb;
|
||||
TRY_STATUS(store(cb, slice, top_bits));
|
||||
return cb.finalize();
|
||||
}
|
||||
bool CellText::fetch_to(CellSlice &cs, std::string &res) {
|
||||
auto r_str = load(cs);
|
||||
if (r_str.is_error()) {
|
||||
return false;
|
||||
}
|
||||
res = r_str.move_as_ok();
|
||||
return true;
|
||||
}
|
||||
} // namespace vm
|
||||
|
|
|
@ -31,11 +31,8 @@ class CellString {
|
|||
static td::Status store(CellBuilder &cb, td::Slice slice, unsigned int top_bits = Cell::max_bits);
|
||||
static td::Status store(CellBuilder &cb, td::BitSlice slice, unsigned int top_bits = Cell::max_bits);
|
||||
static td::Result<td::string> load(CellSlice &cs, unsigned int top_bits = Cell::max_bits);
|
||||
static td::Result<td::Ref<vm::Cell>> create(td::Slice slice, unsigned int top_bits = Cell::max_bits) {
|
||||
vm::CellBuilder cb;
|
||||
TRY_STATUS(store(cb, slice, top_bits));
|
||||
return cb.finalize();
|
||||
}
|
||||
static td::Result<td::Ref<vm::Cell>> create(td::Slice slice, unsigned int top_bits = Cell::max_bits);
|
||||
static bool fetch_to(CellSlice &cs, std::string &res, unsigned int top_bits = Cell::max_bits);
|
||||
|
||||
private:
|
||||
template <class F>
|
||||
|
@ -50,11 +47,8 @@ class CellText {
|
|||
static td::Status store(CellBuilder &cb, td::Slice slice, unsigned int top_bits = Cell::max_bits);
|
||||
static td::Status store(CellBuilder &cb, td::BitSlice slice, unsigned int top_bits = Cell::max_bits);
|
||||
static td::Result<td::string> load(CellSlice &cs);
|
||||
static td::Result<td::Ref<vm::Cell>> create(td::Slice slice, unsigned int top_bits = Cell::max_bits) {
|
||||
vm::CellBuilder cb;
|
||||
TRY_STATUS(store(cb, slice, top_bits));
|
||||
return cb.finalize();
|
||||
}
|
||||
static td::Result<td::Ref<vm::Cell>> create(td::Slice slice, unsigned int top_bits = Cell::max_bits);
|
||||
static bool fetch_to(CellSlice &cs, std::string &res);
|
||||
|
||||
private:
|
||||
template <class F>
|
||||
|
|
|
@ -187,9 +187,9 @@ class StaticBagOfCellsDbBaselineImpl : public StaticBagOfCellsDb {
|
|||
}
|
||||
};
|
||||
|
||||
td::Result<std::shared_ptr<StaticBagOfCellsDb>> StaticBagOfCellsDbBaseline::create(std::unique_ptr<BlobView> data) {
|
||||
std::string buf(data->size(), '\0');
|
||||
TRY_RESULT(slice, data->view(buf, 0));
|
||||
td::Result<std::shared_ptr<StaticBagOfCellsDb>> StaticBagOfCellsDbBaseline::create(td::BlobView data) {
|
||||
std::string buf(data.size(), '\0');
|
||||
TRY_RESULT(slice, data.view(buf, 0));
|
||||
return create(slice);
|
||||
}
|
||||
|
||||
|
@ -211,7 +211,7 @@ td::Result<std::shared_ptr<StaticBagOfCellsDb>> StaticBagOfCellsDbBaseline::crea
|
|||
//
|
||||
class StaticBagOfCellsDbLazyImpl : public StaticBagOfCellsDb {
|
||||
public:
|
||||
explicit StaticBagOfCellsDbLazyImpl(std::unique_ptr<BlobView> data, StaticBagOfCellsDbLazy::Options options)
|
||||
explicit StaticBagOfCellsDbLazyImpl(td::BlobView data, StaticBagOfCellsDbLazy::Options options)
|
||||
: data_(std::move(data)), options_(std::move(options)) {
|
||||
get_thread_safe_counter().add(1);
|
||||
}
|
||||
|
@ -240,7 +240,7 @@ class StaticBagOfCellsDbLazyImpl : public StaticBagOfCellsDb {
|
|||
|
||||
private:
|
||||
std::atomic<bool> should_cache_cells_{true};
|
||||
std::unique_ptr<BlobView> data_;
|
||||
td::BlobView data_;
|
||||
StaticBagOfCellsDbLazy::Options options_;
|
||||
bool has_info_{false};
|
||||
BagOfCells::Info info_;
|
||||
|
@ -313,8 +313,8 @@ class StaticBagOfCellsDbLazyImpl : public StaticBagOfCellsDb {
|
|||
char arr[8];
|
||||
td::RwMutex::ReadLock guard;
|
||||
if (info_.has_index) {
|
||||
TRY_RESULT(new_offset_view, data_->view(td::MutableSlice(arr, info_.offset_byte_size),
|
||||
info_.index_offset + idx * info_.offset_byte_size));
|
||||
TRY_RESULT(new_offset_view, data_.view(td::MutableSlice(arr, info_.offset_byte_size),
|
||||
info_.index_offset + idx * info_.offset_byte_size));
|
||||
offset_view = new_offset_view;
|
||||
} else {
|
||||
guard = index_data_rw_mutex_.lock_read().move_as_ok();
|
||||
|
@ -331,8 +331,8 @@ class StaticBagOfCellsDbLazyImpl : public StaticBagOfCellsDb {
|
|||
return 0;
|
||||
}
|
||||
char arr[8];
|
||||
TRY_RESULT(idx_view, data_->view(td::MutableSlice(arr, info_.ref_byte_size),
|
||||
info_.roots_offset + root_i * info_.ref_byte_size));
|
||||
TRY_RESULT(idx_view, data_.view(td::MutableSlice(arr, info_.ref_byte_size),
|
||||
info_.roots_offset + root_i * info_.ref_byte_size));
|
||||
CHECK(idx_view.size() == (size_t)info_.ref_byte_size);
|
||||
return info_.read_ref(idx_view.ubegin());
|
||||
}
|
||||
|
@ -369,17 +369,17 @@ class StaticBagOfCellsDbLazyImpl : public StaticBagOfCellsDb {
|
|||
return td::Status::OK();
|
||||
}
|
||||
std::string header(1000, '\0');
|
||||
TRY_RESULT(header_view, data_->view(td::MutableSlice(header).truncate(data_->size()), 0))
|
||||
TRY_RESULT(header_view, data_.view(td::MutableSlice(header).truncate(data_.size()), 0))
|
||||
auto parse_res = info_.parse_serialized_header(header_view);
|
||||
if (parse_res <= 0) {
|
||||
return td::Status::Error("bag-of-cell error: failed to read header");
|
||||
}
|
||||
if (info_.total_size < data_->size()) {
|
||||
if (info_.total_size < data_.size()) {
|
||||
return td::Status::Error("bag-of-cell error: not enough data");
|
||||
}
|
||||
if (options_.check_crc32c && info_.has_crc32c) {
|
||||
std::string buf(td::narrow_cast<std::size_t>(info_.total_size), '\0');
|
||||
TRY_RESULT(data, data_->view(td::MutableSlice(buf), 0));
|
||||
TRY_RESULT(data, data_.view(td::MutableSlice(buf), 0));
|
||||
unsigned crc_computed = td::crc32c(td::Slice{data.ubegin(), data.uend() - 4});
|
||||
unsigned crc_stored = td::as<unsigned>(data.uend() - 4);
|
||||
if (crc_computed != crc_stored) {
|
||||
|
@ -407,8 +407,8 @@ class StaticBagOfCellsDbLazyImpl : public StaticBagOfCellsDb {
|
|||
auto buf_slice = td::MutableSlice(buf.data(), buf.size());
|
||||
for (; index_i_ <= idx; index_i_++) {
|
||||
auto offset = td::narrow_cast<size_t>(info_.data_offset + index_offset_);
|
||||
CHECK(data_->size() >= offset);
|
||||
TRY_RESULT(cell, data_->view(buf_slice.copy().truncate(data_->size() - offset), offset));
|
||||
CHECK(data_.size() >= offset);
|
||||
TRY_RESULT(cell, data_.view(buf_slice.copy().truncate(data_.size() - offset), offset));
|
||||
CellSerializationInfo cell_info;
|
||||
TRY_STATUS(cell_info.init(cell, info_.ref_byte_size));
|
||||
index_offset_ += cell_info.end_offset;
|
||||
|
@ -455,7 +455,7 @@ class StaticBagOfCellsDbLazyImpl : public StaticBagOfCellsDb {
|
|||
|
||||
TRY_RESULT(cell_location, get_cell_location(idx));
|
||||
auto buf = alloc(cell_location.end - cell_location.begin);
|
||||
TRY_RESULT(cell_slice, data_->view(buf.as_slice(), cell_location.begin));
|
||||
TRY_RESULT(cell_slice, data_.view(buf.as_slice(), cell_location.begin));
|
||||
TRY_RESULT(res, deserialize_any_cell(idx, cell_slice, cell_location.should_cache));
|
||||
return std::move(res);
|
||||
}
|
||||
|
@ -470,7 +470,7 @@ class StaticBagOfCellsDbLazyImpl : public StaticBagOfCellsDb {
|
|||
|
||||
TRY_RESULT(cell_location, get_cell_location(idx));
|
||||
auto buf = alloc(cell_location.end - cell_location.begin);
|
||||
TRY_RESULT(cell_slice, data_->view(buf.as_slice(), cell_location.begin));
|
||||
TRY_RESULT(cell_slice, data_.view(buf.as_slice(), cell_location.begin));
|
||||
TRY_RESULT(res, deserialize_data_cell(idx, cell_slice, cell_location.should_cache));
|
||||
return std::move(res);
|
||||
}
|
||||
|
@ -528,18 +528,17 @@ class StaticBagOfCellsDbLazyImpl : public StaticBagOfCellsDb {
|
|||
}
|
||||
};
|
||||
|
||||
td::Result<std::shared_ptr<StaticBagOfCellsDb>> StaticBagOfCellsDbLazy::create(std::unique_ptr<BlobView> data,
|
||||
Options options) {
|
||||
td::Result<std::shared_ptr<StaticBagOfCellsDb>> StaticBagOfCellsDbLazy::create(td::BlobView data, Options options) {
|
||||
return std::make_shared<StaticBagOfCellsDbLazyImpl>(std::move(data), std::move(options));
|
||||
}
|
||||
|
||||
td::Result<std::shared_ptr<StaticBagOfCellsDb>> StaticBagOfCellsDbLazy::create(td::BufferSlice data, Options options) {
|
||||
return std::make_shared<StaticBagOfCellsDbLazyImpl>(vm::BufferSliceBlobView::create(std::move(data)),
|
||||
return std::make_shared<StaticBagOfCellsDbLazyImpl>(td::BufferSliceBlobView::create(std::move(data)),
|
||||
std::move(options));
|
||||
}
|
||||
|
||||
td::Result<std::shared_ptr<StaticBagOfCellsDb>> StaticBagOfCellsDbLazy::create(std::string data, Options options) {
|
||||
return create(BufferSliceBlobView::create(td::BufferSlice(data)), std::move(options));
|
||||
return create(td::BufferSliceBlobView::create(td::BufferSlice(data)), std::move(options));
|
||||
}
|
||||
|
||||
} // namespace vm
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "vm/cells.h"
|
||||
#include "vm/db/BlobView.h"
|
||||
#include "td/db/utils/BlobView.h"
|
||||
|
||||
#include "td/utils/Status.h"
|
||||
|
||||
|
@ -41,7 +41,7 @@ class StaticBagOfCellsDb : public std::enable_shared_from_this<StaticBagOfCellsD
|
|||
|
||||
class StaticBagOfCellsDbBaseline {
|
||||
public:
|
||||
static td::Result<std::shared_ptr<StaticBagOfCellsDb>> create(std::unique_ptr<BlobView> data);
|
||||
static td::Result<std::shared_ptr<StaticBagOfCellsDb>> create(td::BlobView data);
|
||||
static td::Result<std::shared_ptr<StaticBagOfCellsDb>> create(td::Slice data);
|
||||
};
|
||||
|
||||
|
@ -52,7 +52,7 @@ class StaticBagOfCellsDbLazy {
|
|||
}
|
||||
bool check_crc32c{false};
|
||||
};
|
||||
static td::Result<std::shared_ptr<StaticBagOfCellsDb>> create(std::unique_ptr<BlobView> data, Options options = {});
|
||||
static td::Result<std::shared_ptr<StaticBagOfCellsDb>> create(td::BlobView data, Options options = {});
|
||||
static td::Result<std::shared_ptr<StaticBagOfCellsDb>> create(td::BufferSlice data, Options options = {});
|
||||
static td::Result<std::shared_ptr<StaticBagOfCellsDb>> create(std::string data, Options options = {});
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue