mirror of
https://github.com/ton-blockchain/ton
synced 2025-03-09 15:40:10 +00:00
updated pow-miner + small bugfix
This commit is contained in:
parent
f064b1047a
commit
dab7ee3f97
16 changed files with 344 additions and 25 deletions
|
@ -69,7 +69,7 @@ if (TONLIB_ENABLE_JNI AND NOT ANDROID) # jni is available by default on Android
|
|||
endif()
|
||||
|
||||
add_executable(tonlib-cli tonlib/tonlib-cli.cpp)
|
||||
target_link_libraries(tonlib-cli tonlib tdactor tdutils terminal)
|
||||
target_link_libraries(tonlib-cli tonlib tdactor tdutils terminal pow-miner-lib)
|
||||
|
||||
if (NOT CMAKE_CROSSCOMPILING)
|
||||
if (TONLIB_ENABLE_JNI)
|
||||
|
|
|
@ -77,6 +77,16 @@ class TonlibClient : public td::actor::Actor {
|
|||
std::string rwallet_init_public_key;
|
||||
};
|
||||
|
||||
template <class T, class P>
|
||||
void make_request(T&& request, P&& promise) {
|
||||
td::Promise<typename std::decay_t<T>::ReturnType> new_promise = std::move(promise);
|
||||
|
||||
auto status = do_request(std::forward<T>(request), std::move(new_promise));
|
||||
if (status.is_error()) {
|
||||
new_promise.operator()(std::move(status));
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
enum class State { Uninited, Running, Closed } state_ = State::Uninited;
|
||||
td::unique_ptr<TonlibCallback> callback_;
|
||||
|
@ -203,15 +213,6 @@ class TonlibClient : public td::actor::Actor {
|
|||
|
||||
void make_any_request(tonlib_api::Function& function, QueryContext query_context,
|
||||
td::Promise<tonlib_api::object_ptr<tonlib_api::Object>>&& promise);
|
||||
template <class T, class P>
|
||||
void make_request(T&& request, P&& promise) {
|
||||
td::Promise<typename std::decay_t<T>::ReturnType> new_promise = std::move(promise);
|
||||
|
||||
auto status = do_request(std::forward<T>(request), std::move(new_promise));
|
||||
if (status.is_error()) {
|
||||
new_promise.operator()(std::move(status));
|
||||
}
|
||||
}
|
||||
|
||||
td::Result<FullConfig> validate_config(tonlib_api::object_ptr<tonlib_api::config> config);
|
||||
void set_config(FullConfig config);
|
||||
|
|
|
@ -25,8 +25,14 @@
|
|||
|
||||
Copyright 2019-2020 Telegram Systems LLP
|
||||
*/
|
||||
#include "auto/tl/tonlib_api.h"
|
||||
#include "block/block.h"
|
||||
#include "common/bigint.hpp"
|
||||
#include "common/refint.h"
|
||||
#include "td/actor/actor.h"
|
||||
|
||||
#include "td/actor/common.h"
|
||||
#include "td/utils/Time.h"
|
||||
#include "td/utils/filesystem.h"
|
||||
#include "td/utils/OptionParser.h"
|
||||
#include "td/utils/overloaded.h"
|
||||
|
@ -48,6 +54,10 @@
|
|||
|
||||
#include "auto/tl/tonlib_api.hpp"
|
||||
|
||||
#include "crypto/util/Miner.h"
|
||||
#include "vm/boc.h"
|
||||
#include "vm/cells/CellBuilder.h"
|
||||
|
||||
#include <cinttypes>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
|
@ -106,6 +116,9 @@ td::Result<Grams> parse_grams(td::Slice grams) {
|
|||
return Grams{value};
|
||||
}
|
||||
|
||||
// Temporary hack
|
||||
td::actor::Scheduler* global_scheduler_{nullptr};
|
||||
|
||||
class TonlibCli : public td::actor::Actor {
|
||||
public:
|
||||
struct Options {
|
||||
|
@ -153,6 +166,9 @@ class TonlibCli : public td::actor::Actor {
|
|||
make_object<tonlib_api::key>(public_key, secret.copy()), td::SecureString(password))
|
||||
: nullptr;
|
||||
}
|
||||
auto tonlib_api() const {
|
||||
return make_object<tonlib_api::accountAddress>(address->account_address_);
|
||||
}
|
||||
};
|
||||
|
||||
std::map<std::uint64_t, td::Promise<tonlib_api::object_ptr<tonlib_api::Object>>> query_handlers_;
|
||||
|
@ -179,7 +195,7 @@ class TonlibCli : public td::actor::Actor {
|
|||
};
|
||||
if (!options_.one_shot) {
|
||||
ref_cnt_++;
|
||||
io_ = td::TerminalIO::create("> ", options_.enable_readline, std::make_unique<Cb>(actor_shared(this)));
|
||||
io_ = td::TerminalIO::create("> ", options_.enable_readline, false, std::make_unique<Cb>(actor_shared(this)));
|
||||
td::actor::send_closure(io_, &td::TerminalIO::set_log_interface);
|
||||
}
|
||||
|
||||
|
@ -317,6 +333,11 @@ class TonlibCli : public td::actor::Actor {
|
|||
td::TerminalIO::out() << "rwallet address <key_id> <public_key>\n";
|
||||
td::TerminalIO::out() << "rwallet init <key_id> <public_key> <start_at> [<seconds>:<value> ...]\n";
|
||||
}
|
||||
void pminer_help() {
|
||||
td::TerminalIO::out() << "pminer help\n";
|
||||
td::TerminalIO::out() << "pminer start <giver_addess> <my_address>\n";
|
||||
td::TerminalIO::out() << "pminer stop\n";
|
||||
}
|
||||
|
||||
void parse_line(td::BufferSlice line) {
|
||||
if (is_closing_) {
|
||||
|
@ -374,6 +395,7 @@ class TonlibCli : public td::actor::Actor {
|
|||
dns_help();
|
||||
pchan_help();
|
||||
rwallet_help();
|
||||
pminer_help();
|
||||
|
||||
td::TerminalIO::out()
|
||||
<< "blockmode auto|manual\tWith auto mode, all queries will be executed with respect to the latest block. "
|
||||
|
@ -477,6 +499,8 @@ class TonlibCli : public td::actor::Actor {
|
|||
run_pchan_cmd(parser, std::move(cmd_promise));
|
||||
} else if (cmd == "rwallet") {
|
||||
run_rwallet_cmd(parser, std::move(cmd_promise));
|
||||
} else if (cmd == "pminer") {
|
||||
run_pminer_cmd(parser, std::move(cmd_promise));
|
||||
} else if (cmd == "gethistory") {
|
||||
get_history(parser.read_word(), std::move(cmd_promise));
|
||||
} else if (cmd == "guessrevision") {
|
||||
|
@ -556,6 +580,216 @@ class TonlibCli : public td::actor::Actor {
|
|||
promise.set_error(td::Status::Error("Unknown command"));
|
||||
}
|
||||
|
||||
class PowMiner : public td::actor::Actor {
|
||||
public:
|
||||
class Callback {
|
||||
public:
|
||||
};
|
||||
struct Options {
|
||||
Address giver_address;
|
||||
Address my_address;
|
||||
};
|
||||
|
||||
PowMiner(Options options, td::actor::ActorId<tonlib::TonlibClient> client)
|
||||
: options_(std::move(options)), client_(std::move(client)) {
|
||||
}
|
||||
|
||||
private:
|
||||
Options options_;
|
||||
td::actor::ActorId<tonlib::TonlibClient> client_;
|
||||
|
||||
td::optional<ton::Miner::Options> miner_options_;
|
||||
static constexpr double QUERY_EACH = 5.0;
|
||||
td::Timestamp next_options_query_at_;
|
||||
|
||||
bool need_run_miners_{false};
|
||||
td::CancellationTokenSource source_;
|
||||
ton::Miner::Options miner_options_copy_;
|
||||
std::size_t threads_alive_{0};
|
||||
std::vector<td::thread> threads_;
|
||||
|
||||
bool close_flag_{false};
|
||||
|
||||
template <class QueryT>
|
||||
void send_query(QueryT query, td::Promise<typename QueryT::ReturnType> promise) {
|
||||
td::actor::send_lambda(client_,
|
||||
[self = client_, query = std::move(query), promise = std::move(promise)]() mutable {
|
||||
self.get_actor_unsafe().make_request(std::move(query), std::move(promise));
|
||||
});
|
||||
}
|
||||
|
||||
void start_up() override {
|
||||
next_options_query_at_ = td::Timestamp::now();
|
||||
loop();
|
||||
}
|
||||
void hangup() override {
|
||||
close_flag_ = true;
|
||||
source_.cancel();
|
||||
try_stop();
|
||||
}
|
||||
void try_stop() {
|
||||
if (threads_alive_ == 0) {
|
||||
td::TerminalIO::out() << "pminer: stopped\n";
|
||||
stop();
|
||||
}
|
||||
}
|
||||
|
||||
void loop() override {
|
||||
if (close_flag_) {
|
||||
try_stop();
|
||||
return;
|
||||
}
|
||||
if (next_options_query_at_ && next_options_query_at_.is_in_past()) {
|
||||
send_query(tonlib_api::smc_load(options_.giver_address.tonlib_api()),
|
||||
promise_send_closure(td::actor::actor_id(this), &PowMiner::with_giver_state));
|
||||
next_options_query_at_ = {};
|
||||
}
|
||||
|
||||
if (miner_options_ && threads_.empty() && need_run_miners_) {
|
||||
td::TerminalIO::out() << "pminer: start workers\n";
|
||||
need_run_miners_ = false;
|
||||
miner_options_copy_ = miner_options_.value();
|
||||
miner_options_copy_.token_ = source_.get_cancellation_token();
|
||||
auto n = td::thread::hardware_concurrency();
|
||||
threads_alive_ = n;
|
||||
for (td::uint32 i = 0; i < n; i++) {
|
||||
threads_.emplace_back([this, actor_id = actor_id(this)] {
|
||||
auto res = ton::Miner::run(miner_options_copy_);
|
||||
global_scheduler_->run_in_context_external(
|
||||
[&] { send_closure(actor_id, &PowMiner::got_answer, std::move(res)); });
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
alarm_timestamp().relax(next_options_query_at_);
|
||||
}
|
||||
|
||||
void got_answer(td::optional<std::string> answer) {
|
||||
source_.cancel();
|
||||
if (--threads_alive_ == 0) {
|
||||
threads_.clear();
|
||||
}
|
||||
if (answer) {
|
||||
td::TerminalIO::out() << "pminer: got some result - sending query to the giver\n";
|
||||
vm::CellBuilder cb;
|
||||
cb.store_bytes(answer.unwrap());
|
||||
send_query(tonlib_api::raw_createAndSendMessage(
|
||||
options_.giver_address.tonlib_api(), "",
|
||||
vm::std_boc_serialize(cb.finalize_novm()).move_as_ok().as_slice().str()),
|
||||
promise_send_closure(td::actor::actor_id(this), &PowMiner::on_query_sent));
|
||||
}
|
||||
loop();
|
||||
}
|
||||
|
||||
void on_query_sent(td::Result<tonlib_api::object_ptr<tonlib_api::ok>> r_ok) {
|
||||
LOG_IF(ERROR, r_ok.is_error()) << "pminer: " << r_ok.error();
|
||||
}
|
||||
|
||||
void with_giver_state(td::Result<tonlib_api::object_ptr<tonlib_api::smc_info>> r_info) {
|
||||
if (r_info.is_error()) {
|
||||
return with_giver_info(r_info.move_as_error());
|
||||
}
|
||||
send_query(tonlib_api::smc_runGetMethod(r_info.ok()->id_,
|
||||
make_object<tonlib_api::smc_methodIdName>("get_pow_params"), {}),
|
||||
promise_send_closure(td::actor::actor_id(this), &PowMiner::with_giver_info));
|
||||
}
|
||||
|
||||
void with_giver_info(td::Result<tonlib_api::object_ptr<tonlib_api::smc_runResult>> r_info) {
|
||||
auto status = do_with_giver_info(std::move(r_info));
|
||||
LOG_IF(ERROR, status.is_error()) << "pminer: " << status;
|
||||
next_options_query_at_ = td::Timestamp::in(QUERY_EACH);
|
||||
return loop();
|
||||
}
|
||||
|
||||
td::Result<td::RefInt256> to_number(const tonlib_api::object_ptr<tonlib_api::tvm_StackEntry>& entry,
|
||||
td::int32 bits) {
|
||||
if (entry->get_id() != tonlib_api::tvm_stackEntryNumber::ID) {
|
||||
return td::Status::Error("Expected stackEntryNumber");
|
||||
}
|
||||
auto& number_str = static_cast<const tonlib_api::tvm_stackEntryNumber&>(*entry.get()).number_->number_;
|
||||
auto num = td::make_refint();
|
||||
if (num.write().parse_dec(number_str.data(), (int)number_str.size()) < (int)number_str.size()) {
|
||||
return td::Status::Error("Failed to parse a number");
|
||||
}
|
||||
if (!num->unsigned_fits_bits(bits)) {
|
||||
return td::Status::Error(PSLICE() << "Number is too big " << num->to_dec_string() << " " << bits);
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
td::Status do_with_giver_info(td::Result<tonlib_api::object_ptr<tonlib_api::smc_runResult>> r_info) {
|
||||
TRY_RESULT(info, std::move(r_info));
|
||||
if (info->stack_.size() < 2) {
|
||||
return td::Status::Error("Unexpected `get_pow_params` result format");
|
||||
}
|
||||
TRY_RESULT(seed, to_number(info->stack_[0], 128));
|
||||
TRY_RESULT(complexity, to_number(info->stack_[1], 256));
|
||||
ton::Miner::Options options;
|
||||
seed->export_bytes(options.seed.data(), 16, false);
|
||||
complexity->export_bytes(options.complexity.data(), 32, false);
|
||||
|
||||
TRY_RESULT(address, block::StdAddress::parse(options_.my_address.address->account_address_));
|
||||
options.my_address = std::move(address);
|
||||
options.token_ = source_.get_cancellation_token();
|
||||
|
||||
if (miner_options_ && miner_options_.value().seed == options.seed) {
|
||||
return td::Status::OK();
|
||||
}
|
||||
|
||||
td::TerminalIO::out() << "pminer: got new options\n";
|
||||
td::BigInt256 bigpower, hrate;
|
||||
bigpower.set_pow2(256).mod_div(*complexity, hrate);
|
||||
long long hash_rate = hrate.to_long();
|
||||
td::TerminalIO::out() << "[ expected required hashes for success: " << hash_rate << " ]\n";
|
||||
miner_options_ = std::move(options);
|
||||
need_run_miners_ = true;
|
||||
source_.cancel();
|
||||
|
||||
return td::Status::OK();
|
||||
}
|
||||
};
|
||||
|
||||
td::uint64 pow_miner_id_{0};
|
||||
std::map<td::uint64, td::actor::ActorOwn<PowMiner>> pow_miners_;
|
||||
|
||||
void pminer_start(td::ConstParser& parser, td::Promise<td::Unit> promise) {
|
||||
if (!pow_miners_.empty()) {
|
||||
promise.set_error(td::Status::Error("One pminer is already running"));
|
||||
}
|
||||
TRY_RESULT_PROMISE_PREFIX(promise, giver_address, to_account_address(parser.read_word(), false), "giver address");
|
||||
TRY_RESULT_PROMISE_PREFIX(promise, my_address, to_account_address(parser.read_word(), false), "my address");
|
||||
|
||||
auto id = ++pow_miner_id_;
|
||||
|
||||
PowMiner::Options options;
|
||||
options.giver_address = std::move(giver_address);
|
||||
options.my_address = std::move(my_address);
|
||||
pow_miners_.emplace(id, td::actor::create_actor<PowMiner>("PowMiner", std::move(options), client_.get()));
|
||||
td::TerminalIO::out() << "Miner #" << id << " created";
|
||||
promise.set_value({});
|
||||
}
|
||||
|
||||
void pminer_stop(td::ConstParser& parser, td::Promise<td::Unit> promise) {
|
||||
pow_miners_.clear();
|
||||
promise.set_value({});
|
||||
}
|
||||
|
||||
void run_pminer_cmd(td::ConstParser& parser, td::Promise<td::Unit> promise) {
|
||||
auto cmd = parser.read_word();
|
||||
if (cmd == "help") {
|
||||
pminer_help();
|
||||
return promise.set_value(td::Unit());
|
||||
}
|
||||
|
||||
if (cmd == "start") {
|
||||
return pminer_start(parser, std::move(promise));
|
||||
}
|
||||
if (cmd == "stop") {
|
||||
return pminer_stop(parser, std::move(promise));
|
||||
}
|
||||
promise.set_error(td::Status::Error("Unknown command"));
|
||||
}
|
||||
|
||||
void run_pchan_cmd(td::ConstParser& parser, td::Promise<td::Unit> promise) {
|
||||
auto cmd = parser.read_word();
|
||||
if (cmd == "help") {
|
||||
|
@ -2121,6 +2355,8 @@ int main(int argc, char* argv[]) {
|
|||
}
|
||||
|
||||
td::actor::Scheduler scheduler({2});
|
||||
// temporaty hack
|
||||
global_scheduler_ = &scheduler;
|
||||
scheduler.run_in_context([&] { td::actor::create_actor<TonlibCli>("console", options).release(); });
|
||||
scheduler.run();
|
||||
return 0;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue