1
0
Fork 0
mirror of https://github.com/ton-blockchain/ton synced 2025-02-13 11:42:18 +00:00
ton/storage/storage-daemon/StorageProvider.cpp

863 lines
40 KiB
C++
Raw Normal View History

TON Storage utilities (#564) * Rename chunk to piece in MerkleTree for consistency * Refactor PeerManager * Make PeerState thread-safe * Download torrent by hash * First version of storage daemon * Download torrents partially * Improve storing and loading torrent state in DB * Rewrite MerkleTree * "Remove torrent" in storage daemon * Process errors, fix bugs in storage * Move TonlibClientWrapper from rldp-http-proxy to tonlib * Initial version of storage provider * Move interaction with contracts to smc-util * Improve TonlibClientWrapper interface * Various improvements in storage provider * Fix TorrentCreator.cpp * Improve interface for partial download * Client mode in storage-daemon * Improve interface of storage-daemon-cli * Fix calculating speed, show peers in storage-daemon * Use permanent adnl id in storage daemon * Fix sending large "storage.addUpdate" messages * Improve printing torrents in cli * Update tlo * Fix RldpSender::on_ack * Update storage provider * Add "address" parameter to get-provider-params * Allow client to close storage contract * Limit torrent description * Add more logs to storage provider * smc.forget tonlib method * Use smc.forget in storage daemon * Optimize sending messages in smc-util.cpp * Fix verbosity, remove excessive logs * Json output in storage-daemon-cli * Update storage provider contracts * Fix rldp2 acks * Change verbosity of logs in rldp2 * Update help and output of commands and in storage-daemon-cli Co-authored-by: SpyCheese <mikle98@yandex.ru>
2022-12-22 09:24:13 +00:00
/*
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/>.
*/
#include "StorageProvider.h"
#include "td/db/RocksDb.h"
#include "td/utils/JsonBuilder.h"
#include "auto/tl/ton_api_json.h"
#include "td/utils/port/path.h"
#include "block/block-auto.h"
#include "common/delay.h"
#include "td/actor/MultiPromise.h"
namespace ton {
TON Storage utilities (#564) * Rename chunk to piece in MerkleTree for consistency * Refactor PeerManager * Make PeerState thread-safe * Download torrent by hash * First version of storage daemon * Download torrents partially * Improve storing and loading torrent state in DB * Rewrite MerkleTree * "Remove torrent" in storage daemon * Process errors, fix bugs in storage * Move TonlibClientWrapper from rldp-http-proxy to tonlib * Initial version of storage provider * Move interaction with contracts to smc-util * Improve TonlibClientWrapper interface * Various improvements in storage provider * Fix TorrentCreator.cpp * Improve interface for partial download * Client mode in storage-daemon * Improve interface of storage-daemon-cli * Fix calculating speed, show peers in storage-daemon * Use permanent adnl id in storage daemon * Fix sending large "storage.addUpdate" messages * Improve printing torrents in cli * Update tlo * Fix RldpSender::on_ack * Update storage provider * Add "address" parameter to get-provider-params * Allow client to close storage contract * Limit torrent description * Add more logs to storage provider * smc.forget tonlib method * Use smc.forget in storage daemon * Optimize sending messages in smc-util.cpp * Fix verbosity, remove excessive logs * Json output in storage-daemon-cli * Update storage provider contracts * Fix rldp2 acks * Change verbosity of logs in rldp2 * Update help and output of commands and in storage-daemon-cli Co-authored-by: SpyCheese <mikle98@yandex.ru>
2022-12-22 09:24:13 +00:00
td::Result<ProviderParams> ProviderParams::create(const tl_object_ptr<ton_api::storage_daemon_provider_params>& obj) {
ProviderParams p;
p.accept_new_contracts = obj->accept_new_contracts_;
p.rate_per_mb_day = td::string_to_int256(obj->rate_per_mb_day_);
if (p.rate_per_mb_day.is_null() || p.rate_per_mb_day->sgn() < 0) {
return td::Status::Error("Invalid rate");
}
p.max_span = obj->max_span_;
p.minimal_file_size = obj->minimal_file_size_;
p.maximal_file_size = obj->maximal_file_size_;
return p;
}
tl_object_ptr<ton_api::storage_daemon_provider_params> ProviderParams::tl() const {
return create_tl_object<ton_api::storage_daemon_provider_params>(
accept_new_contracts, rate_per_mb_day->to_dec_string(), max_span, minimal_file_size, maximal_file_size);
}
bool ProviderParams::to_builder(vm::CellBuilder& b) const {
return b.store_long_bool(accept_new_contracts, 1) && store_coins(b, rate_per_mb_day) &&
b.store_long_bool(max_span, 32) && b.store_long_bool(minimal_file_size, 64) &&
b.store_long_bool(maximal_file_size, 64);
}
StorageProvider::StorageProvider(ContractAddress account_address, std::string db_root,
td::actor::ActorId<tonlib::TonlibClientWrapper> tonlib_client,
td::actor::ActorId<StorageManager> storage_manager,
td::actor::ActorId<keyring::Keyring> keyring)
: main_address_(account_address)
, db_root_(std::move(db_root))
, tonlib_client_(std::move(tonlib_client))
, storage_manager_(std::move(storage_manager))
, keyring_(std::move(keyring)) {
}
void StorageProvider::start_up() {
LOG(INFO) << "Initing storage provider, account address: " << main_address_.to_string();
td::mkdir(db_root_).ensure();
db_ = std::make_unique<td::RocksDb>(td::RocksDb::open(db_root_).move_as_ok());
auto r_state = db::db_get<ton_api::storage_provider_db_state>(
*db_, create_hash_tl_object<ton_api::storage_provider_db_key_state>(), true);
r_state.ensure();
auto state = r_state.move_as_ok();
if (state) {
last_processed_lt_ = state->last_processed_lt_;
LOG(INFO) << "Loaded storage provider state";
LOG(INFO) << "Last processed lt: " << last_processed_lt_;
}
class Callback : public FabricContractWrapper::Callback {
public:
explicit Callback(td::actor::ActorId<StorageProvider> id) : id_(std::move(id)) {
}
void on_transaction(tl_object_ptr<tonlib_api::raw_transaction> transaction) override {
td::actor::send_closure(id_, &StorageProvider::process_transaction, std::move(transaction));
}
private:
td::actor::ActorId<StorageProvider> id_;
};
contract_wrapper_ =
td::actor::create_actor<FabricContractWrapper>("ContractWrapper", main_address_, tonlib_client_, keyring_,
td::make_unique<Callback>(actor_id(this)), last_processed_lt_);
auto r_config = db::db_get<ton_api::storage_daemon_providerConfig>(
*db_, create_hash_tl_object<ton_api::storage_provider_db_key_providerConfig>(), true);
r_config.ensure();
auto config_obj = r_config.move_as_ok();
if (config_obj) {
LOG(INFO) << "Loaded config from db";
config_ = Config(config_obj);
} else {
LOG(INFO) << "Using default config";
db_store_config();
}
LOG(INFO) << "Config: max_contracts=" << config_.max_contracts << ", max_total_size=" << config_.max_total_size;
auto r_contract_list = db::db_get<ton_api::storage_provider_db_contractList>(
*db_, create_hash_tl_object<ton_api::storage_provider_db_key_contractList>(), true);
r_contract_list.ensure();
auto contract_list = r_contract_list.move_as_ok();
if (contract_list) {
LOG(INFO) << "Loading " << contract_list->contracts_.size() << " contracts from db";
for (auto& c : contract_list->contracts_) {
ContractAddress address(c->wc_, c->addr_);
if (contracts_.count(address)) {
LOG(ERROR) << "Duplicate contract in db: " << address.to_string();
continue;
}
auto r_contract = db::db_get<ton_api::storage_provider_db_storageContract>(
*db_, create_hash_tl_object<ton_api::storage_provider_db_key_storageContract>(address.wc, address.addr),
true);
r_contract.ensure();
auto db_contract = r_contract.move_as_ok();
if (!db_contract) {
LOG(ERROR) << "Missing contract in db: " << address.to_string();
continue;
}
StorageContract& contract = contracts_[address];
contract.torrent_hash = db_contract->torrent_hash_;
contract.microchunk_hash = db_contract->microchunk_hash_;
contract.created_time = db_contract->created_time_;
contract.state = (StorageContract::State)db_contract->state_;
contract.file_size = db_contract->file_size_;
contract.max_span = db_contract->max_span_;
contract.rate = td::string_to_int256(db_contract->rate_);
contracts_total_size_ += contract.file_size;
auto r_tree = db::db_get<ton_api::storage_provider_db_microchunkTree>(
*db_, create_hash_tl_object<ton_api::storage_provider_db_key_microchunkTree>(address.wc, address.addr), true);
r_tree.ensure();
auto tree = r_tree.move_as_ok();
if (tree) {
contract.microchunk_tree = std::make_shared<MicrochunkTree>(vm::std_boc_deserialize(tree->data_).move_as_ok());
}
LOG(INFO) << "Loaded contract from db: " << address.to_string() << ", torrent=" << contract.torrent_hash.to_hex()
<< ", state=" << contract.state;
}
}
for (auto& p : contracts_) {
const ContractAddress& address = p.first;
StorageContract& contract = p.second;
switch (contract.state) {
case StorageContract::st_downloading:
init_new_storage_contract(address, contract);
break;
case StorageContract::st_downloaded:
after_contract_downloaded(address);
TON Storage utilities (#564) * Rename chunk to piece in MerkleTree for consistency * Refactor PeerManager * Make PeerState thread-safe * Download torrent by hash * First version of storage daemon * Download torrents partially * Improve storing and loading torrent state in DB * Rewrite MerkleTree * "Remove torrent" in storage daemon * Process errors, fix bugs in storage * Move TonlibClientWrapper from rldp-http-proxy to tonlib * Initial version of storage provider * Move interaction with contracts to smc-util * Improve TonlibClientWrapper interface * Various improvements in storage provider * Fix TorrentCreator.cpp * Improve interface for partial download * Client mode in storage-daemon * Improve interface of storage-daemon-cli * Fix calculating speed, show peers in storage-daemon * Use permanent adnl id in storage daemon * Fix sending large "storage.addUpdate" messages * Improve printing torrents in cli * Update tlo * Fix RldpSender::on_ack * Update storage provider * Add "address" parameter to get-provider-params * Allow client to close storage contract * Limit torrent description * Add more logs to storage provider * smc.forget tonlib method * Use smc.forget in storage daemon * Optimize sending messages in smc-util.cpp * Fix verbosity, remove excessive logs * Json output in storage-daemon-cli * Update storage provider contracts * Fix rldp2 acks * Change verbosity of logs in rldp2 * Update help and output of commands and in storage-daemon-cli Co-authored-by: SpyCheese <mikle98@yandex.ru>
2022-12-22 09:24:13 +00:00
break;
case StorageContract::st_active:
contract.check_next_proof_at = td::Timestamp::now();
break;
case StorageContract::st_closing:
check_storage_contract_deleted(address);
break;
default:
LOG(FATAL) << "Invalid contract state in db";
}
}
LOG(INFO) << "Loaded contracts from db";
alarm();
}
void StorageProvider::get_params(td::Promise<ProviderParams> promise) {
return get_provider_params(tonlib_client_, main_address_, std::move(promise));
}
void StorageProvider::get_provider_params(td::actor::ActorId<tonlib::TonlibClientWrapper> client,
ContractAddress address, td::Promise<ProviderParams> promise) {
run_get_method(
address, client, "get_storage_params", std::vector<tl_object_ptr<tonlib_api::tvm_StackEntry>>(),
promise.wrap([](std::vector<tl_object_ptr<tonlib_api::tvm_StackEntry>> stack) -> td::Result<ProviderParams> {
if (stack.size() != 5) {
return td::Status::Error(PSTRING() << "Method returned " << stack.size() << " values, 5 expected");
}
TRY_RESULT_PREFIX(accept_new_contracts, entry_to_int<int>(stack[0]), "Invalid accept_new_contracts: ");
TRY_RESULT_PREFIX(rate_per_mb_day, entry_to_int<td::RefInt256>(stack[1]), "Invalid rate_per_mb_day: ");
TRY_RESULT_PREFIX(max_span, entry_to_int<td::uint32>(stack[2]), "Invalid max_span: ");
TRY_RESULT_PREFIX(minimal_file_size, entry_to_int<td::uint64>(stack[3]), "Invalid minimal_file_size: ");
TRY_RESULT_PREFIX(maximal_file_size, entry_to_int<td::uint64>(stack[4]), "Invalid maximal_file_size: ");
ProviderParams params;
params.accept_new_contracts = accept_new_contracts;
params.rate_per_mb_day = rate_per_mb_day;
params.max_span = max_span;
params.minimal_file_size = minimal_file_size;
params.maximal_file_size = maximal_file_size;
return params;
}));
}
void StorageProvider::set_params(ProviderParams params, td::Promise<td::Unit> promise) {
vm::CellBuilder b;
b.store_long(0x54cbf19b, 32); // const op::update_storage_params = 0x54cbf19b;
b.store_long(0, 64); // query_id
if (!params.to_builder(b)) {
promise.set_error(td::Status::Error("Failed to store params to builder"));
return;
}
LOG(INFO) << "Sending external message to update provider parameters: " << params.accept_new_contracts << ", "
<< params.max_span << ", " << params.rate_per_mb_day << ", " << params.minimal_file_size << ", "
<< params.maximal_file_size;
td::actor::send_closure(contract_wrapper_, &FabricContractWrapper::send_internal_message, main_address_,
td::make_refint(100'000'000), b.as_cellslice(), std::move(promise));
}
void StorageProvider::db_store_state() {
LOG(DEBUG) << "db_store_state last_lt=" << last_processed_lt_;
db_->begin_transaction().ensure();
db_->set(create_hash_tl_object<ton_api::storage_provider_db_key_state>().as_slice(),
create_serialize_tl_object<ton_api::storage_provider_db_state>(last_processed_lt_))
.ensure();
db_->commit_transaction().ensure();
}
void StorageProvider::db_store_config() {
LOG(DEBUG) << "db_store_config";
db_->begin_transaction().ensure();
db_->set(create_hash_tl_object<ton_api::storage_provider_db_key_providerConfig>().as_slice(),
serialize_tl_object(config_.tl(), true))
.ensure();
db_->commit_transaction().ensure();
}
void StorageProvider::alarm() {
for (auto& p : contracts_) {
if (p.second.check_next_proof_at && p.second.check_next_proof_at.is_in_past()) {
p.second.check_next_proof_at = td::Timestamp::never();
check_next_proof(p.first, p.second);
}
alarm_timestamp().relax(p.second.check_next_proof_at);
}
}
void StorageProvider::process_transaction(tl_object_ptr<tonlib_api::raw_transaction> transaction) {
std::string new_contract_address;
for (auto& message : transaction->out_msgs_) {
auto data = dynamic_cast<tonlib_api::msg_dataRaw*>(message->msg_data_.get());
if (data == nullptr) {
continue;
}
auto r_body = vm::std_boc_deserialize(data->body_);
if (r_body.is_error()) {
LOG(ERROR) << "Invalid message body in tonlib response: " << r_body.move_as_error();
continue;
}
td::Ref<vm::Cell> body = r_body.move_as_ok();
vm::CellSlice cs = vm::load_cell_slice(body);
if (cs.size() >= 32) {
2023-03-07 18:22:17 +00:00
long long op_code = cs.prefetch_ulong(32);
// const op::offer_storage_contract = 0x107c49ef; -- old versions
// const op::deploy_storage_contract = 0xe4748df1; -- new versions
if((op_code == 0x107c49ef) || (op_code == 0xe4748df1)) {
new_contract_address = message->destination_->account_address_;
}
TON Storage utilities (#564) * Rename chunk to piece in MerkleTree for consistency * Refactor PeerManager * Make PeerState thread-safe * Download torrent by hash * First version of storage daemon * Download torrents partially * Improve storing and loading torrent state in DB * Rewrite MerkleTree * "Remove torrent" in storage daemon * Process errors, fix bugs in storage * Move TonlibClientWrapper from rldp-http-proxy to tonlib * Initial version of storage provider * Move interaction with contracts to smc-util * Improve TonlibClientWrapper interface * Various improvements in storage provider * Fix TorrentCreator.cpp * Improve interface for partial download * Client mode in storage-daemon * Improve interface of storage-daemon-cli * Fix calculating speed, show peers in storage-daemon * Use permanent adnl id in storage daemon * Fix sending large "storage.addUpdate" messages * Improve printing torrents in cli * Update tlo * Fix RldpSender::on_ack * Update storage provider * Add "address" parameter to get-provider-params * Allow client to close storage contract * Limit torrent description * Add more logs to storage provider * smc.forget tonlib method * Use smc.forget in storage daemon * Optimize sending messages in smc-util.cpp * Fix verbosity, remove excessive logs * Json output in storage-daemon-cli * Update storage provider contracts * Fix rldp2 acks * Change verbosity of logs in rldp2 * Update help and output of commands and in storage-daemon-cli Co-authored-by: SpyCheese <mikle98@yandex.ru>
2022-12-22 09:24:13 +00:00
}
}
if (!new_contract_address.empty()) {
auto P = td::PromiseCreator::lambda(
[SelfId = actor_id(this), lt = (td::uint64)transaction->transaction_id_->lt_](td::Result<td::Unit> R) {
if (R.is_error()) {
LOG(ERROR) << "Error during processing new storage contract, skipping: " << R.move_as_error();
}
});
on_new_storage_contract(ContractAddress::parse(new_contract_address).move_as_ok(), std::move(P));
}
last_processed_lt_ = transaction->transaction_id_->lt_;
db_store_state();
}
void StorageProvider::on_new_storage_contract(ContractAddress address, td::Promise<td::Unit> promise, int max_retries) {
LOG(INFO) << "Processing new storage contract: " << address.to_string();
get_storage_contract_data(
address, tonlib_client_,
[SelfId = actor_id(this), address, promise = std::move(promise),
max_retries](td::Result<StorageContractData> R) mutable {
if (R.is_error()) {
if (max_retries > 0) {
LOG(WARNING) << "Processing new storage contract: " << R.move_as_error() << ", retrying";
delay_action(
[SelfId = std::move(SelfId), promise = std::move(promise), address = std::move(address),
max_retries]() mutable {
td::actor::send_closure(SelfId, &StorageProvider::on_new_storage_contract, std::move(address),
std::move(promise), max_retries - 1);
},
td::Timestamp::in(5.0));
} else {
promise.set_error(R.move_as_error());
}
return;
}
td::actor::send_closure(SelfId, &StorageProvider::on_new_storage_contract_cont, address, R.move_as_ok(),
std::move(promise));
});
}
void StorageProvider::on_new_storage_contract_cont(ContractAddress address, StorageContractData data,
td::Promise<td::Unit> promise) {
auto it = contracts_.emplace(address, StorageContract());
if (!it.second) {
promise.set_error(td::Status::Error(PSTRING() << "Storage contract already registered: " << address.to_string()));
return;
}
LOG(INFO) << "New storage contract " << address.to_string() << ", torrent hash: " << data.torrent_hash.to_hex();
LOG(DEBUG) << "Stoage contract data: microchunk_hash=" << data.microchunk_hash << ", balance=" << data.balance
<< ", file_size=" << data.file_size << ", next_proof=" << data.next_proof
<< ", rate=" << data.rate_per_mb_day << ", max_span=" << data.max_span;
StorageContract& contract = it.first->second;
contract.torrent_hash = data.torrent_hash;
contract.microchunk_hash = data.microchunk_hash;
contract.state = StorageContract::st_downloading;
contract.created_time = (td::uint32)td::Clocks::system();
contract.file_size = data.file_size;
contract.max_span = data.max_span;
contract.rate = data.rate_per_mb_day;
contracts_total_size_ += contract.file_size;
promise.set_result(td::Unit());
if (contracts_.size() <= config_.max_contracts && contracts_total_size_ <= config_.max_total_size) {
db_update_storage_contract(address, true);
init_new_storage_contract(address, contract);
} else {
if (contracts_.size() > config_.max_contracts) {
LOG(WARNING) << "Cannot add new storage contract: too many contracts (limit = " << config_.max_contracts << ")";
} else {
LOG(WARNING) << "Cannot add new storage contract: total size exceeded (limit = "
<< td::format::as_size(config_.max_total_size) << ")";
}
contract.state = StorageContract::st_closing;
db_update_storage_contract(address, true);
do_close_storage_contract(address);
}
}
void StorageProvider::db_update_storage_contract(const ContractAddress& address, bool update_list) {
LOG(DEBUG) << "db_update_storage_contract " << address.to_string() << " " << update_list;
db_->begin_transaction().ensure();
if (update_list) {
std::vector<tl_object_ptr<ton_api::storage_provider_db_contractAddress>> list;
for (const auto& t : contracts_) {
list.push_back(create_tl_object<ton_api::storage_provider_db_contractAddress>(t.first.wc, t.first.addr));
}
db_->set(create_hash_tl_object<ton_api::storage_provider_db_key_contractList>().as_slice(),
create_serialize_tl_object<ton_api::storage_provider_db_contractList>(std::move(list)))
.ensure();
}
auto key = create_hash_tl_object<ton_api::storage_provider_db_key_storageContract>(address.wc, address.addr);
auto it = contracts_.find(address);
if (it == contracts_.end()) {
db_->erase(key.as_slice()).ensure();
} else {
const StorageContract& contract = it->second;
db_->set(key.as_slice(),
create_serialize_tl_object<ton_api::storage_provider_db_storageContract>(
contract.torrent_hash, contract.microchunk_hash, contract.created_time, (int)contract.state,
contract.file_size, contract.rate->to_dec_string(), contract.max_span));
}
db_->commit_transaction().ensure();
}
void StorageProvider::db_update_microchunk_tree(const ContractAddress& address) {
LOG(DEBUG) << "db_update_microchunk_tree " << address.to_string();
db_->begin_transaction().ensure();
auto key = create_hash_tl_object<ton_api::storage_provider_db_key_microchunkTree>(address.wc, address.addr);
auto it = contracts_.find(address);
if (it == contracts_.end() || it->second.microchunk_tree == nullptr) {
db_->erase(key.as_slice()).ensure();
} else {
db_->set(key.as_slice(), create_serialize_tl_object<ton_api::storage_provider_db_microchunkTree>(
vm::std_boc_serialize(it->second.microchunk_tree->get_root()).move_as_ok()));
}
db_->commit_transaction().ensure();
}
void StorageProvider::init_new_storage_contract(ContractAddress address, StorageContract& contract) {
CHECK(contract.state == StorageContract::st_downloading);
td::actor::send_closure(storage_manager_, &StorageManager::add_torrent_by_hash, contract.torrent_hash, "", false,
false, [](td::Result<td::Unit> R) {
TON Storage utilities (#564) * Rename chunk to piece in MerkleTree for consistency * Refactor PeerManager * Make PeerState thread-safe * Download torrent by hash * First version of storage daemon * Download torrents partially * Improve storing and loading torrent state in DB * Rewrite MerkleTree * "Remove torrent" in storage daemon * Process errors, fix bugs in storage * Move TonlibClientWrapper from rldp-http-proxy to tonlib * Initial version of storage provider * Move interaction with contracts to smc-util * Improve TonlibClientWrapper interface * Various improvements in storage provider * Fix TorrentCreator.cpp * Improve interface for partial download * Client mode in storage-daemon * Improve interface of storage-daemon-cli * Fix calculating speed, show peers in storage-daemon * Use permanent adnl id in storage daemon * Fix sending large "storage.addUpdate" messages * Improve printing torrents in cli * Update tlo * Fix RldpSender::on_ack * Update storage provider * Add "address" parameter to get-provider-params * Allow client to close storage contract * Limit torrent description * Add more logs to storage provider * smc.forget tonlib method * Use smc.forget in storage daemon * Optimize sending messages in smc-util.cpp * Fix verbosity, remove excessive logs * Json output in storage-daemon-cli * Update storage provider contracts * Fix rldp2 acks * Change verbosity of logs in rldp2 * Update help and output of commands and in storage-daemon-cli Co-authored-by: SpyCheese <mikle98@yandex.ru>
2022-12-22 09:24:13 +00:00
// Ignore errors: error can mean that the torrent already exists, other errors will be caught later
if (R.is_error()) {
LOG(DEBUG) << "Add torrent: " << R.move_as_error();
} else {
LOG(DEBUG) << "Add torrent: OK";
}
});
td::actor::send_closure(storage_manager_, &StorageManager::set_active_download, contract.torrent_hash, true,
[SelfId = actor_id(this), address](td::Result<td::Unit> R) {
if (R.is_error()) {
LOG(ERROR) << "Failed to init storage contract: " << R.move_as_error();
td::actor::send_closure(SelfId, &StorageProvider::do_close_storage_contract, address);
return;
}
LOG(DEBUG) << "Set active download: OK";
});
td::actor::send_closure(
storage_manager_, &StorageManager::wait_for_completion, contract.torrent_hash,
[SelfId = actor_id(this), address, hash = contract.torrent_hash, microchunk_hash = contract.microchunk_hash,
manager = storage_manager_](td::Result<td::Unit> R) {
if (R.is_error()) {
LOG(WARNING) << "Failed to download torrent " << hash.to_hex() << ": " << R.move_as_error();
td::actor::send_closure(SelfId, &StorageProvider::do_close_storage_contract, address);
return;
}
LOG(DEBUG) << "Downloaded torrent " << hash;
td::actor::send_closure(
manager, &StorageManager::with_torrent, hash,
[SelfId, address, hash, microchunk_hash](td::Result<NodeActor::NodeState> R) {
auto r_microchunk_tree = [&]() -> td::Result<MicrochunkTree> {
TRY_RESULT(state, std::move(R));
Torrent& torrent = state.torrent;
if (!torrent.is_completed() || torrent.get_included_size() != torrent.get_info().file_size) {
return td::Status::Error("unknown error");
}
LOG(DEBUG) << "Building microchunk tree for " << hash;
TRY_RESULT(tree, MicrochunkTree::Builder::build_for_torrent(torrent));
if (tree.get_root_hash() != microchunk_hash) {
return td::Status::Error("microchunk tree hash mismatch");
}
return tree;
}();
if (r_microchunk_tree.is_error()) {
LOG(WARNING) << "Failed to download torrent " << hash.to_hex() << ": " << R.move_as_error();
td::actor::send_closure(SelfId, &StorageProvider::do_close_storage_contract, address);
} else {
td::actor::send_closure(SelfId, &StorageProvider::downloaded_torrent, address,
r_microchunk_tree.move_as_ok());
}
});
});
}
void StorageProvider::downloaded_torrent(ContractAddress address, MicrochunkTree microchunk_tree) {
auto it = contracts_.find(address);
if (it == contracts_.end()) {
LOG(WARNING) << "Contract " << address.to_string() << " does not exist anymore";
return;
}
auto& contract = it->second;
LOG(INFO) << "Finished downloading torrent " << contract.torrent_hash.to_hex() << " for contract "
<< address.to_string();
contract.state = StorageContract::st_downloaded;
contract.microchunk_tree = std::make_shared<MicrochunkTree>(std::move(microchunk_tree));
db_update_microchunk_tree(address);
db_update_storage_contract(address, false);
after_contract_downloaded(address);
TON Storage utilities (#564) * Rename chunk to piece in MerkleTree for consistency * Refactor PeerManager * Make PeerState thread-safe * Download torrent by hash * First version of storage daemon * Download torrents partially * Improve storing and loading torrent state in DB * Rewrite MerkleTree * "Remove torrent" in storage daemon * Process errors, fix bugs in storage * Move TonlibClientWrapper from rldp-http-proxy to tonlib * Initial version of storage provider * Move interaction with contracts to smc-util * Improve TonlibClientWrapper interface * Various improvements in storage provider * Fix TorrentCreator.cpp * Improve interface for partial download * Client mode in storage-daemon * Improve interface of storage-daemon-cli * Fix calculating speed, show peers in storage-daemon * Use permanent adnl id in storage daemon * Fix sending large "storage.addUpdate" messages * Improve printing torrents in cli * Update tlo * Fix RldpSender::on_ack * Update storage provider * Add "address" parameter to get-provider-params * Allow client to close storage contract * Limit torrent description * Add more logs to storage provider * smc.forget tonlib method * Use smc.forget in storage daemon * Optimize sending messages in smc-util.cpp * Fix verbosity, remove excessive logs * Json output in storage-daemon-cli * Update storage provider contracts * Fix rldp2 acks * Change verbosity of logs in rldp2 * Update help and output of commands and in storage-daemon-cli Co-authored-by: SpyCheese <mikle98@yandex.ru>
2022-12-22 09:24:13 +00:00
}
void StorageProvider::after_contract_downloaded(ContractAddress address, td::Timestamp retry_until,
td::Timestamp retry_false_until) {
auto it = contracts_.find(address);
if (it == contracts_.end()) {
LOG(WARNING) << "Contract " << address.to_string() << " does not exist anymore";
return;
}
auto& contract = it->second;
td::actor::send_closure(storage_manager_, &StorageManager::set_active_upload, contract.torrent_hash, true,
[SelfId = actor_id(this), address](td::Result<td::Unit> R) {
if (R.is_error()) {
LOG(ERROR) << "Set active upload: " << R.move_as_error();
return;
}
LOG(DEBUG) << "Set active upload: OK";
});
TON Storage utilities (#564) * Rename chunk to piece in MerkleTree for consistency * Refactor PeerManager * Make PeerState thread-safe * Download torrent by hash * First version of storage daemon * Download torrents partially * Improve storing and loading torrent state in DB * Rewrite MerkleTree * "Remove torrent" in storage daemon * Process errors, fix bugs in storage * Move TonlibClientWrapper from rldp-http-proxy to tonlib * Initial version of storage provider * Move interaction with contracts to smc-util * Improve TonlibClientWrapper interface * Various improvements in storage provider * Fix TorrentCreator.cpp * Improve interface for partial download * Client mode in storage-daemon * Improve interface of storage-daemon-cli * Fix calculating speed, show peers in storage-daemon * Use permanent adnl id in storage daemon * Fix sending large "storage.addUpdate" messages * Improve printing torrents in cli * Update tlo * Fix RldpSender::on_ack * Update storage provider * Add "address" parameter to get-provider-params * Allow client to close storage contract * Limit torrent description * Add more logs to storage provider * smc.forget tonlib method * Use smc.forget in storage daemon * Optimize sending messages in smc-util.cpp * Fix verbosity, remove excessive logs * Json output in storage-daemon-cli * Update storage provider contracts * Fix rldp2 acks * Change verbosity of logs in rldp2 * Update help and output of commands and in storage-daemon-cli Co-authored-by: SpyCheese <mikle98@yandex.ru>
2022-12-22 09:24:13 +00:00
get_storage_contract_data(address, tonlib_client_,
[=, SelfId = actor_id(this)](td::Result<StorageContractData> R) mutable {
if (R.is_error()) {
LOG(WARNING) << "Failed to check that contract is active: " << R.move_as_error();
if (retry_until && retry_until.is_in_past()) {
delay_action(
[=]() {
td::actor::send_closure(SelfId, &StorageProvider::after_contract_downloaded,
TON Storage utilities (#564) * Rename chunk to piece in MerkleTree for consistency * Refactor PeerManager * Make PeerState thread-safe * Download torrent by hash * First version of storage daemon * Download torrents partially * Improve storing and loading torrent state in DB * Rewrite MerkleTree * "Remove torrent" in storage daemon * Process errors, fix bugs in storage * Move TonlibClientWrapper from rldp-http-proxy to tonlib * Initial version of storage provider * Move interaction with contracts to smc-util * Improve TonlibClientWrapper interface * Various improvements in storage provider * Fix TorrentCreator.cpp * Improve interface for partial download * Client mode in storage-daemon * Improve interface of storage-daemon-cli * Fix calculating speed, show peers in storage-daemon * Use permanent adnl id in storage daemon * Fix sending large "storage.addUpdate" messages * Improve printing torrents in cli * Update tlo * Fix RldpSender::on_ack * Update storage provider * Add "address" parameter to get-provider-params * Allow client to close storage contract * Limit torrent description * Add more logs to storage provider * smc.forget tonlib method * Use smc.forget in storage daemon * Optimize sending messages in smc-util.cpp * Fix verbosity, remove excessive logs * Json output in storage-daemon-cli * Update storage provider contracts * Fix rldp2 acks * Change verbosity of logs in rldp2 * Update help and output of commands and in storage-daemon-cli Co-authored-by: SpyCheese <mikle98@yandex.ru>
2022-12-22 09:24:13 +00:00
address, retry_until, retry_false_until);
},
td::Timestamp::in(5.0));
}
return;
}
if (R.ok().active) {
td::actor::send_closure(SelfId, &StorageProvider::activated_storage_contract, address);
} else if (retry_false_until && retry_false_until.is_in_past()) {
delay_action(
[=]() {
td::actor::send_closure(SelfId, &StorageProvider::after_contract_downloaded,
address, retry_until, retry_false_until);
TON Storage utilities (#564) * Rename chunk to piece in MerkleTree for consistency * Refactor PeerManager * Make PeerState thread-safe * Download torrent by hash * First version of storage daemon * Download torrents partially * Improve storing and loading torrent state in DB * Rewrite MerkleTree * "Remove torrent" in storage daemon * Process errors, fix bugs in storage * Move TonlibClientWrapper from rldp-http-proxy to tonlib * Initial version of storage provider * Move interaction with contracts to smc-util * Improve TonlibClientWrapper interface * Various improvements in storage provider * Fix TorrentCreator.cpp * Improve interface for partial download * Client mode in storage-daemon * Improve interface of storage-daemon-cli * Fix calculating speed, show peers in storage-daemon * Use permanent adnl id in storage daemon * Fix sending large "storage.addUpdate" messages * Improve printing torrents in cli * Update tlo * Fix RldpSender::on_ack * Update storage provider * Add "address" parameter to get-provider-params * Allow client to close storage contract * Limit torrent description * Add more logs to storage provider * smc.forget tonlib method * Use smc.forget in storage daemon * Optimize sending messages in smc-util.cpp * Fix verbosity, remove excessive logs * Json output in storage-daemon-cli * Update storage provider contracts * Fix rldp2 acks * Change verbosity of logs in rldp2 * Update help and output of commands and in storage-daemon-cli Co-authored-by: SpyCheese <mikle98@yandex.ru>
2022-12-22 09:24:13 +00:00
},
td::Timestamp::in(5.0));
} else {
td::actor::send_closure(SelfId, &StorageProvider::activate_contract_cont, address);
}
});
}
void StorageProvider::activate_contract_cont(ContractAddress address) {
vm::CellBuilder b;
b.store_long(0x7a361688, 32); // const op::accept_storage_contract = 0x7a361688;
b.store_long(0, 64); // query_id
LOG(DEBUG) << "Sending op::accept_storage_contract to " << address.to_string();
td::actor::send_closure(
contract_wrapper_, &FabricContractWrapper::send_internal_message, address, td::make_refint(100'000'000),
b.as_cellslice(), [SelfId = actor_id(this), address](td::Result<td::Unit> R) {
if (R.is_error()) {
LOG(ERROR) << "Failed to send activate message, retrying later: " << R.move_as_error();
delay_action([=]() { td::actor::send_closure(SelfId, &StorageProvider::activate_contract_cont, address); },
td::Timestamp::in(10.0));
return;
}
td::actor::send_closure(SelfId, &StorageProvider::after_contract_downloaded, address, td::Timestamp::in(60.0),
TON Storage utilities (#564) * Rename chunk to piece in MerkleTree for consistency * Refactor PeerManager * Make PeerState thread-safe * Download torrent by hash * First version of storage daemon * Download torrents partially * Improve storing and loading torrent state in DB * Rewrite MerkleTree * "Remove torrent" in storage daemon * Process errors, fix bugs in storage * Move TonlibClientWrapper from rldp-http-proxy to tonlib * Initial version of storage provider * Move interaction with contracts to smc-util * Improve TonlibClientWrapper interface * Various improvements in storage provider * Fix TorrentCreator.cpp * Improve interface for partial download * Client mode in storage-daemon * Improve interface of storage-daemon-cli * Fix calculating speed, show peers in storage-daemon * Use permanent adnl id in storage daemon * Fix sending large "storage.addUpdate" messages * Improve printing torrents in cli * Update tlo * Fix RldpSender::on_ack * Update storage provider * Add "address" parameter to get-provider-params * Allow client to close storage contract * Limit torrent description * Add more logs to storage provider * smc.forget tonlib method * Use smc.forget in storage daemon * Optimize sending messages in smc-util.cpp * Fix verbosity, remove excessive logs * Json output in storage-daemon-cli * Update storage provider contracts * Fix rldp2 acks * Change verbosity of logs in rldp2 * Update help and output of commands and in storage-daemon-cli Co-authored-by: SpyCheese <mikle98@yandex.ru>
2022-12-22 09:24:13 +00:00
td::Timestamp::in(40.0));
});
}
void StorageProvider::activated_storage_contract(ContractAddress address) {
auto it = contracts_.find(address);
if (it == contracts_.end()) {
LOG(WARNING) << "Contract " << address.to_string() << " does not exist anymore";
return;
}
LOG(INFO) << "Storage contract " << address.to_string() << " is active";
auto& contract = it->second;
contract.state = StorageContract::st_active;
db_update_storage_contract(address, false);
alarm_timestamp().relax(contract.check_next_proof_at = td::Timestamp::in(1.0));
}
void StorageProvider::do_close_storage_contract(ContractAddress address) {
auto it = contracts_.find(address);
if (it == contracts_.end()) {
LOG(WARNING) << "Contract " << address.to_string() << " does not exist anymore";
return;
}
LOG(INFO) << "Closing storage contract " << address.to_string();
auto& contract = it->second;
contract.state = StorageContract::st_closing;
db_update_storage_contract(address, false);
check_storage_contract_deleted(address);
}
void StorageProvider::send_close_storage_contract(ContractAddress address) {
vm::CellBuilder b;
b.store_long(0x79f937ea, 32); // const op::close_contract = 0x79f937ea;
b.store_long(0, 64); // query_id
LOG(DEBUG) << "Sending op::close_contract to " << address.to_string();
td::actor::send_closure(
contract_wrapper_, &FabricContractWrapper::send_internal_message, address, td::make_refint(100'000'000),
b.as_cellslice(), [SelfId = actor_id(this), address](td::Result<td::Unit> R) {
if (R.is_error()) {
LOG(ERROR) << "Failed to send close message, retrying later: " << R.move_as_error();
delay_action([=]() { td::actor::send_closure(SelfId, &StorageProvider::activate_contract_cont, address); },
td::Timestamp::in(10.0));
return;
}
td::actor::send_closure(SelfId, &StorageProvider::check_storage_contract_deleted, address,
td::Timestamp::in(40.0));
});
}
void StorageProvider::check_storage_contract_deleted(ContractAddress address, td::Timestamp retry_false_until) {
check_contract_exists(address, tonlib_client_, [=, SelfId = actor_id(this)](td::Result<bool> R) {
if (R.is_error()) {
delay_action(
[=]() {
td::actor::send_closure(SelfId, &StorageProvider::check_storage_contract_deleted, address,
retry_false_until);
},
td::Timestamp::in(10.0));
return;
}
if (!R.move_as_ok()) {
td::actor::send_closure(SelfId, &StorageProvider::storage_contract_deleted, address);
} else if (retry_false_until && !retry_false_until.is_in_past()) {
delay_action(
[=]() {
td::actor::send_closure(SelfId, &StorageProvider::check_storage_contract_deleted, address,
retry_false_until);
},
td::Timestamp::in(5.0));
} else {
td::actor::send_closure(SelfId, &StorageProvider::send_close_storage_contract, address);
}
});
}
void StorageProvider::storage_contract_deleted(ContractAddress address) {
auto it = contracts_.find(address);
if (it == contracts_.end()) {
return;
}
LOG(INFO) << "Storage contract " << address.to_string() << " was deleted";
td::Bits256 hash = it->second.torrent_hash;
contracts_total_size_ -= it->second.file_size;
contracts_.erase(it);
bool delete_torrent = true;
for (const auto& p : contracts_) {
if (p.second.torrent_hash == hash) {
delete_torrent = false;
break;
}
}
if (delete_torrent) {
LOG(INFO) << "Deleting torrent " << hash.to_hex();
td::actor::send_closure(storage_manager_, &StorageManager::remove_torrent, hash, true,
[](td::Result<td::Unit> R) {});
}
db_update_storage_contract(address, true);
}
void StorageProvider::check_next_proof(ContractAddress address, StorageContract& contract) {
if (contract.state != StorageContract::st_active) {
return;
}
CHECK(contract.microchunk_tree != nullptr);
get_storage_contract_data(
address, tonlib_client_, [SelfId = actor_id(this), address](td::Result<StorageContractData> R) {
td::actor::send_closure(SelfId, &StorageProvider::got_next_proof_info, address, std::move(R));
});
}
void StorageProvider::got_next_proof_info(ContractAddress address, td::Result<StorageContractData> R) {
auto it = contracts_.find(address);
if (it == contracts_.end() || it->second.state != StorageContract::st_active) {
return;
}
auto& contract = it->second;
if (R.is_error()) {
LOG(ERROR) << "get_next_proof_info for " << address.to_string() << ": " << R.move_as_error();
check_contract_exists(address, tonlib_client_, [SelfId = actor_id(this), address](td::Result<bool> R) {
td::actor::send_closure(SelfId, &StorageProvider::got_contract_exists, address, std::move(R));
});
return;
}
auto data = R.move_as_ok();
if (data.balance->sgn() == 0) {
LOG(INFO) << "Balance of contract " << address.to_string() << " is zero, closing";
do_close_storage_contract(address);
return;
}
td::uint32 send_at = data.last_proof_time + data.max_span / 2, now = (td::uint32)td::Clocks::system();
if (now < send_at) {
LOG(DEBUG) << "Will send proof in " << send_at - now << "s (last_proof_time=" << data.last_proof_time
<< ", max_span=" << data.max_span << ")";
alarm_timestamp().relax(contract.check_next_proof_at = td::Timestamp::in(send_at - now + 2));
return;
}
LOG(INFO) << "Sending proof for " << address.to_string() << ": next_proof=" << data.next_proof
<< ", max_span=" << data.max_span << ", last_proof_time=" << data.last_proof_time << " ("
<< now - data.last_proof_time << "s ago)";
td::actor::send_closure(
storage_manager_, &StorageManager::with_torrent, contract.torrent_hash,
[=, SelfId = actor_id(this), tree = contract.microchunk_tree](td::Result<NodeActor::NodeState> R) {
if (R.is_error()) {
LOG(ERROR) << "Missing torrent for " << address.to_string();
return;
}
auto state = R.move_as_ok();
td::uint64 l = data.next_proof / MicrochunkTree::MICROCHUNK_SIZE * MicrochunkTree::MICROCHUNK_SIZE;
td::uint64 r = l + MicrochunkTree::MICROCHUNK_SIZE;
auto proof = tree->get_proof(l, r, state.torrent);
td::actor::send_closure(SelfId, &StorageProvider::got_next_proof, address, std::move(proof));
});
}
void StorageProvider::got_contract_exists(ContractAddress address, td::Result<bool> R) {
auto it = contracts_.find(address);
if (it == contracts_.end() || it->second.state != StorageContract::st_active) {
return;
}
auto& contract = it->second;
if (R.is_error()) {
LOG(ERROR) << "Check contract exists for " << address.to_string() << ": " << R.move_as_error();
alarm_timestamp().relax(contract.check_next_proof_at = td::Timestamp::in(10.0));
return;
}
if (R.ok()) {
alarm_timestamp().relax(contract.check_next_proof_at = td::Timestamp::in(10.0));
return;
}
storage_contract_deleted(address);
}
void StorageProvider::got_next_proof(ContractAddress address, td::Result<td::Ref<vm::Cell>> R) {
if (R.is_error()) {
LOG(ERROR) << "Failed to build proof: " << R.move_as_error();
return;
}
LOG(INFO) << "Got proof, sending";
vm::CellBuilder b;
b.store_long(0x419d5d4d, 32); // const op::proof_storage = 0x419d5d4d;
b.store_long(0, 64); // query_id
b.store_ref(R.move_as_ok());
td::actor::send_closure(contract_wrapper_, &FabricContractWrapper::send_internal_message, address,
td::make_refint(100'000'000), b.as_cellslice(),
[SelfId = actor_id(this), address](td::Result<td::Unit> R) {
if (R.is_error()) {
LOG(ERROR) << "Failed to send proof message: " << R.move_as_error();
} else {
LOG(DEBUG) << "Proof for " << address.to_string() << " was sent";
}
td::actor::send_closure(SelfId, &StorageProvider::sent_next_proof, address);
});
}
void StorageProvider::sent_next_proof(ContractAddress address) {
auto it = contracts_.find(address);
if (it == contracts_.end() || it->second.state != StorageContract::st_active) {
return;
}
auto& contract = it->second;
alarm_timestamp().relax(contract.check_next_proof_at = td::Timestamp::in(30.0));
}
void StorageProvider::get_provider_info(bool with_balances, bool with_contracts,
td::Promise<tl_object_ptr<ton_api::storage_daemon_providerInfo>> promise) {
auto result = std::make_shared<ton_api::storage_daemon_providerInfo>();
td::MultiPromise mp;
auto ig = mp.init_guard();
ig.add_promise(promise.wrap(
[result](td::Unit) { return create_tl_object<ton_api::storage_daemon_providerInfo>(std::move(*result)); }));
result->address_ = main_address_.to_string();
result->config_ = config_.tl();
result->contracts_count_ = (int)contracts_.size();
result->contracts_total_size_ = contracts_total_size_;
if (with_balances) {
get_contract_balance(main_address_, tonlib_client_, ig.get_promise().wrap([result](td::RefInt256 balance) {
result->balance_ = balance->to_dec_string();
return td::Unit();
}));
} else {
result->balance_ = "-1";
}
if (with_contracts) {
for (const auto& p : contracts_) {
auto obj = create_tl_object<ton_api::storage_daemon_contractInfo>();
const StorageContract& contract = p.second;
obj->address_ = p.first.to_string();
obj->state_ = (int)contract.state;
obj->torrent_ = contract.torrent_hash;
obj->created_time_ = contract.created_time;
obj->rate_ = contract.rate->to_dec_string();
obj->max_span_ = contract.max_span;
obj->file_size_ = contract.file_size;
obj->downloaded_size_ = obj->file_size_;
obj->client_balance_ = "-1";
obj->contract_balance_ = "-1";
result->contracts_.push_back(std::move(obj));
}
size_t i = 0;
for (const auto& p : contracts_) {
const StorageContract& contract = p.second;
if (contract.state == StorageContract::st_downloading) {
td::actor::send_closure(storage_manager_, &StorageManager::with_torrent, contract.torrent_hash,
[i, result, promise = ig.get_promise()](td::Result<NodeActor::NodeState> R) mutable {
if (R.is_error()) {
result->contracts_[i]->downloaded_size_ = 0;
} else {
auto state = R.move_as_ok();
result->contracts_[i]->downloaded_size_ = state.torrent.get_included_ready_size();
}
promise.set_result(td::Unit());
});
}
if (with_balances) {
get_contract_balance(p.first, tonlib_client_,
[i, result, promise = ig.get_promise()](td::Result<td::RefInt256> R) mutable {
if (R.is_ok()) {
result->contracts_[i]->contract_balance_ = R.ok()->to_dec_string();
}
promise.set_result(td::Unit());
});
get_storage_contract_data(p.first, tonlib_client_,
[i, result, promise = ig.get_promise()](td::Result<StorageContractData> R) mutable {
auto S = [&]() -> td::Status {
TRY_RESULT(data, std::move(R));
result->contracts_[i]->client_balance_ = data.balance->to_dec_string();
return td::Status::OK();
}();
promise.set_result(td::Unit());
});
}
i += 1;
}
}
}
void StorageProvider::set_provider_config(Config config, td::Promise<td::Unit> promise) {
config_ = config;
LOG(INFO) << "Changing provider config: max_contracts=" << config_.max_contracts
<< ", max_total_size=" << config_.max_total_size;
db_store_config();
promise.set_result(td::Unit());
}
void StorageProvider::withdraw(ContractAddress address, td::Promise<td::Unit> promise) {
auto it = contracts_.find(address);
if (it == contracts_.end() || it->second.state != StorageContract::st_active) {
promise.set_error(td::Status::Error("No such storage contract"));
return;
}
if (it->second.state != StorageContract::st_active) {
promise.set_error(td::Status::Error("Storage contract is not active"));
return;
}
vm::CellBuilder b;
b.store_long(0x46ed2e94, 32); // const op::withdraw = 0x46ed2e94;
b.store_long(0, 64); // query_id
LOG(INFO) << "Sending op::withdraw to storage contract " << address.to_string();
td::actor::send_closure(contract_wrapper_, &FabricContractWrapper::send_internal_message, address,
td::make_refint(100'000'000), b.as_cellslice(), std::move(promise));
}
void StorageProvider::send_coins(ContractAddress dest, td::RefInt256 amount, std::string message,
td::Promise<td::Unit> promise) {
if (amount->sgn() < 0) {
promise.set_error(td::Status::Error("Amount is negative"));
return;
}
vm::CellBuilder b;
if (!message.empty()) {
b.store_long(0, 32);
if (b.remaining_bits() < message.size() * 8) {
promise.set_error(td::Status::Error("Message is too long (max 122 bytes)"));
return;
}
b.store_bytes(td::Slice(message));
}
LOG(INFO) << "Sending " << amount << " nanoTON to " << dest.to_string();
td::actor::send_closure(contract_wrapper_, &FabricContractWrapper::send_internal_message, dest, amount,
b.finalize_novm(), std::move(promise));
}
void StorageProvider::close_storage_contract(ContractAddress address, td::Promise<td::Unit> promise) {
if (!contracts_.count(address)) {
promise.set_error(td::Status::Error("No such storage contract"));
return;
}
do_close_storage_contract(address);
promise.set_result(td::Unit());
}
StorageProvider::Config::Config(const tl_object_ptr<ton_api::storage_daemon_providerConfig>& obj)
: max_contracts(obj->max_contracts_), max_total_size(obj->max_total_size_) {
}
tl_object_ptr<ton_api::storage_daemon_providerConfig> StorageProvider::Config::tl() const {
return create_tl_object<ton_api::storage_daemon_providerConfig>(max_contracts, max_total_size);
}
} // namespace ton