mirror of
				https://github.com/ton-blockchain/ton
				synced 2025-03-09 15:40:10 +00:00 
			
		
		
		
	Recent updates in storage (#667)
* Fix error handling in Torrent.cpp, improve choosing peers for upload * Various improvements in storage daemon "get-pieces-info" Store "added at" Improve calculating up/down speed Improve TL protocol for future compatibility Remove empty directories on "--remove-files" Better windows support Debug logs in PeerActor More restrictions on TorrentInfo Bugfixes * Global speed limits for download and upload +bugfix * Reset download/upload speed on changing settings or completion * Exclude some system files in TorrentCreator
This commit is contained in:
		
							parent
							
								
									e3af63e6c0
								
							
						
					
					
						commit
						bb21f732fd
					
				
					 21 changed files with 974 additions and 213 deletions
				
			
		| 
						 | 
				
			
			@ -8,9 +8,11 @@ endif()
 | 
			
		|||
set(STORAGE_SOURCE
 | 
			
		||||
  LoadSpeed.cpp
 | 
			
		||||
  MerkleTree.cpp
 | 
			
		||||
  MicrochunkTree.cpp
 | 
			
		||||
  NodeActor.cpp
 | 
			
		||||
  PeerActor.cpp
 | 
			
		||||
  PeerState.cpp
 | 
			
		||||
  SpeedLimiter.cpp
 | 
			
		||||
  Torrent.cpp
 | 
			
		||||
  TorrentCreator.cpp
 | 
			
		||||
  TorrentHeader.cpp
 | 
			
		||||
| 
						 | 
				
			
			@ -25,13 +27,14 @@ set(STORAGE_SOURCE
 | 
			
		|||
  PartsHelper.h
 | 
			
		||||
  PeerActor.h
 | 
			
		||||
  PeerState.h
 | 
			
		||||
  SpeedLimiter.h
 | 
			
		||||
  Torrent.h
 | 
			
		||||
  TorrentCreator.h
 | 
			
		||||
  TorrentHeader.h
 | 
			
		||||
  TorrentInfo.h
 | 
			
		||||
  TorrentMeta.h
 | 
			
		||||
  PeerManager.h
 | 
			
		||||
  MicrochunkTree.h MicrochunkTree.cpp)
 | 
			
		||||
  MicrochunkTree.h)
 | 
			
		||||
set(STORAGE_CLI_SOURCE
 | 
			
		||||
  storage-cli.cpp
 | 
			
		||||
)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -31,13 +31,17 @@ double LoadSpeed::speed(td::Timestamp now) const {
 | 
			
		|||
  update(now);
 | 
			
		||||
  return (double)total_size_ / duration(now);
 | 
			
		||||
}
 | 
			
		||||
void LoadSpeed::reset() {
 | 
			
		||||
  events_ = {};
 | 
			
		||||
  total_size_ = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
td::StringBuilder &operator<<(td::StringBuilder &sb, const LoadSpeed &speed) {
 | 
			
		||||
  return sb << td::format::as_size(static_cast<td::uint64>(speed.speed())) << "/s";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void LoadSpeed::update(td::Timestamp now) const {
 | 
			
		||||
  while (duration(now) > 30) {
 | 
			
		||||
  while (duration(now) > 10) {
 | 
			
		||||
    total_size_ -= events_.front().size;
 | 
			
		||||
    events_.pop();
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -28,6 +28,7 @@ class LoadSpeed {
 | 
			
		|||
 public:
 | 
			
		||||
  void add(td::uint64 size, td::Timestamp now = td::Timestamp::now());
 | 
			
		||||
  double speed(td::Timestamp now = td::Timestamp::now()) const;
 | 
			
		||||
  void reset();
 | 
			
		||||
  friend td::StringBuilder &operator<<(td::StringBuilder &sb, const LoadSpeed &speed);
 | 
			
		||||
 | 
			
		||||
 private:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -25,27 +25,14 @@
 | 
			
		|||
#include "td/utils/Enumerator.h"
 | 
			
		||||
#include "td/utils/tests.h"
 | 
			
		||||
#include "td/utils/overloaded.h"
 | 
			
		||||
#include "tl-utils/common-utils.hpp"
 | 
			
		||||
#include "tl-utils/tl-utils.hpp"
 | 
			
		||||
#include "auto/tl/ton_api.hpp"
 | 
			
		||||
#include "td/actor/MultiPromise.h"
 | 
			
		||||
 | 
			
		||||
namespace ton {
 | 
			
		||||
NodeActor::NodeActor(PeerId self_id, Torrent torrent, td::unique_ptr<Callback> callback,
 | 
			
		||||
                     td::unique_ptr<NodeCallback> node_callback, std::shared_ptr<db::DbType> db, bool should_download,
 | 
			
		||||
                     bool should_upload)
 | 
			
		||||
    : self_id_(self_id)
 | 
			
		||||
    , torrent_(std::move(torrent))
 | 
			
		||||
    , callback_(std::move(callback))
 | 
			
		||||
    , node_callback_(std::move(node_callback))
 | 
			
		||||
    , db_(std::move(db))
 | 
			
		||||
    , should_download_(should_download)
 | 
			
		||||
    , should_upload_(should_upload) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
NodeActor::NodeActor(PeerId self_id, ton::Torrent torrent, td::unique_ptr<Callback> callback,
 | 
			
		||||
                     td::unique_ptr<NodeCallback> node_callback, std::shared_ptr<db::DbType> db, bool should_download,
 | 
			
		||||
                     bool should_upload, DbInitialData db_initial_data)
 | 
			
		||||
                     td::unique_ptr<NodeCallback> node_callback, std::shared_ptr<db::DbType> db,
 | 
			
		||||
                     SpeedLimiters speed_limiters, bool should_download, bool should_upload)
 | 
			
		||||
    : self_id_(self_id)
 | 
			
		||||
    , torrent_(std::move(torrent))
 | 
			
		||||
    , callback_(std::move(callback))
 | 
			
		||||
| 
						 | 
				
			
			@ -53,6 +40,23 @@ NodeActor::NodeActor(PeerId self_id, ton::Torrent torrent, td::unique_ptr<Callba
 | 
			
		|||
    , db_(std::move(db))
 | 
			
		||||
    , should_download_(should_download)
 | 
			
		||||
    , should_upload_(should_upload)
 | 
			
		||||
    , added_at_((td::uint32)td::Clocks::system())
 | 
			
		||||
    , speed_limiters_(std::move(speed_limiters)) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
NodeActor::NodeActor(PeerId self_id, ton::Torrent torrent, td::unique_ptr<Callback> callback,
 | 
			
		||||
                     td::unique_ptr<NodeCallback> node_callback, std::shared_ptr<db::DbType> db,
 | 
			
		||||
                     SpeedLimiters speed_limiters, bool should_download, bool should_upload,
 | 
			
		||||
                     DbInitialData db_initial_data)
 | 
			
		||||
    : self_id_(self_id)
 | 
			
		||||
    , torrent_(std::move(torrent))
 | 
			
		||||
    , callback_(std::move(callback))
 | 
			
		||||
    , node_callback_(std::move(node_callback))
 | 
			
		||||
    , db_(std::move(db))
 | 
			
		||||
    , should_download_(should_download)
 | 
			
		||||
    , should_upload_(should_upload)
 | 
			
		||||
    , added_at_(db_initial_data.added_at)
 | 
			
		||||
    , speed_limiters_(std::move(speed_limiters))
 | 
			
		||||
    , pending_set_file_priority_(std::move(db_initial_data.priorities))
 | 
			
		||||
    , pieces_in_db_(std::move(db_initial_data.pieces_in_db)) {
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -100,12 +104,12 @@ void NodeActor::init_torrent() {
 | 
			
		|||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  torrent_info_str_ =
 | 
			
		||||
      std::make_shared<td::BufferSlice>(vm::std_boc_serialize(torrent_.get_info().as_cell()).move_as_ok());
 | 
			
		||||
  torrent_info_shared_ = std::make_shared<TorrentInfo>(torrent_.get_info());
 | 
			
		||||
  for (auto &p : peers_) {
 | 
			
		||||
    auto &state = p.second.state;
 | 
			
		||||
    state->torrent_info_str_ = torrent_info_str_;
 | 
			
		||||
    state->torrent_info_ = torrent_info_shared_;
 | 
			
		||||
    CHECK(!state->torrent_info_ready_.exchange(true));
 | 
			
		||||
    state->notify_peer();
 | 
			
		||||
  }
 | 
			
		||||
  LOG(INFO) << "Inited torrent info for " << torrent_.get_hash().to_hex() << ": size=" << torrent_.get_info().file_size
 | 
			
		||||
            << ", pieces=" << torrent_.get_info().pieces_count();
 | 
			
		||||
| 
						 | 
				
			
			@ -185,7 +189,7 @@ void NodeActor::loop_will_upload() {
 | 
			
		|||
    auto &state = it.second.state;
 | 
			
		||||
    bool needed = false;
 | 
			
		||||
    if (state->peer_state_ready_) {
 | 
			
		||||
      needed = state->peer_state_.load().want_download;
 | 
			
		||||
      needed = state->peer_state_.load().want_download && state->peer_online_;
 | 
			
		||||
    }
 | 
			
		||||
    peers.emplace_back(!needed, !state->node_state_.load().want_download, -it.second.download_speed.speed(), it.first);
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			@ -247,6 +251,10 @@ void NodeActor::loop() {
 | 
			
		|||
      }
 | 
			
		||||
      wait_for_completion_.clear();
 | 
			
		||||
      is_completed_ = true;
 | 
			
		||||
      download_speed_.reset();
 | 
			
		||||
      for (auto &peer : peers_) {
 | 
			
		||||
        peer.second.download_speed.reset();
 | 
			
		||||
      }
 | 
			
		||||
      callback_->on_completed();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			@ -398,6 +406,12 @@ void NodeActor::set_should_download(bool should_download) {
 | 
			
		|||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  should_download_ = should_download;
 | 
			
		||||
  if (!should_download_) {
 | 
			
		||||
    download_speed_.reset();
 | 
			
		||||
    for (auto &peer : peers_) {
 | 
			
		||||
      peer.second.download_speed.reset();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  db_store_torrent();
 | 
			
		||||
  yield();
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -407,7 +421,14 @@ void NodeActor::set_should_upload(bool should_upload) {
 | 
			
		|||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  should_upload_ = should_upload;
 | 
			
		||||
  if (!should_upload_) {
 | 
			
		||||
    upload_speed_.reset();
 | 
			
		||||
    for (auto &peer : peers_) {
 | 
			
		||||
      peer.second.upload_speed.reset();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  db_store_torrent();
 | 
			
		||||
  will_upload_at_ = td::Timestamp::now();
 | 
			
		||||
  yield();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -478,6 +499,7 @@ void NodeActor::loop_start_stop_peers() {
 | 
			
		|||
 | 
			
		||||
    if (peer.actor.empty()) {
 | 
			
		||||
      auto &state = peer.state = std::make_shared<PeerState>(peer.notifier.get());
 | 
			
		||||
      state->speed_limiters_ = speed_limiters_;
 | 
			
		||||
      if (torrent_.inited_info()) {
 | 
			
		||||
        std::vector<td::uint32> node_ready_parts;
 | 
			
		||||
        for (td::uint32 i = 0; i < parts_.parts.size(); i++) {
 | 
			
		||||
| 
						 | 
				
			
			@ -486,7 +508,7 @@ void NodeActor::loop_start_stop_peers() {
 | 
			
		|||
          }
 | 
			
		||||
        }
 | 
			
		||||
        state->node_ready_parts_.add_elements(std::move(node_ready_parts));
 | 
			
		||||
        state->torrent_info_str_ = torrent_info_str_;
 | 
			
		||||
        state->torrent_info_ = torrent_info_shared_;
 | 
			
		||||
        state->torrent_info_ready_ = true;
 | 
			
		||||
      } else {
 | 
			
		||||
        state->torrent_info_response_callback_ = [SelfId = actor_id(this)](td::BufferSlice data) {
 | 
			
		||||
| 
						 | 
				
			
			@ -575,6 +597,11 @@ void NodeActor::loop_peer(const PeerId &peer_id, Peer &peer) {
 | 
			
		|||
  for (auto part_id : state->peer_ready_parts_.read()) {
 | 
			
		||||
    parts_helper_.on_peer_part_ready(peer.peer_token, part_id);
 | 
			
		||||
  }
 | 
			
		||||
  if (state->peer_online_set_.load()) {
 | 
			
		||||
    state->peer_online_set_ = false;
 | 
			
		||||
    will_upload_at_ = td::Timestamp::now();
 | 
			
		||||
    loop_will_upload();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Answer queries from peer
 | 
			
		||||
  bool should_notify_peer = false;
 | 
			
		||||
| 
						 | 
				
			
			@ -600,7 +627,7 @@ void NodeActor::loop_peer(const PeerId &peer_id, Peer &peer) {
 | 
			
		|||
      TRY_RESULT(proof_serialized, vm::std_boc_serialize(std::move(proof)));
 | 
			
		||||
      res.proof = std::move(proof_serialized);
 | 
			
		||||
      res.data = td::BufferSlice(std::move(data));
 | 
			
		||||
      td::uint64 size = res.data.size() + res.proof.size();
 | 
			
		||||
      td::uint64 size = res.data.size();
 | 
			
		||||
      upload_speed_.add(size);
 | 
			
		||||
      peer.upload_speed.add(size);
 | 
			
		||||
      return std::move(res);
 | 
			
		||||
| 
						 | 
				
			
			@ -701,10 +728,11 @@ void NodeActor::db_store_torrent() {
 | 
			
		|||
  if (!db_) {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  auto obj = create_tl_object<ton_api::storage_db_torrent>();
 | 
			
		||||
  auto obj = create_tl_object<ton_api::storage_db_torrentV2>();
 | 
			
		||||
  obj->active_download_ = should_download_;
 | 
			
		||||
  obj->active_upload_ = should_upload_;
 | 
			
		||||
  obj->root_dir_ = torrent_.get_root_dir();
 | 
			
		||||
  obj->added_at_ = added_at_;
 | 
			
		||||
  db_->set(create_hash_tl_object<ton_api::storage_db_key_torrent>(torrent_.get_hash()), serialize_tl_object(obj, true),
 | 
			
		||||
           [](td::Result<td::Unit> R) {
 | 
			
		||||
             if (R.is_error()) {
 | 
			
		||||
| 
						 | 
				
			
			@ -823,16 +851,18 @@ void NodeActor::db_update_pieces_list() {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
void NodeActor::load_from_db(std::shared_ptr<db::DbType> db, td::Bits256 hash, td::unique_ptr<Callback> callback,
 | 
			
		||||
                             td::unique_ptr<NodeCallback> node_callback,
 | 
			
		||||
                             td::unique_ptr<NodeCallback> node_callback, SpeedLimiters speed_limiters,
 | 
			
		||||
                             td::Promise<td::actor::ActorOwn<NodeActor>> promise) {
 | 
			
		||||
  class Loader : public td::actor::Actor {
 | 
			
		||||
   public:
 | 
			
		||||
    Loader(std::shared_ptr<db::DbType> db, td::Bits256 hash, td::unique_ptr<Callback> callback,
 | 
			
		||||
           td::unique_ptr<NodeCallback> node_callback, td::Promise<td::actor::ActorOwn<NodeActor>> promise)
 | 
			
		||||
           td::unique_ptr<NodeCallback> node_callback, SpeedLimiters speed_limiters,
 | 
			
		||||
           td::Promise<td::actor::ActorOwn<NodeActor>> promise)
 | 
			
		||||
        : db_(std::move(db))
 | 
			
		||||
        , hash_(hash)
 | 
			
		||||
        , callback_(std::move(callback))
 | 
			
		||||
        , node_callback_(std::move(node_callback))
 | 
			
		||||
        , speed_limiters_(std::move(speed_limiters))
 | 
			
		||||
        , promise_(std::move(promise)) {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -842,9 +872,9 @@ void NodeActor::load_from_db(std::shared_ptr<db::DbType> db, td::Bits256 hash, t
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
    void start_up() override {
 | 
			
		||||
      db::db_get<ton_api::storage_db_torrent>(
 | 
			
		||||
      db::db_get<ton_api::storage_db_TorrentShort>(
 | 
			
		||||
          *db_, create_hash_tl_object<ton_api::storage_db_key_torrent>(hash_), false,
 | 
			
		||||
          [SelfId = actor_id(this)](td::Result<tl_object_ptr<ton_api::storage_db_torrent>> R) {
 | 
			
		||||
          [SelfId = actor_id(this)](td::Result<tl_object_ptr<ton_api::storage_db_TorrentShort>> R) {
 | 
			
		||||
            if (R.is_error()) {
 | 
			
		||||
              td::actor::send_closure(SelfId, &Loader::finish, R.move_as_error_prefix("Torrent: "));
 | 
			
		||||
            } else {
 | 
			
		||||
| 
						 | 
				
			
			@ -853,10 +883,20 @@ void NodeActor::load_from_db(std::shared_ptr<db::DbType> db, td::Bits256 hash, t
 | 
			
		|||
          });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void got_torrent(tl_object_ptr<ton_api::storage_db_torrent> obj) {
 | 
			
		||||
      root_dir_ = std::move(obj->root_dir_);
 | 
			
		||||
      active_download_ = obj->active_download_;
 | 
			
		||||
      active_upload_ = obj->active_upload_;
 | 
			
		||||
    void got_torrent(tl_object_ptr<ton_api::storage_db_TorrentShort> obj) {
 | 
			
		||||
      ton_api::downcast_call(*obj, td::overloaded(
 | 
			
		||||
                                       [&](ton_api::storage_db_torrent &t) {
 | 
			
		||||
                                         root_dir_ = std::move(t.root_dir_);
 | 
			
		||||
                                         active_download_ = t.active_download_;
 | 
			
		||||
                                         active_upload_ = t.active_upload_;
 | 
			
		||||
                                         added_at_ = (td::uint32)td::Clocks::system();
 | 
			
		||||
                                       },
 | 
			
		||||
                                       [&](ton_api::storage_db_torrentV2 &t) {
 | 
			
		||||
                                         root_dir_ = std::move(t.root_dir_);
 | 
			
		||||
                                         active_download_ = t.active_download_;
 | 
			
		||||
                                         active_upload_ = t.active_upload_;
 | 
			
		||||
                                         added_at_ = t.added_at_;
 | 
			
		||||
                                       }));
 | 
			
		||||
      db_->get(create_hash_tl_object<ton_api::storage_db_key_torrentMeta>(hash_),
 | 
			
		||||
               [SelfId = actor_id(this)](td::Result<db::DbType::GetResult> R) {
 | 
			
		||||
                 if (R.is_error()) {
 | 
			
		||||
| 
						 | 
				
			
			@ -980,9 +1020,10 @@ void NodeActor::load_from_db(std::shared_ptr<db::DbType> db, td::Bits256 hash, t
 | 
			
		|||
      DbInitialData data;
 | 
			
		||||
      data.priorities = std::move(priorities_);
 | 
			
		||||
      data.pieces_in_db = std::move(pieces_in_db_);
 | 
			
		||||
      data.added_at = added_at_;
 | 
			
		||||
      finish(td::actor::create_actor<NodeActor>("Node", 1, torrent_.unwrap(), std::move(callback_),
 | 
			
		||||
                                                std::move(node_callback_), std::move(db_), active_download_,
 | 
			
		||||
                                                active_upload_, std::move(data)));
 | 
			
		||||
                                                std::move(node_callback_), std::move(db_), std::move(speed_limiters_),
 | 
			
		||||
                                                active_download_, active_upload_, std::move(data)));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
   private:
 | 
			
		||||
| 
						 | 
				
			
			@ -990,18 +1031,20 @@ void NodeActor::load_from_db(std::shared_ptr<db::DbType> db, td::Bits256 hash, t
 | 
			
		|||
    td::Bits256 hash_;
 | 
			
		||||
    td::unique_ptr<Callback> callback_;
 | 
			
		||||
    td::unique_ptr<NodeCallback> node_callback_;
 | 
			
		||||
    SpeedLimiters speed_limiters_;
 | 
			
		||||
    td::Promise<td::actor::ActorOwn<NodeActor>> promise_;
 | 
			
		||||
 | 
			
		||||
    std::string root_dir_;
 | 
			
		||||
    bool active_download_{false};
 | 
			
		||||
    bool active_upload_{false};
 | 
			
		||||
    td::uint32 added_at_;
 | 
			
		||||
    td::optional<Torrent> torrent_;
 | 
			
		||||
    std::vector<PendingSetFilePriority> priorities_;
 | 
			
		||||
    std::set<td::uint64> pieces_in_db_;
 | 
			
		||||
    size_t remaining_pieces_in_db_ = 0;
 | 
			
		||||
  };
 | 
			
		||||
  td::actor::create_actor<Loader>("loader", std::move(db), hash, std::move(callback), std::move(node_callback),
 | 
			
		||||
                                  std::move(promise))
 | 
			
		||||
                                  std::move(speed_limiters), std::move(promise))
 | 
			
		||||
      .release();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -23,6 +23,7 @@
 | 
			
		|||
#include "PartsHelper.h"
 | 
			
		||||
#include "PeerActor.h"
 | 
			
		||||
#include "Torrent.h"
 | 
			
		||||
#include "SpeedLimiter.h"
 | 
			
		||||
 | 
			
		||||
#include "td/utils/Random.h"
 | 
			
		||||
#include "td/utils/Variant.h"
 | 
			
		||||
| 
						 | 
				
			
			@ -31,6 +32,7 @@
 | 
			
		|||
#include "db.h"
 | 
			
		||||
 | 
			
		||||
namespace ton {
 | 
			
		||||
 | 
			
		||||
class NodeActor : public td::actor::Actor {
 | 
			
		||||
 public:
 | 
			
		||||
  class NodeCallback {
 | 
			
		||||
| 
						 | 
				
			
			@ -60,14 +62,15 @@ class NodeActor : public td::actor::Actor {
 | 
			
		|||
  struct DbInitialData {
 | 
			
		||||
    std::vector<PendingSetFilePriority> priorities;
 | 
			
		||||
    std::set<td::uint64> pieces_in_db;
 | 
			
		||||
    td::uint32 added_at;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  NodeActor(PeerId self_id, ton::Torrent torrent, td::unique_ptr<Callback> callback,
 | 
			
		||||
            td::unique_ptr<NodeCallback> node_callback, std::shared_ptr<db::DbType> db, bool should_download = true,
 | 
			
		||||
            bool should_upload = true);
 | 
			
		||||
            td::unique_ptr<NodeCallback> node_callback, std::shared_ptr<db::DbType> db, SpeedLimiters speed_limiters,
 | 
			
		||||
            bool should_download = true, bool should_upload = true);
 | 
			
		||||
  NodeActor(PeerId self_id, ton::Torrent torrent, td::unique_ptr<Callback> callback,
 | 
			
		||||
            td::unique_ptr<NodeCallback> node_callback, std::shared_ptr<db::DbType> db, bool should_download,
 | 
			
		||||
            bool should_upload, DbInitialData db_initial_data);
 | 
			
		||||
            td::unique_ptr<NodeCallback> node_callback, std::shared_ptr<db::DbType> db, SpeedLimiters speed_limiters,
 | 
			
		||||
            bool should_download, bool should_upload, DbInitialData db_initial_data);
 | 
			
		||||
  void start_peer(PeerId peer_id, td::Promise<td::actor::ActorId<PeerActor>> promise);
 | 
			
		||||
 | 
			
		||||
  struct NodeState {
 | 
			
		||||
| 
						 | 
				
			
			@ -76,11 +79,12 @@ class NodeActor : public td::actor::Actor {
 | 
			
		|||
    bool active_upload;
 | 
			
		||||
    double download_speed;
 | 
			
		||||
    double upload_speed;
 | 
			
		||||
    td::uint32 added_at;
 | 
			
		||||
    const std::vector<td::uint8> &file_priority;
 | 
			
		||||
  };
 | 
			
		||||
  void with_torrent(td::Promise<NodeState> promise) {
 | 
			
		||||
    promise.set_value(NodeState{torrent_, should_download_, should_upload_, download_speed_.speed(),
 | 
			
		||||
                                upload_speed_.speed(), file_priority_});
 | 
			
		||||
                                upload_speed_.speed(), added_at_, file_priority_});
 | 
			
		||||
  }
 | 
			
		||||
  std::string get_stats_str();
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -98,20 +102,22 @@ class NodeActor : public td::actor::Actor {
 | 
			
		|||
  void get_peers_info(td::Promise<tl_object_ptr<ton_api::storage_daemon_peerList>> promise);
 | 
			
		||||
 | 
			
		||||
  static void load_from_db(std::shared_ptr<db::DbType> db, td::Bits256 hash, td::unique_ptr<Callback> callback,
 | 
			
		||||
                           td::unique_ptr<NodeCallback> node_callback,
 | 
			
		||||
                           td::unique_ptr<NodeCallback> node_callback, SpeedLimiters speed_limiters,
 | 
			
		||||
                           td::Promise<td::actor::ActorOwn<NodeActor>> promise);
 | 
			
		||||
  static void cleanup_db(std::shared_ptr<db::DbType> db, td::Bits256 hash, td::Promise<td::Unit> promise);
 | 
			
		||||
 | 
			
		||||
 private:
 | 
			
		||||
  PeerId self_id_;
 | 
			
		||||
  ton::Torrent torrent_;
 | 
			
		||||
  std::shared_ptr<td::BufferSlice> torrent_info_str_;
 | 
			
		||||
  std::shared_ptr<TorrentInfo> torrent_info_shared_;
 | 
			
		||||
  std::vector<td::uint8> file_priority_;
 | 
			
		||||
  td::unique_ptr<Callback> callback_;
 | 
			
		||||
  td::unique_ptr<NodeCallback> node_callback_;
 | 
			
		||||
  std::shared_ptr<db::DbType> db_;
 | 
			
		||||
  bool should_download_{false};
 | 
			
		||||
  bool should_upload_{false};
 | 
			
		||||
  td::uint32 added_at_{0};
 | 
			
		||||
  SpeedLimiters speed_limiters_;
 | 
			
		||||
 | 
			
		||||
  class Notifier : public td::actor::Actor {
 | 
			
		||||
   public:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -24,6 +24,7 @@
 | 
			
		|||
 | 
			
		||||
#include "td/utils/overloaded.h"
 | 
			
		||||
#include "td/utils/Random.h"
 | 
			
		||||
#include "vm/boc.h"
 | 
			
		||||
 | 
			
		||||
namespace ton {
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -44,7 +45,7 @@ PeerActor::PeerActor(td::unique_ptr<Callback> callback, std::shared_ptr<PeerStat
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
template <class T, class... ArgsT>
 | 
			
		||||
td::uint64 PeerActor::create_and_send_query(ArgsT &&... args) {
 | 
			
		||||
td::uint64 PeerActor::create_and_send_query(ArgsT &&...args) {
 | 
			
		||||
  return send_query(ton::create_serialize_tl_object<T>(std::forward<ArgsT>(args)...));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -86,7 +87,9 @@ void PeerActor::on_ping_result(td::Result<td::BufferSlice> r_answer) {
 | 
			
		|||
 | 
			
		||||
void PeerActor::on_pong() {
 | 
			
		||||
  wait_pong_till_ = td::Timestamp::in(10);
 | 
			
		||||
  state_->peer_online_ = true;
 | 
			
		||||
  if (!state_->peer_online_.exchange(true)) {
 | 
			
		||||
    state_->peer_online_set_ = true;
 | 
			
		||||
  }
 | 
			
		||||
  notify_node();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -115,6 +118,11 @@ void PeerActor::on_get_piece_result(PartId piece_id, td::Result<td::BufferSlice>
 | 
			
		|||
    res.proof = std::move(piece->proof_);
 | 
			
		||||
    return std::move(res);
 | 
			
		||||
  }();
 | 
			
		||||
  if (res.is_error()) {
 | 
			
		||||
    LOG(DEBUG) << "getPiece " << piece_id << "query: " << res.error();
 | 
			
		||||
  } else {
 | 
			
		||||
    LOG(DEBUG) << "getPiece " << piece_id << "query: OK";
 | 
			
		||||
  }
 | 
			
		||||
  state_->node_queries_results_.add_element(std::make_pair(piece_id, std::move(res)));
 | 
			
		||||
  notify_node();
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -130,13 +138,16 @@ void PeerActor::on_get_info_result(td::Result<td::BufferSlice> r_answer) {
 | 
			
		|||
  next_get_info_at_ = td::Timestamp::in(5.0);
 | 
			
		||||
  alarm_timestamp().relax(next_get_info_at_);
 | 
			
		||||
  if (r_answer.is_error()) {
 | 
			
		||||
    LOG(DEBUG) << "getTorrentInfo query: " << r_answer.move_as_error();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  auto R = fetch_tl_object<ton::ton_api::storage_torrentInfo>(r_answer.move_as_ok(), true);
 | 
			
		||||
  if (R.is_error()) {
 | 
			
		||||
    LOG(DEBUG) << "getTorrentInfo query: " << R.move_as_error();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  td::BufferSlice data = std::move(R.ok_ref()->data_);
 | 
			
		||||
  LOG(DEBUG) << "getTorrentInfo query: got result (" << data.size() << " bytes)";
 | 
			
		||||
  if (!data.empty() && !state_->torrent_info_ready_) {
 | 
			
		||||
    state_->torrent_info_response_callback_(std::move(data));
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			@ -245,6 +256,8 @@ void PeerActor::loop_update_init() {
 | 
			
		|||
  update_state_query_.state = node_state;
 | 
			
		||||
  update_state_query_.query_id = 0;
 | 
			
		||||
 | 
			
		||||
  LOG(DEBUG) << "Sending updateInit query (" << update_state_query_.state.want_download << ", "
 | 
			
		||||
             << update_state_query_.state.will_upload << ", offset=" << peer_init_offset_ * 8 << ")";
 | 
			
		||||
  update_query_id_ = send_query(std::move(query));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -265,13 +278,15 @@ void PeerActor::loop_update_state() {
 | 
			
		|||
 | 
			
		||||
  auto query = create_update_query(
 | 
			
		||||
      ton::create_tl_object<ton::ton_api::storage_updateState>(to_ton_api(update_state_query_.state)));
 | 
			
		||||
  LOG(DEBUG) << "Sending updateState query (" << update_state_query_.state.want_download << ", "
 | 
			
		||||
             << update_state_query_.state.will_upload << ")";
 | 
			
		||||
  update_state_query_.query_id = send_query(std::move(query));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void PeerActor::update_have_pieces() {
 | 
			
		||||
  auto node_ready_parts = state_->node_ready_parts_.read();
 | 
			
		||||
  for (auto piece_id : node_ready_parts) {
 | 
			
		||||
    if (piece_id < peer_init_offset_ + UPDATE_INIT_BLOCK_SIZE) {
 | 
			
		||||
    if (piece_id < (peer_init_offset_ + UPDATE_INIT_BLOCK_SIZE) * 8 && !have_pieces_.get(piece_id)) {
 | 
			
		||||
      have_pieces_list_.push_back(piece_id);
 | 
			
		||||
    }
 | 
			
		||||
    have_pieces_.set_one(piece_id);
 | 
			
		||||
| 
						 | 
				
			
			@ -291,23 +306,49 @@ void PeerActor::loop_update_pieces() {
 | 
			
		|||
    have_pieces_list_.erase(have_pieces_list_.end() - count, have_pieces_list_.end());
 | 
			
		||||
    auto query = create_update_query(ton::create_tl_object<ton::ton_api::storage_updateHavePieces>(
 | 
			
		||||
        td::transform(sent_have_pieces_list_, [](auto x) { return static_cast<td::int32>(x); })));
 | 
			
		||||
    LOG(DEBUG) << "Sending updateHavePieces query (" << sent_have_pieces_list_.size() << " pieces)";
 | 
			
		||||
    update_query_id_ = send_query(std::move(query));
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void PeerActor::loop_get_torrent_info() {
 | 
			
		||||
  if (get_info_query_id_ || state_->torrent_info_ready_) {
 | 
			
		||||
  if (state_->torrent_info_ready_) {
 | 
			
		||||
    if (!torrent_info_) {
 | 
			
		||||
      torrent_info_ = state_->torrent_info_;
 | 
			
		||||
      for (const auto &u : pending_update_peer_parts_) {
 | 
			
		||||
        process_update_peer_parts(u);
 | 
			
		||||
      }
 | 
			
		||||
      pending_update_peer_parts_.clear();
 | 
			
		||||
    }
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  if (get_info_query_id_) {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  if (next_get_info_at_ && !next_get_info_at_.is_in_past()) {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  LOG(DEBUG) << "Sending getTorrentInfo query";
 | 
			
		||||
  get_info_query_id_ = create_and_send_query<ton::ton_api::storage_getTorrentInfo>();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void PeerActor::loop_node_get_piece() {
 | 
			
		||||
  for (auto part : state_->node_queries_.read()) {
 | 
			
		||||
    if (state_->speed_limiters_.download.empty()) {
 | 
			
		||||
      node_get_piece_.emplace(part, NodePieceQuery{});
 | 
			
		||||
    } else {
 | 
			
		||||
      if (!torrent_info_) {
 | 
			
		||||
        CHECK(state_->torrent_info_ready_);
 | 
			
		||||
        loop_get_torrent_info();
 | 
			
		||||
      }
 | 
			
		||||
      auto piece_size =
 | 
			
		||||
          std::min<td::uint64>(torrent_info_->piece_size, torrent_info_->file_size - part * torrent_info_->piece_size);
 | 
			
		||||
      td::actor::send_closure(state_->speed_limiters_.download, &SpeedLimiter::enqueue, (double)piece_size,
 | 
			
		||||
                              td::Timestamp::in(3.0), [part, SelfId = actor_id(this)](td::Result<td::Unit> R) {
 | 
			
		||||
                                td::actor::send_closure(SelfId, &PeerActor::node_get_piece_query_ready, part,
 | 
			
		||||
                                                        std::move(R));
 | 
			
		||||
                              });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  for (auto &query_it : node_get_piece_) {
 | 
			
		||||
| 
						 | 
				
			
			@ -315,11 +356,21 @@ void PeerActor::loop_node_get_piece() {
 | 
			
		|||
      continue;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    LOG(DEBUG) << "Sending getPiece " << query_it.first << " query";
 | 
			
		||||
    query_it.second.query_id =
 | 
			
		||||
        create_and_send_query<ton::ton_api::storage_getPiece>(static_cast<td::int32>(query_it.first));
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void PeerActor::node_get_piece_query_ready(PartId part, td::Result<td::Unit> R) {
 | 
			
		||||
  if (R.is_error()) {
 | 
			
		||||
    on_get_piece_result(part, R.move_as_error());
 | 
			
		||||
  } else {
 | 
			
		||||
    node_get_piece_.emplace(part, NodePieceQuery{});
 | 
			
		||||
  }
 | 
			
		||||
  schedule_loop();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void PeerActor::loop_peer_get_piece() {
 | 
			
		||||
  // process answers
 | 
			
		||||
  for (auto &p : state_->peer_queries_results_.read()) {
 | 
			
		||||
| 
						 | 
				
			
			@ -328,9 +379,26 @@ void PeerActor::loop_peer_get_piece() {
 | 
			
		|||
    if (promise_it == peer_get_piece_.end()) {
 | 
			
		||||
      continue;
 | 
			
		||||
    }
 | 
			
		||||
    promise_it->second.promise.set_result(p.second.move_map([](PeerState::Part part) {
 | 
			
		||||
      return ton::create_serialize_tl_object<ton::ton_api::storage_piece>(std::move(part.proof), std::move(part.data));
 | 
			
		||||
    td::Promise<PeerState::Part> promise =
 | 
			
		||||
        [i = p.first, promise = std::move(promise_it->second.promise)](td::Result<PeerState::Part> R) mutable {
 | 
			
		||||
          LOG(DEBUG) << "Responding to getPiece " << i << ": " << (R.is_ok() ? "OK" : R.error().to_string());
 | 
			
		||||
          promise.set_result(R.move_map([](PeerState::Part part) {
 | 
			
		||||
            return create_serialize_tl_object<ton_api::storage_piece>(std::move(part.proof), std::move(part.data));
 | 
			
		||||
          }));
 | 
			
		||||
        };
 | 
			
		||||
    if (p.second.is_error()) {
 | 
			
		||||
      promise.set_error(p.second.move_as_error());
 | 
			
		||||
    } else {
 | 
			
		||||
      auto part = p.second.move_as_ok();
 | 
			
		||||
      auto size = (double)part.data.size();
 | 
			
		||||
      td::Promise<td::Unit> P = promise.wrap([part = std::move(part)](td::Unit) mutable { return std::move(part); });
 | 
			
		||||
      if (state_->speed_limiters_.upload.empty()) {
 | 
			
		||||
        P.set_result(td::Unit());
 | 
			
		||||
      } else {
 | 
			
		||||
        td::actor::send_closure(state_->speed_limiters_.upload, &SpeedLimiter::enqueue, size, td::Timestamp::in(3.0),
 | 
			
		||||
                                std::move(P));
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    peer_get_piece_.erase(promise_it);
 | 
			
		||||
    notify_node();
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			@ -373,18 +441,8 @@ void PeerActor::execute_add_update(ton::ton_api::storage_addUpdate &add_update,
 | 
			
		|||
    promise.set_error(td::Status::Error(404, "INVALID_SESSION"));
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  promise.set_value(ton::create_serialize_tl_object<ton::ton_api::storage_ok>());
 | 
			
		||||
 | 
			
		||||
  std::vector<td::uint32> new_peer_ready_parts;
 | 
			
		||||
  auto add_piece = [&](PartId id) {
 | 
			
		||||
    if (!peer_have_pieces_.get(id)) {
 | 
			
		||||
      peer_have_pieces_.set_one(id);
 | 
			
		||||
      new_peer_ready_parts.push_back(id);
 | 
			
		||||
      notify_node();
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  auto seqno = static_cast<td::uint32>(add_update.seqno_);
 | 
			
		||||
  auto update_peer_state = [&](PeerState::State peer_state) {
 | 
			
		||||
    if (peer_seqno_ >= seqno) {
 | 
			
		||||
| 
						 | 
				
			
			@ -393,22 +451,48 @@ void PeerActor::execute_add_update(ton::ton_api::storage_addUpdate &add_update,
 | 
			
		|||
    if (state_->peer_state_ready_ && state_->peer_state_.load() == peer_state) {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    LOG(DEBUG) << "Processing update peer state query (" << peer_state.want_download << ", " << peer_state.will_upload
 | 
			
		||||
               << ")";
 | 
			
		||||
    peer_seqno_ = seqno;
 | 
			
		||||
    state_->peer_state_.exchange(peer_state);
 | 
			
		||||
    state_->peer_state_ready_ = true;
 | 
			
		||||
    notify_node();
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  downcast_call(*add_update.update_,
 | 
			
		||||
  downcast_call(
 | 
			
		||||
      *add_update.update_,
 | 
			
		||||
      td::overloaded(
 | 
			
		||||
                    [&](ton::ton_api::storage_updateHavePieces &have_pieces) {
 | 
			
		||||
          [&](const ton::ton_api::storage_updateHavePieces &have_pieces) {},
 | 
			
		||||
          [&](const ton::ton_api::storage_updateState &state) { update_peer_state(from_ton_api(*state.state_)); },
 | 
			
		||||
          [&](const ton::ton_api::storage_updateInit &init) { update_peer_state(from_ton_api(*init.state_)); }));
 | 
			
		||||
  if (torrent_info_) {
 | 
			
		||||
    process_update_peer_parts(add_update.update_);
 | 
			
		||||
  } else {
 | 
			
		||||
    pending_update_peer_parts_.push_back(std::move(add_update.update_));
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void PeerActor::process_update_peer_parts(const tl_object_ptr<ton_api::storage_Update> &update) {
 | 
			
		||||
  CHECK(torrent_info_);
 | 
			
		||||
  td::uint64 pieces_count = torrent_info_->pieces_count();
 | 
			
		||||
  std::vector<td::uint32> new_peer_ready_parts;
 | 
			
		||||
  auto add_piece = [&](PartId id) {
 | 
			
		||||
    if (id < pieces_count && !peer_have_pieces_.get(id)) {
 | 
			
		||||
      peer_have_pieces_.set_one(id);
 | 
			
		||||
      new_peer_ready_parts.push_back(id);
 | 
			
		||||
      notify_node();
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
  downcast_call(*update,
 | 
			
		||||
                td::overloaded(
 | 
			
		||||
                    [&](const ton::ton_api::storage_updateHavePieces &have_pieces) {
 | 
			
		||||
                      LOG(DEBUG) << "Processing updateHavePieces query (" << have_pieces.piece_id_ << " pieces)";
 | 
			
		||||
                      for (auto id : have_pieces.piece_id_) {
 | 
			
		||||
                        add_piece(id);
 | 
			
		||||
                      }
 | 
			
		||||
                    },
 | 
			
		||||
                    [&](ton::ton_api::storage_updateState &state) { update_peer_state(from_ton_api(*state.state_)); },
 | 
			
		||||
                    [&](ton::ton_api::storage_updateInit &init) {
 | 
			
		||||
                      update_peer_state(from_ton_api(*init.state_));
 | 
			
		||||
                    [&](const ton::ton_api::storage_updateState &state) {},
 | 
			
		||||
                    [&](const ton::ton_api::storage_updateInit &init) {
 | 
			
		||||
                      LOG(DEBUG) << "Processing updateInit query (offset=" << init.have_pieces_offset_ * 8 << ")";
 | 
			
		||||
                      td::Bitset new_bitset;
 | 
			
		||||
                      new_bitset.set_raw(init.have_pieces_.as_slice().str());
 | 
			
		||||
                      size_t offset = init.have_pieces_offset_ * 8;
 | 
			
		||||
| 
						 | 
				
			
			@ -428,7 +512,13 @@ void PeerActor::execute_get_piece(ton::ton_api::storage_getPiece &get_piece, td:
 | 
			
		|||
 | 
			
		||||
void PeerActor::execute_get_torrent_info(td::Promise<td::BufferSlice> promise) {
 | 
			
		||||
  td::BufferSlice result = create_serialize_tl_object<ton_api::storage_torrentInfo>(
 | 
			
		||||
      state_->torrent_info_ready_ ? state_->torrent_info_str_->clone() : td::BufferSlice());
 | 
			
		||||
      state_->torrent_info_ready_ ? vm::std_boc_serialize(state_->torrent_info_->as_cell()).move_as_ok()
 | 
			
		||||
                                  : td::BufferSlice());
 | 
			
		||||
  if (state_->torrent_info_ready_) {
 | 
			
		||||
    LOG(DEBUG) << "Responding to getTorrentInfo: " << result.size() << " bytes";
 | 
			
		||||
  } else {
 | 
			
		||||
    LOG(DEBUG) << "Responding to getTorrentInfo: no info";
 | 
			
		||||
  }
 | 
			
		||||
  promise.set_result(std::move(result));
 | 
			
		||||
}
 | 
			
		||||
}  // namespace ton
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -59,6 +59,10 @@ class PeerActor : public td::actor::Actor {
 | 
			
		|||
  // startSession
 | 
			
		||||
  td::uint64 node_session_id_;
 | 
			
		||||
  td::Bitset peer_have_pieces_;
 | 
			
		||||
  std::shared_ptr<TorrentInfo> torrent_info_;
 | 
			
		||||
  std::vector<tl_object_ptr<ton_api::storage_Update>> pending_update_peer_parts_;
 | 
			
		||||
 | 
			
		||||
  void process_update_peer_parts(const tl_object_ptr<ton_api::storage_Update> &update);
 | 
			
		||||
 | 
			
		||||
  // update
 | 
			
		||||
  td::optional<td::uint64> peer_session_id_;
 | 
			
		||||
| 
						 | 
				
			
			@ -112,6 +116,7 @@ class PeerActor : public td::actor::Actor {
 | 
			
		|||
  td::BufferSlice create_update_query(ton::tl_object_ptr<ton::ton_api::storage_Update> update);
 | 
			
		||||
 | 
			
		||||
  void loop_node_get_piece();
 | 
			
		||||
  void node_get_piece_query_ready(PartId part, td::Result<td::Unit> R);
 | 
			
		||||
 | 
			
		||||
  void loop_peer_get_piece();
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -23,6 +23,8 @@
 | 
			
		|||
#include "td/utils/optional.h"
 | 
			
		||||
 | 
			
		||||
#include "td/actor/actor.h"
 | 
			
		||||
#include "TorrentInfo.h"
 | 
			
		||||
#include "SpeedLimiter.h"
 | 
			
		||||
 | 
			
		||||
#include <map>
 | 
			
		||||
#include <atomic>
 | 
			
		||||
| 
						 | 
				
			
			@ -100,6 +102,8 @@ struct PeerState {
 | 
			
		|||
  std::atomic_bool peer_state_ready_{false};
 | 
			
		||||
  std::atomic<State> peer_state_{State{false, false}};
 | 
			
		||||
  std::atomic_bool peer_online_{false};
 | 
			
		||||
  std::atomic_bool peer_online_set_{false};
 | 
			
		||||
  SpeedLimiters speed_limiters_;
 | 
			
		||||
 | 
			
		||||
  struct Part {
 | 
			
		||||
    td::BufferSlice proof;
 | 
			
		||||
| 
						 | 
				
			
			@ -121,7 +125,7 @@ struct PeerState {
 | 
			
		|||
 | 
			
		||||
  // Node -> Peer
 | 
			
		||||
  std::atomic_bool torrent_info_ready_{false};
 | 
			
		||||
  std::shared_ptr<td::BufferSlice> torrent_info_str_;
 | 
			
		||||
  std::shared_ptr<TorrentInfo> torrent_info_;
 | 
			
		||||
  std::function<void(td::BufferSlice)> torrent_info_response_callback_;
 | 
			
		||||
 | 
			
		||||
  const td::actor::ActorId<> node;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										82
									
								
								storage/SpeedLimiter.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								storage/SpeedLimiter.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,82 @@
 | 
			
		|||
/*
 | 
			
		||||
    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 "SpeedLimiter.h"
 | 
			
		||||
 | 
			
		||||
namespace ton {
 | 
			
		||||
 | 
			
		||||
SpeedLimiter::SpeedLimiter(double max_speed) : max_speed_(max_speed) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void SpeedLimiter::set_max_speed(double max_speed) {
 | 
			
		||||
  max_speed_ = max_speed;
 | 
			
		||||
  auto old_queue = std::move(queue_);
 | 
			
		||||
  unlock_at_ = (queue_.empty() ? td::Timestamp::now() : queue_.front().execute_at_);
 | 
			
		||||
  queue_ = {};
 | 
			
		||||
  while (!old_queue.empty()) {
 | 
			
		||||
    auto &e = old_queue.front();
 | 
			
		||||
    enqueue(e.size_, e.timeout_, std::move(e.promise_));
 | 
			
		||||
    old_queue.pop();
 | 
			
		||||
  }
 | 
			
		||||
  process_queue();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void SpeedLimiter::enqueue(double size, td::Timestamp timeout, td::Promise<td::Unit> promise) {
 | 
			
		||||
  if (max_speed_ < 0.0) {
 | 
			
		||||
    promise.set_result(td::Unit());
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  if (max_speed_ == 0.0) {
 | 
			
		||||
    promise.set_error(td::Status::Error("Speed limit is 0"));
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  if (timeout < unlock_at_) {
 | 
			
		||||
    promise.set_error(td::Status::Error("Timeout caused by speed limit"));
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  if (queue_.empty() && unlock_at_.is_in_past()) {
 | 
			
		||||
    unlock_at_ = td::Timestamp::now();
 | 
			
		||||
    promise.set_result(td::Unit());
 | 
			
		||||
  } else {
 | 
			
		||||
    queue_.push({unlock_at_, size, timeout, std::move(promise)});
 | 
			
		||||
  }
 | 
			
		||||
  unlock_at_ = td::Timestamp::in(size / max_speed_, unlock_at_);
 | 
			
		||||
  if (!queue_.empty()) {
 | 
			
		||||
    alarm_timestamp() = queue_.front().execute_at_;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void SpeedLimiter::alarm() {
 | 
			
		||||
  process_queue();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void SpeedLimiter::process_queue() {
 | 
			
		||||
  while (!queue_.empty()) {
 | 
			
		||||
    auto &e = queue_.front();
 | 
			
		||||
    if (e.execute_at_.is_in_past()) {
 | 
			
		||||
      e.promise_.set_result(td::Unit());
 | 
			
		||||
      queue_.pop();
 | 
			
		||||
    } else {
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  if (!queue_.empty()) {
 | 
			
		||||
    alarm_timestamp() = queue_.front().execute_at_;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace ton
 | 
			
		||||
							
								
								
									
										52
									
								
								storage/SpeedLimiter.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								storage/SpeedLimiter.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,52 @@
 | 
			
		|||
/*
 | 
			
		||||
    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/>.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
#include "td/actor/actor.h"
 | 
			
		||||
#include <queue>
 | 
			
		||||
 | 
			
		||||
namespace ton {
 | 
			
		||||
 | 
			
		||||
class SpeedLimiter : public td::actor::Actor {
 | 
			
		||||
 public:
 | 
			
		||||
  explicit SpeedLimiter(double max_speed);
 | 
			
		||||
 | 
			
		||||
  void set_max_speed(double max_speed);  // Negative = unlimited
 | 
			
		||||
  void enqueue(double size, td::Timestamp timeout, td::Promise<td::Unit> promise);
 | 
			
		||||
 | 
			
		||||
  void alarm() override;
 | 
			
		||||
 | 
			
		||||
 private:
 | 
			
		||||
  double max_speed_ = -1.0;
 | 
			
		||||
  td::Timestamp unlock_at_ = td::Timestamp::never();
 | 
			
		||||
 | 
			
		||||
  struct Event {
 | 
			
		||||
    td::Timestamp execute_at_;
 | 
			
		||||
    double size_;
 | 
			
		||||
    td::Timestamp timeout_;
 | 
			
		||||
    td::Promise<td::Unit> promise_;
 | 
			
		||||
  };
 | 
			
		||||
  std::queue<Event> queue_;
 | 
			
		||||
 | 
			
		||||
  void process_queue();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct SpeedLimiters {
 | 
			
		||||
  td::actor::ActorId<SpeedLimiter> download, upload;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace ton
 | 
			
		||||
| 
						 | 
				
			
			@ -266,7 +266,9 @@ td::Result<std::string> Torrent::get_piece_data(td::uint64 piece_i) {
 | 
			
		|||
  if (!inited_info_) {
 | 
			
		||||
    return td::Status::Error("Torrent info not inited");
 | 
			
		||||
  }
 | 
			
		||||
  CHECK(piece_i < info_.pieces_count());
 | 
			
		||||
  if (piece_i >= info_.pieces_count()) {
 | 
			
		||||
    return td::Status::Error("Piece idx is too big");
 | 
			
		||||
  }
 | 
			
		||||
  if (!piece_is_ready_[piece_i]) {
 | 
			
		||||
    return td::Status::Error("Piece is not ready");
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			@ -291,7 +293,9 @@ td::Result<td::Ref<vm::Cell>> Torrent::get_piece_proof(td::uint64 piece_i) {
 | 
			
		|||
  if (!inited_info_) {
 | 
			
		||||
    return td::Status::Error("Torrent info not inited");
 | 
			
		||||
  }
 | 
			
		||||
  CHECK(piece_i < info_.pieces_count());
 | 
			
		||||
  if (piece_i >= info_.pieces_count()) {
 | 
			
		||||
    return td::Status::Error("Piece idx is too big");
 | 
			
		||||
  }
 | 
			
		||||
  return merkle_tree_.gen_proof(piece_i, piece_i);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -305,7 +309,9 @@ td::Status Torrent::add_piece(td::uint64 piece_i, td::Slice data, td::Ref<vm::Ce
 | 
			
		|||
  if (!proof.is_null()) {
 | 
			
		||||
    TRY_STATUS(merkle_tree_.add_proof(proof));
 | 
			
		||||
  }
 | 
			
		||||
  CHECK(piece_i < info_.pieces_count());
 | 
			
		||||
  if (piece_i >= info_.pieces_count()) {
 | 
			
		||||
    return td::Status::Error("Piece idx is too big");
 | 
			
		||||
  }
 | 
			
		||||
  if (piece_is_ready_[piece_i]) {
 | 
			
		||||
    return td::Status::OK();
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -29,18 +29,22 @@
 | 
			
		|||
#include "TorrentHeader.hpp"
 | 
			
		||||
 | 
			
		||||
namespace ton {
 | 
			
		||||
static bool is_dir_slash(char c) {
 | 
			
		||||
  return (c == TD_DIR_SLASH) | (c == '/');
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
td::Result<Torrent> Torrent::Creator::create_from_path(Options options, td::CSlice raw_path) {
 | 
			
		||||
  TRY_RESULT(path, td::realpath(raw_path));
 | 
			
		||||
  TRY_RESULT(stat, td::stat(path));
 | 
			
		||||
  std::string root_dir = path;
 | 
			
		||||
  while (!root_dir.empty() && root_dir.back() == TD_DIR_SLASH) {
 | 
			
		||||
  while (!root_dir.empty() && is_dir_slash(root_dir.back())) {
 | 
			
		||||
    root_dir.pop_back();
 | 
			
		||||
  }
 | 
			
		||||
  while (!root_dir.empty() && root_dir.back() != TD_DIR_SLASH) {
 | 
			
		||||
  while (!root_dir.empty() && !is_dir_slash(root_dir.back())) {
 | 
			
		||||
    root_dir.pop_back();
 | 
			
		||||
  }
 | 
			
		||||
  if (stat.is_dir_) {
 | 
			
		||||
    if (!path.empty() && path.back() != TD_DIR_SLASH) {
 | 
			
		||||
    if (!path.empty() && !is_dir_slash(path.back())) {
 | 
			
		||||
      path += TD_DIR_SLASH;
 | 
			
		||||
    }
 | 
			
		||||
    if (!options.dir_name) {
 | 
			
		||||
| 
						 | 
				
			
			@ -50,7 +54,20 @@ td::Result<Torrent> Torrent::Creator::create_from_path(Options options, td::CSli
 | 
			
		|||
    td::Status status;
 | 
			
		||||
    auto walk_status = td::WalkPath::run(path, [&](td::CSlice name, td::WalkPath::Type type) {
 | 
			
		||||
      if (type == td::WalkPath::Type::NotDir) {
 | 
			
		||||
        status = creator.add_file(td::PathView::relative(name, path), name);
 | 
			
		||||
        std::string rel_name = td::PathView::relative(name, path).str();
 | 
			
		||||
        td::Slice file_name = rel_name;
 | 
			
		||||
        for (size_t i = 0; i < rel_name.size(); ++i) {
 | 
			
		||||
          if (is_dir_slash(rel_name[i])) {
 | 
			
		||||
            rel_name[i] = '/';
 | 
			
		||||
            file_name = td::Slice(rel_name.c_str() + i + 1, rel_name.c_str() + rel_name.size());
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
        // Exclude OS-created files that can be modified automatically and thus break some torrent pieces
 | 
			
		||||
        if (file_name == ".DS_Store" || td::to_lower(file_name) == "desktop.ini" ||
 | 
			
		||||
            td::to_lower(file_name) == "thumbs.db") {
 | 
			
		||||
          return td::WalkPath::Action::Continue;
 | 
			
		||||
        }
 | 
			
		||||
        status = creator.add_file(rel_name, name);
 | 
			
		||||
        if (status.is_error()) {
 | 
			
		||||
          return td::WalkPath::Action::Abort;
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -73,6 +73,12 @@ td::Status TorrentInfo::validate() const {
 | 
			
		|||
  if (description.size() > 1024) {
 | 
			
		||||
    return td::Status::Error("Description is too long");
 | 
			
		||||
  }
 | 
			
		||||
  if (piece_size > (1 << 23)) {
 | 
			
		||||
    return td::Status::Error("Piece size is too big");
 | 
			
		||||
  }
 | 
			
		||||
  if (pieces_count() >= (1ULL << 31)) {
 | 
			
		||||
    return td::Status::Error("Too many pieces");
 | 
			
		||||
  }
 | 
			
		||||
  return td::Status::OK();
 | 
			
		||||
}
 | 
			
		||||
}  // namespace ton
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -458,9 +458,9 @@ class StorageCli : public td::actor::Actor {
 | 
			
		|||
    }
 | 
			
		||||
    auto callback = td::make_unique<Callback>(actor_id(this), ptr->id, std::move(on_completed));
 | 
			
		||||
    auto context = PeerManager::create_callback(ptr->peer_manager.get());
 | 
			
		||||
    ptr->node =
 | 
			
		||||
        td::actor::create_actor<ton::NodeActor>(PSLICE() << "Node#" << self_id, self_id, ptr->torrent.unwrap(),
 | 
			
		||||
                                                std::move(callback), std::move(context), nullptr, should_download);
 | 
			
		||||
    ptr->node = td::actor::create_actor<ton::NodeActor>(PSLICE() << "Node#" << self_id, self_id, ptr->torrent.unwrap(),
 | 
			
		||||
                                                        std::move(callback), std::move(context), nullptr,
 | 
			
		||||
                                                        ton::SpeedLimiters{}, should_download);
 | 
			
		||||
    td::TerminalIO::out() << "Torrent #" << ptr->id << " started\n";
 | 
			
		||||
    promise.release().release();
 | 
			
		||||
    if (promise) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -50,6 +50,29 @@ void StorageManager::start_up() {
 | 
			
		|||
 | 
			
		||||
  db_ = std::make_shared<db::DbType>(
 | 
			
		||||
      std::make_shared<td::RocksDb>(td::RocksDb::open(db_root_ + "/torrent-db").move_as_ok()));
 | 
			
		||||
 | 
			
		||||
  db::db_get<ton_api::storage_db_config>(
 | 
			
		||||
      *db_, create_hash_tl_object<ton_api::storage_db_key_config>(), true,
 | 
			
		||||
      [SelfId = actor_id(this)](td::Result<tl_object_ptr<ton_api::storage_db_config>> R) {
 | 
			
		||||
        if (R.is_error()) {
 | 
			
		||||
          LOG(ERROR) << "Failed to load config from db: " << R.move_as_error();
 | 
			
		||||
          td::actor::send_closure(SelfId, &StorageManager::loaded_config_from_db, nullptr);
 | 
			
		||||
        } else {
 | 
			
		||||
          td::actor::send_closure(SelfId, &StorageManager::loaded_config_from_db, R.move_as_ok());
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void StorageManager::loaded_config_from_db(tl_object_ptr<ton_api::storage_db_config> config) {
 | 
			
		||||
  if (config) {
 | 
			
		||||
    LOG(INFO) << "Loaded config from DB. Speed limits: download=" << config->download_speed_limit_
 | 
			
		||||
              << ", upload=" << config->upload_speed_limit_;
 | 
			
		||||
    download_speed_limit_ = config->download_speed_limit_;
 | 
			
		||||
    upload_speed_limit_ = config->upload_speed_limit_;
 | 
			
		||||
    td::actor::send_closure(download_speed_limiter_, &SpeedLimiter::set_max_speed, download_speed_limit_);
 | 
			
		||||
    td::actor::send_closure(upload_speed_limiter_, &SpeedLimiter::set_max_speed, upload_speed_limit_);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  db::db_get<ton_api::storage_db_torrentList>(
 | 
			
		||||
      *db_, create_hash_tl_object<ton_api::storage_db_key_torrentList>(), true,
 | 
			
		||||
      [SelfId = actor_id(this)](td::Result<tl_object_ptr<ton_api::storage_db_torrentList>> R) {
 | 
			
		||||
| 
						 | 
				
			
			@ -79,6 +102,7 @@ void StorageManager::load_torrents_from_db(std::vector<td::Bits256> torrents) {
 | 
			
		|||
                                                              client_mode_, overlays_, adnl_, rldp_);
 | 
			
		||||
    NodeActor::load_from_db(
 | 
			
		||||
        db_, hash, create_callback(hash, entry.closing_state), PeerManager::create_callback(entry.peer_manager.get()),
 | 
			
		||||
        SpeedLimiters{download_speed_limiter_.get(), upload_speed_limiter_.get()},
 | 
			
		||||
        [SelfId = actor_id(this), hash,
 | 
			
		||||
         promise = ig.get_promise()](td::Result<td::actor::ActorOwn<NodeActor>> R) mutable {
 | 
			
		||||
          td::actor::send_closure(SelfId, &StorageManager::loaded_torrent_from_db, hash, std::move(R));
 | 
			
		||||
| 
						 | 
				
			
			@ -162,9 +186,9 @@ td::Status StorageManager::add_torrent_impl(Torrent torrent, bool start_download
 | 
			
		|||
                                                            client_mode_, overlays_, adnl_, rldp_);
 | 
			
		||||
  auto context = PeerManager::create_callback(entry.peer_manager.get());
 | 
			
		||||
  LOG(INFO) << "Added torrent " << hash.to_hex() << " , root_dir = " << torrent.get_root_dir();
 | 
			
		||||
  entry.actor =
 | 
			
		||||
      td::actor::create_actor<NodeActor>("Node", 1, std::move(torrent), create_callback(hash, entry.closing_state),
 | 
			
		||||
                                         std::move(context), db_, start_download, allow_upload);
 | 
			
		||||
  entry.actor = td::actor::create_actor<NodeActor>(
 | 
			
		||||
      "Node", 1, std::move(torrent), create_callback(hash, entry.closing_state), std::move(context), db_,
 | 
			
		||||
      SpeedLimiters{download_speed_limiter_.get(), upload_speed_limiter_.get()}, start_download, allow_upload);
 | 
			
		||||
  return td::Status::OK();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -210,6 +234,18 @@ void StorageManager::get_all_torrents(td::Promise<std::vector<td::Bits256>> prom
 | 
			
		|||
  promise.set_result(std::move(result));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void StorageManager::db_store_config() {
 | 
			
		||||
  auto config = create_tl_object<ton_api::storage_db_config>();
 | 
			
		||||
  config->download_speed_limit_ = download_speed_limit_;
 | 
			
		||||
  config->upload_speed_limit_ = upload_speed_limit_;
 | 
			
		||||
  db_->set(create_hash_tl_object<ton_api::storage_db_key_config>(), serialize_tl_object(config, true),
 | 
			
		||||
           [](td::Result<td::Unit> R) {
 | 
			
		||||
             if (R.is_error()) {
 | 
			
		||||
               LOG(ERROR) << "Failed to save config to db: " << R.move_as_error();
 | 
			
		||||
             }
 | 
			
		||||
           });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void StorageManager::db_store_torrent_list() {
 | 
			
		||||
  std::vector<td::Bits256> torrents;
 | 
			
		||||
  for (const auto& p : torrents_) {
 | 
			
		||||
| 
						 | 
				
			
			@ -259,19 +295,55 @@ void StorageManager::load_from(td::Bits256 hash, td::optional<TorrentMeta> meta,
 | 
			
		|||
                          std::move(promise));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool try_rm_empty_dir(const std::string& path) {
 | 
			
		||||
  auto stat = td::stat(path);
 | 
			
		||||
  if (stat.is_error() || !stat.ok().is_dir_) {
 | 
			
		||||
    return true;
 | 
			
		||||
  }
 | 
			
		||||
  size_t cnt = 0;
 | 
			
		||||
  td::WalkPath::run(path, [&](td::CSlice name, td::WalkPath::Type type) {
 | 
			
		||||
    if (type != td::WalkPath::Type::ExitDir) {
 | 
			
		||||
      ++cnt;
 | 
			
		||||
    }
 | 
			
		||||
    if (cnt < 2) {
 | 
			
		||||
      return td::WalkPath::Action::Continue;
 | 
			
		||||
    } else {
 | 
			
		||||
      return td::WalkPath::Action::Abort;
 | 
			
		||||
    }
 | 
			
		||||
  }).ignore();
 | 
			
		||||
  if (cnt == 1) {
 | 
			
		||||
    td::rmdir(path).ignore();
 | 
			
		||||
    return true;
 | 
			
		||||
  }
 | 
			
		||||
  return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void StorageManager::on_torrent_closed(Torrent torrent, std::shared_ptr<TorrentEntry::ClosingState> closing_state) {
 | 
			
		||||
  if (!closing_state->removing) {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  if (closing_state->remove_files && torrent.inited_header()) {
 | 
			
		||||
    // Ignore all errors: files may just not exist
 | 
			
		||||
    size_t files_count = torrent.get_files_count().unwrap();
 | 
			
		||||
    for (size_t i = 0; i < files_count; ++i) {
 | 
			
		||||
      std::string path = torrent.get_file_path(i);
 | 
			
		||||
      td::unlink(path).ignore();
 | 
			
		||||
      // TODO: Check errors, remove empty directories
 | 
			
		||||
      std::string name = torrent.get_file_name(i).str();
 | 
			
		||||
      for (int j = (int)name.size() - 1; j >= 0; --j) {
 | 
			
		||||
        if (name[j] == '/') {
 | 
			
		||||
          name.resize(j + 1);
 | 
			
		||||
          if (!try_rm_empty_dir(torrent.get_root_dir() + '/' + torrent.get_header().dir_name + '/' + name)) {
 | 
			
		||||
            break;
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
  td::rmrf(db_root_ + "/torrent-files/" + torrent.get_hash().to_hex()).ignore();
 | 
			
		||||
      }
 | 
			
		||||
      if (!torrent.get_header().dir_name.empty()) {
 | 
			
		||||
        try_rm_empty_dir(torrent.get_root_dir() + '/' + torrent.get_header().dir_name);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  std::string path = db_root_ + "/torrent-files/" + torrent.get_hash().to_hex();
 | 
			
		||||
  td::rmrf(path).ignore();
 | 
			
		||||
  NodeActor::cleanup_db(db_, torrent.get_hash(),
 | 
			
		||||
                        [promise = std::move(closing_state->promise)](td::Result<td::Unit> R) mutable {
 | 
			
		||||
                          if (R.is_error()) {
 | 
			
		||||
| 
						 | 
				
			
			@ -292,4 +364,28 @@ void StorageManager::get_peers_info(td::Bits256 hash,
 | 
			
		|||
  td::actor::send_closure(entry->actor, &NodeActor::get_peers_info, std::move(promise));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void StorageManager::get_speed_limits(td::Promise<std::pair<double, double>> promise) {
 | 
			
		||||
  promise.set_result(std::make_pair(download_speed_limit_, upload_speed_limit_));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void StorageManager::set_download_speed_limit(double max_speed) {
 | 
			
		||||
  if (max_speed < 0.0) {
 | 
			
		||||
    max_speed = -1.0;
 | 
			
		||||
  }
 | 
			
		||||
  LOG(INFO) << "Set download speed limit to " << max_speed;
 | 
			
		||||
  download_speed_limit_ = max_speed;
 | 
			
		||||
  td::actor::send_closure(download_speed_limiter_, &SpeedLimiter::set_max_speed, max_speed);
 | 
			
		||||
  db_store_config();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void StorageManager::set_upload_speed_limit(double max_speed) {
 | 
			
		||||
  if (max_speed < 0.0) {
 | 
			
		||||
    max_speed = -1.0;
 | 
			
		||||
  }
 | 
			
		||||
  LOG(INFO) << "Set upload speed limit to " << max_speed;
 | 
			
		||||
  upload_speed_limit_ = max_speed;
 | 
			
		||||
  td::actor::send_closure(upload_speed_limiter_, &SpeedLimiter::set_max_speed, max_speed);
 | 
			
		||||
  db_store_config();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace ton
 | 
			
		||||
| 
						 | 
				
			
			@ -22,6 +22,7 @@
 | 
			
		|||
#include "overlay/overlays.h"
 | 
			
		||||
#include "storage/PeerManager.h"
 | 
			
		||||
#include "storage/db.h"
 | 
			
		||||
#include "SpeedLimiter.h"
 | 
			
		||||
 | 
			
		||||
namespace ton {
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -63,6 +64,10 @@ class StorageManager : public td::actor::Actor {
 | 
			
		|||
  void wait_for_completion(td::Bits256 hash, td::Promise<td::Unit> promise);
 | 
			
		||||
  void get_peers_info(td::Bits256 hash, td::Promise<tl_object_ptr<ton_api::storage_daemon_peerList>> promise);
 | 
			
		||||
 | 
			
		||||
  void get_speed_limits(td::Promise<std::pair<double, double>> promise);  // Download, upload
 | 
			
		||||
  void set_download_speed_limit(double max_speed);
 | 
			
		||||
  void set_upload_speed_limit(double max_speed);
 | 
			
		||||
 | 
			
		||||
 private:
 | 
			
		||||
  adnl::AdnlNodeIdShort local_id_;
 | 
			
		||||
  std::string db_root_;
 | 
			
		||||
| 
						 | 
				
			
			@ -89,6 +94,13 @@ class StorageManager : public td::actor::Actor {
 | 
			
		|||
 | 
			
		||||
  std::map<td::Bits256, TorrentEntry> torrents_;
 | 
			
		||||
 | 
			
		||||
  double download_speed_limit_ = -1.0;
 | 
			
		||||
  double upload_speed_limit_ = -1.0;
 | 
			
		||||
  td::actor::ActorOwn<SpeedLimiter> download_speed_limiter_ =
 | 
			
		||||
      td::actor::create_actor<SpeedLimiter>("DownloadRateLimitrer", -1.0);
 | 
			
		||||
  td::actor::ActorOwn<SpeedLimiter> upload_speed_limiter_ =
 | 
			
		||||
      td::actor::create_actor<SpeedLimiter>("DownloadRateLimitrer", -1.0);
 | 
			
		||||
 | 
			
		||||
  td::Status add_torrent_impl(Torrent torrent, bool start_download, bool allow_upload);
 | 
			
		||||
 | 
			
		||||
  td::Result<TorrentEntry*> get_torrent(td::Bits256 hash) {
 | 
			
		||||
| 
						 | 
				
			
			@ -102,9 +114,11 @@ class StorageManager : public td::actor::Actor {
 | 
			
		|||
  td::unique_ptr<NodeActor::Callback> create_callback(td::Bits256 hash,
 | 
			
		||||
                                                      std::shared_ptr<TorrentEntry::ClosingState> closing_state);
 | 
			
		||||
 | 
			
		||||
  void loaded_config_from_db(tl_object_ptr<ton_api::storage_db_config> config);
 | 
			
		||||
  void load_torrents_from_db(std::vector<td::Bits256> torrents);
 | 
			
		||||
  void loaded_torrent_from_db(td::Bits256 hash, td::Result<td::actor::ActorOwn<NodeActor>> R);
 | 
			
		||||
  void after_load_torrents_from_db();
 | 
			
		||||
  void db_store_config();
 | 
			
		||||
  void db_store_torrent_list();
 | 
			
		||||
 | 
			
		||||
  void on_torrent_closed(Torrent torrent, std::shared_ptr<TorrentEntry::ClosingState> closing_state);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -107,6 +107,29 @@ std::string size_to_str(td::uint64 size) {
 | 
			
		|||
  return s.as_cslice().str();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
td::Result<td::uint64> str_to_size(std::string s) {
 | 
			
		||||
  if (!s.empty() && std::tolower(s.back()) == 'b') {
 | 
			
		||||
    s.pop_back();
 | 
			
		||||
  }
 | 
			
		||||
  int shift = 0;
 | 
			
		||||
  if (!s.empty()) {
 | 
			
		||||
    auto c = std::tolower(s.back());
 | 
			
		||||
    static std::string suffixes = "kmgtpe";
 | 
			
		||||
    for (size_t i = 0; i < suffixes.size(); ++i) {
 | 
			
		||||
      if (c == suffixes[i]) {
 | 
			
		||||
        shift = 10 * (int)(i + 1);
 | 
			
		||||
        s.pop_back();
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  TRY_RESULT(x, td::to_integer_safe<td::uint64>(s));
 | 
			
		||||
  if (td::count_leading_zeroes64(x) < shift) {
 | 
			
		||||
    return td::Status::Error("Number is too big");
 | 
			
		||||
  }
 | 
			
		||||
  return x << shift;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string time_to_str(td::uint32 time) {
 | 
			
		||||
  char time_buffer[80];
 | 
			
		||||
  time_t rawtime = time;
 | 
			
		||||
| 
						 | 
				
			
			@ -285,10 +308,13 @@ class StorageDaemonCli : public td::actor::Actor {
 | 
			
		|||
      }
 | 
			
		||||
      std::_Exit(0);
 | 
			
		||||
    } else if (tokens[0] == "help") {
 | 
			
		||||
      if (tokens.size() != 1) {
 | 
			
		||||
      std::string category;
 | 
			
		||||
      if (tokens.size() == 2) {
 | 
			
		||||
        category = tokens[1];
 | 
			
		||||
      } else if (tokens.size() != 1) {
 | 
			
		||||
        return td::Status::Error("Unexpected tokens");
 | 
			
		||||
      }
 | 
			
		||||
      return execute_help();
 | 
			
		||||
      return execute_help(category);
 | 
			
		||||
    } else if (tokens[0] == "setverbosity") {
 | 
			
		||||
      if (tokens.size() != 2) {
 | 
			
		||||
        return td::Status::Error("Expected level");
 | 
			
		||||
| 
						 | 
				
			
			@ -460,6 +486,46 @@ class StorageDaemonCli : public td::actor::Actor {
 | 
			
		|||
        return td::Status::Error("Unexpected EOLN");
 | 
			
		||||
      }
 | 
			
		||||
      return execute_get_peers(hash, json);
 | 
			
		||||
    } else if (tokens[0] == "get-pieces-info") {
 | 
			
		||||
      td::Bits256 hash;
 | 
			
		||||
      bool found_hash = false;
 | 
			
		||||
      bool json = false;
 | 
			
		||||
      bool files = false;
 | 
			
		||||
      td::uint64 offset = 0;
 | 
			
		||||
      td::optional<td::uint64> max_pieces;
 | 
			
		||||
      for (size_t i = 1; i < tokens.size(); ++i) {
 | 
			
		||||
        if (!tokens[i].empty() && tokens[i][0] == '-') {
 | 
			
		||||
          if (tokens[i] == "--json") {
 | 
			
		||||
            json = true;
 | 
			
		||||
            continue;
 | 
			
		||||
          }
 | 
			
		||||
          if (tokens[i] == "--files") {
 | 
			
		||||
            files = true;
 | 
			
		||||
            continue;
 | 
			
		||||
          }
 | 
			
		||||
          if (tokens[i] == "--offset") {
 | 
			
		||||
            TRY_RESULT_PREFIX_ASSIGN(offset, td::to_integer_safe<td::uint64>(tokens[i + 1]), "Invalid offset: ");
 | 
			
		||||
            ++i;
 | 
			
		||||
            continue;
 | 
			
		||||
          }
 | 
			
		||||
          if (tokens[i] == "--max-pieces") {
 | 
			
		||||
            TRY_RESULT_PREFIX_ASSIGN(max_pieces, td::to_integer_safe<td::uint64>(tokens[i + 1]), "Invalid offset: ");
 | 
			
		||||
            max_pieces.value() = std::min(max_pieces.value(), ((td::uint64)1 << 62));
 | 
			
		||||
            ++i;
 | 
			
		||||
            continue;
 | 
			
		||||
          }
 | 
			
		||||
          return td::Status::Error(PSTRING() << "Unknown flag " << tokens[i]);
 | 
			
		||||
        }
 | 
			
		||||
        if (found_hash) {
 | 
			
		||||
          return td::Status::Error("Unexpected token");
 | 
			
		||||
        }
 | 
			
		||||
        TRY_RESULT_ASSIGN(hash, parse_torrent(tokens[i]));
 | 
			
		||||
        found_hash = true;
 | 
			
		||||
      }
 | 
			
		||||
      if (!found_hash) {
 | 
			
		||||
        return td::Status::Error("Unexpected EOLN");
 | 
			
		||||
      }
 | 
			
		||||
      return execute_get_pieces_info(hash, files, offset, max_pieces, json);
 | 
			
		||||
    } else if (tokens[0] == "download-pause" || tokens[0] == "download-resume") {
 | 
			
		||||
      if (tokens.size() != 2) {
 | 
			
		||||
        return td::Status::Error("Expected bag");
 | 
			
		||||
| 
						 | 
				
			
			@ -544,6 +610,48 @@ class StorageDaemonCli : public td::actor::Actor {
 | 
			
		|||
        return td::Status::Error("Unexpected EOLN");
 | 
			
		||||
      }
 | 
			
		||||
      return execute_load_from(hash, std::move(meta), std::move(path));
 | 
			
		||||
    } else if (tokens[0] == "get-speed-limits") {
 | 
			
		||||
      bool json = false;
 | 
			
		||||
      for (size_t i = 1; i < tokens.size(); ++i) {
 | 
			
		||||
        if (!tokens[i].empty() && tokens[i][0] == '-') {
 | 
			
		||||
          if (tokens[i] == "--json") {
 | 
			
		||||
            json = true;
 | 
			
		||||
            continue;
 | 
			
		||||
          }
 | 
			
		||||
          return td::Status::Error(PSTRING() << "Unknown flag " << tokens[i]);
 | 
			
		||||
        }
 | 
			
		||||
        return td::Status::Error("Unexpected token");
 | 
			
		||||
      }
 | 
			
		||||
      return execute_get_speed_limits(json);
 | 
			
		||||
    } else if (tokens[0] == "set-speed-limits") {
 | 
			
		||||
      td::optional<double> download, upload;
 | 
			
		||||
      for (size_t i = 1; i < tokens.size(); ++i) {
 | 
			
		||||
        if (!tokens[i].empty() && tokens[i][0] == '-') {
 | 
			
		||||
          if (tokens[i] == "--download") {
 | 
			
		||||
            ++i;
 | 
			
		||||
            if (tokens[i] == "unlimited") {
 | 
			
		||||
              download = -1.0;
 | 
			
		||||
            } else {
 | 
			
		||||
              TRY_RESULT_PREFIX(x, str_to_size(tokens[i]), "Invalid download speed: ");
 | 
			
		||||
              download = (double)x;
 | 
			
		||||
            }
 | 
			
		||||
            continue;
 | 
			
		||||
          }
 | 
			
		||||
          if (tokens[i] == "--upload") {
 | 
			
		||||
            ++i;
 | 
			
		||||
            if (tokens[i] == "unlimited") {
 | 
			
		||||
              upload = -1.0;
 | 
			
		||||
            } else {
 | 
			
		||||
              TRY_RESULT_PREFIX(x, str_to_size(tokens[i]), "Invalid upload speed: ");
 | 
			
		||||
              upload = (double)x;
 | 
			
		||||
            }
 | 
			
		||||
            continue;
 | 
			
		||||
          }
 | 
			
		||||
          return td::Status::Error(PSTRING() << "Unknown flag " << tokens[i]);
 | 
			
		||||
        }
 | 
			
		||||
        return td::Status::Error("Unexpected token");
 | 
			
		||||
      }
 | 
			
		||||
      return execute_set_speed_limits(download, upload);
 | 
			
		||||
    } else if (tokens[0] == "new-contract-message") {
 | 
			
		||||
      td::Bits256 hash;
 | 
			
		||||
      std::string file;
 | 
			
		||||
| 
						 | 
				
			
			@ -659,12 +767,12 @@ class StorageDaemonCli : public td::actor::Actor {
 | 
			
		|||
          continue;
 | 
			
		||||
        }
 | 
			
		||||
        if (tokens[i] == "--min-file-size") {
 | 
			
		||||
          TRY_RESULT_PREFIX(x, td::to_integer_safe<td::uint64>(tokens[i + 1]), "Invalid value for --min-file-size: ");
 | 
			
		||||
          TRY_RESULT_PREFIX(x, str_to_size(tokens[i + 1]), "Invalid value for --min-file-size: ");
 | 
			
		||||
          new_params.minimal_file_size = x;
 | 
			
		||||
          continue;
 | 
			
		||||
        }
 | 
			
		||||
        if (tokens[i] == "--max-file-size") {
 | 
			
		||||
          TRY_RESULT_PREFIX(x, td::to_integer_safe<td::uint64>(tokens[i + 1]), "Invalid value for --max-file-size: ");
 | 
			
		||||
          TRY_RESULT_PREFIX(x, str_to_size(tokens[i + 1]), "Invalid value for --max-file-size: ");
 | 
			
		||||
          new_params.maximal_file_size = x;
 | 
			
		||||
          continue;
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -708,7 +816,7 @@ class StorageDaemonCli : public td::actor::Actor {
 | 
			
		|||
          continue;
 | 
			
		||||
        }
 | 
			
		||||
        if (tokens[i] == "--max-total-size") {
 | 
			
		||||
          TRY_RESULT_PREFIX(x, td::to_integer_safe<td::uint64>(tokens[i + 1]), "Invalid value for --max-total-size: ");
 | 
			
		||||
          TRY_RESULT_PREFIX(x, str_to_size(tokens[i + 1]), "Invalid value for --max-total-size: ");
 | 
			
		||||
          new_config.max_total_size = x;
 | 
			
		||||
          continue;
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -765,10 +873,10 @@ class StorageDaemonCli : public td::actor::Actor {
 | 
			
		|||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  td::Status execute_help() {
 | 
			
		||||
    td::TerminalIO::out() << "help\tPrint this help\n";
 | 
			
		||||
    td::TerminalIO::out()
 | 
			
		||||
        << "create [-d description] [--no-upload] [--copy] [--json] <file/dir>\tCreate bag of files from <file/dir>\n";
 | 
			
		||||
  td::Status execute_help(std::string category) {
 | 
			
		||||
    if (category == "") {
 | 
			
		||||
      td::TerminalIO::out() << "create [-d description] [--no-upload] [--copy] [--json] <file/dir>\tCreate bag of "
 | 
			
		||||
                               "files from <file/dir>\n";
 | 
			
		||||
      td::TerminalIO::out() << "\t-d\tDescription will be stored in torrent info\n";
 | 
			
		||||
      td::TerminalIO::out() << "\t--no-upload\tDon't share bag with peers\n";
 | 
			
		||||
      td::TerminalIO::out() << "\t--copy\tFiles will be copied to an internal directory of storage-daemon\n";
 | 
			
		||||
| 
						 | 
				
			
			@ -792,6 +900,11 @@ class StorageDaemonCli : public td::actor::Actor {
 | 
			
		|||
      td::TerminalIO::out() << "\tHere and below bags are identified by BagID (in hex) or index (see bag list)\n";
 | 
			
		||||
      td::TerminalIO::out() << "get-meta <bag> <file>\tSave bag meta of <bag> to <file>\n";
 | 
			
		||||
      td::TerminalIO::out() << "get-peers <bag> [--json]\tPrint a list of peers\n";
 | 
			
		||||
      td::TerminalIO::out() << "get-pieces-info <bag> [--files] [--offset l] [--max-pieces m] [--json]\tPrint "
 | 
			
		||||
                               "information about ready pieces\n";
 | 
			
		||||
      td::TerminalIO::out() << "\t--files\tShow piece ranges for each file\n";
 | 
			
		||||
      td::TerminalIO::out() << "\t--offset l\tShow pieces starting from l (deafault: 0)\n";
 | 
			
		||||
      td::TerminalIO::out() << "\t--max-pieces m\tShow no more than m pieces (deafault: unlimited)\n";
 | 
			
		||||
      td::TerminalIO::out() << "\t--json\tOutput in json\n";
 | 
			
		||||
      td::TerminalIO::out() << "download-pause <bag>\tPause download of <bag>\n";
 | 
			
		||||
      td::TerminalIO::out() << "download-resume <bag>\tResume download of <bag>\n";
 | 
			
		||||
| 
						 | 
				
			
			@ -809,6 +922,12 @@ class StorageDaemonCli : public td::actor::Actor {
 | 
			
		|||
                               "incomplete bag.\n";
 | 
			
		||||
      td::TerminalIO::out() << "\t--meta meta\ttorrent info and header will be inited (if not ready) from meta file\n";
 | 
			
		||||
      td::TerminalIO::out() << "\t--files path\tdata for files will be taken from here\n";
 | 
			
		||||
      td::TerminalIO::out() << "get-speed-limits [--json]\tShow global limits for download and upload speed\n";
 | 
			
		||||
      td::TerminalIO::out() << "\t--json\tOutput in json\n";
 | 
			
		||||
      td::TerminalIO::out()
 | 
			
		||||
          << "set-speed-limits [--download x] [--upload x]\tSet global limits for download and upload speed\n";
 | 
			
		||||
      td::TerminalIO::out() << "\t--download x\tDownload speed limit in bytes/s, or \"unlimited\"\n";
 | 
			
		||||
      td::TerminalIO::out() << "\t--upload x\tUpload speed limit in bytes/s, or \"unlimited\"\n";
 | 
			
		||||
      td::TerminalIO::out() << "new-contract-message <bag> <file> [--query-id id] --provider <provider>\tCreate "
 | 
			
		||||
                               "\"new contract message\" for storage provider. Saves message body to <file>.\n";
 | 
			
		||||
      td::TerminalIO::out() << "\t<provider>\tAddress of storage provider account to take parameters from.\n";
 | 
			
		||||
| 
						 | 
				
			
			@ -817,6 +936,9 @@ class StorageDaemonCli : public td::actor::Actor {
 | 
			
		|||
      td::TerminalIO::out() << "exit\tExit\n";
 | 
			
		||||
      td::TerminalIO::out() << "quit\tExit\n";
 | 
			
		||||
      td::TerminalIO::out() << "setverbosity <level>\tSet vetbosity to <level> in [0..10]\n";
 | 
			
		||||
      td::TerminalIO::out() << "help\tPrint this help\n";
 | 
			
		||||
      td::TerminalIO::out() << "help provider\tcommands for deploying and controling storage provider\n";
 | 
			
		||||
    } else if (category == "provider") {
 | 
			
		||||
      td::TerminalIO::out() << "\nStorage provider control:\n";
 | 
			
		||||
      td::TerminalIO::out() << "import-pk <file>\tImport private key from <file>\n";
 | 
			
		||||
      td::TerminalIO::out() << "deploy-provider\tInit storage provider by deploying a new provider smart contract\n";
 | 
			
		||||
| 
						 | 
				
			
			@ -846,12 +968,16 @@ class StorageDaemonCli : public td::actor::Actor {
 | 
			
		|||
      td::TerminalIO::out() << "\t--max-contracts\tMaximal number of storage contracts\n";
 | 
			
		||||
      td::TerminalIO::out() << "\t--max-total-size\tMaximal total size storage contracts (in bytes)\n";
 | 
			
		||||
      td::TerminalIO::out() << "withdraw <address>\tSend bounty from storage contract <address> to the main contract\n";
 | 
			
		||||
    td::TerminalIO::out() << "withdraw-all\tSend bounty from all storage contracts (where at least 1 TON is available) "
 | 
			
		||||
                             "to the main contract\n";
 | 
			
		||||
      td::TerminalIO::out()
 | 
			
		||||
        << "send-coins <address> <amount> [--message msg]\tSend <amount> nanoTON to <address> from the main contract\n";
 | 
			
		||||
          << "withdraw-all\tSend bounty from all storage contracts (where at least 1 TON is available) "
 | 
			
		||||
             "to the main contract\n";
 | 
			
		||||
      td::TerminalIO::out() << "send-coins <address> <amount> [--message msg]\tSend <amount> nanoTON to <address> from "
 | 
			
		||||
                               "the main contract\n";
 | 
			
		||||
      td::TerminalIO::out()
 | 
			
		||||
          << "close-contract <address>\tClose storage contract <address> and delete bag (if possible)\n";
 | 
			
		||||
    } else {
 | 
			
		||||
      td::TerminalIO::out() << "Unknown command 'help " << category << "'\n";
 | 
			
		||||
    }
 | 
			
		||||
    command_finished(td::Status::OK());
 | 
			
		||||
    return td::Status::OK();
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			@ -871,7 +997,7 @@ class StorageDaemonCli : public td::actor::Actor {
 | 
			
		|||
 | 
			
		||||
  td::Status execute_create(std::string path, std::string description, bool upload, bool copy, bool json) {
 | 
			
		||||
    TRY_RESULT_PREFIX_ASSIGN(path, td::realpath(path), "Invalid path: ");
 | 
			
		||||
    auto query = create_tl_object<ton_api::storage_daemon_createTorrent>(path, description, upload, copy);
 | 
			
		||||
    auto query = create_tl_object<ton_api::storage_daemon_createTorrent>(path, description, upload, copy, 0);
 | 
			
		||||
    send_query(std::move(query),
 | 
			
		||||
               [=, SelfId = actor_id(this)](td::Result<tl_object_ptr<ton_api::storage_daemon_torrentFull>> R) {
 | 
			
		||||
                 if (R.is_error()) {
 | 
			
		||||
| 
						 | 
				
			
			@ -904,7 +1030,7 @@ class StorageDaemonCli : public td::actor::Actor {
 | 
			
		|||
      }
 | 
			
		||||
    }
 | 
			
		||||
    auto query = create_tl_object<ton_api::storage_daemon_addByHash>(hash, std::move(root_dir), !paused, upload,
 | 
			
		||||
                                                                     std::move(priorities));
 | 
			
		||||
                                                                     std::move(priorities), 0);
 | 
			
		||||
    send_query(std::move(query),
 | 
			
		||||
               [=, SelfId = actor_id(this)](td::Result<tl_object_ptr<ton_api::storage_daemon_torrentFull>> R) {
 | 
			
		||||
                 if (R.is_error()) {
 | 
			
		||||
| 
						 | 
				
			
			@ -938,7 +1064,7 @@ class StorageDaemonCli : public td::actor::Actor {
 | 
			
		|||
      }
 | 
			
		||||
    }
 | 
			
		||||
    auto query = create_tl_object<ton_api::storage_daemon_addByMeta>(std::move(meta), std::move(root_dir), !paused,
 | 
			
		||||
                                                                     upload, std::move(priorities));
 | 
			
		||||
                                                                     upload, std::move(priorities), 0);
 | 
			
		||||
    send_query(std::move(query),
 | 
			
		||||
               [=, SelfId = actor_id(this)](td::Result<tl_object_ptr<ton_api::storage_daemon_torrentFull>> R) {
 | 
			
		||||
                 if (R.is_error()) {
 | 
			
		||||
| 
						 | 
				
			
			@ -957,7 +1083,7 @@ class StorageDaemonCli : public td::actor::Actor {
 | 
			
		|||
  }
 | 
			
		||||
 | 
			
		||||
  td::Status execute_list(bool with_hashes, bool json) {
 | 
			
		||||
    auto query = create_tl_object<ton_api::storage_daemon_getTorrents>();
 | 
			
		||||
    auto query = create_tl_object<ton_api::storage_daemon_getTorrents>(0);
 | 
			
		||||
    send_query(std::move(query),
 | 
			
		||||
               [=, SelfId = actor_id(this)](td::Result<tl_object_ptr<ton_api::storage_daemon_torrentList>> R) {
 | 
			
		||||
                 if (R.is_error()) {
 | 
			
		||||
| 
						 | 
				
			
			@ -975,7 +1101,7 @@ class StorageDaemonCli : public td::actor::Actor {
 | 
			
		|||
  }
 | 
			
		||||
 | 
			
		||||
  td::Status execute_get(td::Bits256 hash, bool json) {
 | 
			
		||||
    auto query = create_tl_object<ton_api::storage_daemon_getTorrentFull>(hash);
 | 
			
		||||
    auto query = create_tl_object<ton_api::storage_daemon_getTorrentFull>(hash, 0);
 | 
			
		||||
    send_query(std::move(query),
 | 
			
		||||
               [=, SelfId = actor_id(this)](td::Result<tl_object_ptr<ton_api::storage_daemon_torrentFull>> R) {
 | 
			
		||||
                 if (R.is_error()) {
 | 
			
		||||
| 
						 | 
				
			
			@ -993,7 +1119,7 @@ class StorageDaemonCli : public td::actor::Actor {
 | 
			
		|||
  }
 | 
			
		||||
 | 
			
		||||
  td::Status execute_get_meta(td::Bits256 hash, std::string meta_file) {
 | 
			
		||||
    auto query = create_tl_object<ton_api::storage_daemon_getTorrentMeta>(hash);
 | 
			
		||||
    auto query = create_tl_object<ton_api::storage_daemon_getTorrentMeta>(hash, 0);
 | 
			
		||||
    send_query(std::move(query),
 | 
			
		||||
               [SelfId = actor_id(this), meta_file](td::Result<tl_object_ptr<ton_api::storage_daemon_torrentMeta>> R) {
 | 
			
		||||
                 if (R.is_error()) {
 | 
			
		||||
| 
						 | 
				
			
			@ -1014,7 +1140,7 @@ class StorageDaemonCli : public td::actor::Actor {
 | 
			
		|||
  }
 | 
			
		||||
 | 
			
		||||
  td::Status execute_get_peers(td::Bits256 hash, bool json) {
 | 
			
		||||
    auto query = create_tl_object<ton_api::storage_daemon_getTorrentPeers>(hash);
 | 
			
		||||
    auto query = create_tl_object<ton_api::storage_daemon_getTorrentPeers>(hash, 0);
 | 
			
		||||
    send_query(
 | 
			
		||||
        std::move(query), [=, SelfId = actor_id(this)](td::Result<tl_object_ptr<ton_api::storage_daemon_peerList>> R) {
 | 
			
		||||
          if (R.is_error()) {
 | 
			
		||||
| 
						 | 
				
			
			@ -1054,6 +1180,63 @@ class StorageDaemonCli : public td::actor::Actor {
 | 
			
		|||
    return td::Status::OK();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  td::Status execute_get_pieces_info(td::Bits256 hash, bool files, td::uint64 offset,
 | 
			
		||||
                                     td::optional<td::uint64> max_pieces, bool json) {
 | 
			
		||||
    auto query = create_tl_object<ton_api::storage_daemon_getTorrentPiecesInfo>(hash, files ? 1 : 0, offset,
 | 
			
		||||
                                                                                max_pieces ? max_pieces.value() : -1);
 | 
			
		||||
    send_query(std::move(query),
 | 
			
		||||
               [=, SelfId = actor_id(this)](td::Result<tl_object_ptr<ton_api::storage_daemon_torrentPiecesInfo>> R) {
 | 
			
		||||
                 if (R.is_error()) {
 | 
			
		||||
                   return;
 | 
			
		||||
                 }
 | 
			
		||||
                 if (json) {
 | 
			
		||||
                   print_json(R.ok());
 | 
			
		||||
                   td::actor::send_closure(SelfId, &StorageDaemonCli::command_finished, td::Status::OK());
 | 
			
		||||
                   return;
 | 
			
		||||
                 }
 | 
			
		||||
                 auto obj = R.move_as_ok();
 | 
			
		||||
                 td::TerminalIO::out() << "BagID " << hash.to_hex() << "\n";
 | 
			
		||||
                 td::TerminalIO::out() << "Total pieces: " << obj->total_pieces_ << ", piece size: " << obj->piece_size_
 | 
			
		||||
                                       << "\n";
 | 
			
		||||
                 if (files) {
 | 
			
		||||
                   if (obj->flags_ & 1) {
 | 
			
		||||
                     td::TerminalIO::out() << "Files:\n";
 | 
			
		||||
                     std::vector<std::vector<std::string>> table;
 | 
			
		||||
                     table.push_back({"#####", "Piece range", "Name"});
 | 
			
		||||
                     size_t i = 0;
 | 
			
		||||
                     for (const auto& f : obj->files_) {
 | 
			
		||||
                       table.push_back({i == 0 ? "" : td::to_string(i - 1),
 | 
			
		||||
                                        PSTRING() << "[" << f->range_l_ << ".." << f->range_r_ << ")",
 | 
			
		||||
                                        f->name_.empty() ? "[HEADER]" : f->name_});
 | 
			
		||||
                       ++i;
 | 
			
		||||
                     }
 | 
			
		||||
                     print_table(table, {1, 2});
 | 
			
		||||
                   } else {
 | 
			
		||||
                     td::TerminalIO::out() << "Cannot show files: torrent header is not available\n";
 | 
			
		||||
                   }
 | 
			
		||||
                 }
 | 
			
		||||
                 td::uint64 l = obj->range_l_, r = obj->range_r_;
 | 
			
		||||
                 td::TerminalIO::out() << "Pieces [" << l << ".." << r << ")\n";
 | 
			
		||||
                 if (obj->range_l_ != obj->range_r_) {
 | 
			
		||||
                   std::vector<std::vector<std::string>> table;
 | 
			
		||||
                   td::uint64 i = l;
 | 
			
		||||
                   while (i < r) {
 | 
			
		||||
                     td::uint64 ir = std::min(i + 100, r);
 | 
			
		||||
                     std::string s = "[";
 | 
			
		||||
                     for (td::uint64 j = i; j < ir; ++j) {
 | 
			
		||||
                       s += (obj->piece_ready_bitset_[(j - l) / 8] & (1 << ((j - l) % 8)) ? '#' : '-');
 | 
			
		||||
                     }
 | 
			
		||||
                     s += ']';
 | 
			
		||||
                     table.push_back({std::to_string(i), s});
 | 
			
		||||
                     i = ir;
 | 
			
		||||
                   }
 | 
			
		||||
                   print_table(table, {1});
 | 
			
		||||
                 }
 | 
			
		||||
                 td::actor::send_closure(SelfId, &StorageDaemonCli::command_finished, td::Status::OK());
 | 
			
		||||
               });
 | 
			
		||||
    return td::Status::OK();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  td::Status execute_set_active_download(td::Bits256 hash, bool active) {
 | 
			
		||||
    auto query = create_tl_object<ton_api::storage_daemon_setActiveDownload>(hash, active);
 | 
			
		||||
    send_query(std::move(query),
 | 
			
		||||
| 
						 | 
				
			
			@ -1156,7 +1339,7 @@ class StorageDaemonCli : public td::actor::Actor {
 | 
			
		|||
    if (!path.empty()) {
 | 
			
		||||
      TRY_RESULT_PREFIX_ASSIGN(path, td::realpath(path), "Invalid path: ");
 | 
			
		||||
    }
 | 
			
		||||
    auto query = create_tl_object<ton_api::storage_daemon_loadFrom>(hash, std::move(meta_data), std::move(path));
 | 
			
		||||
    auto query = create_tl_object<ton_api::storage_daemon_loadFrom>(hash, std::move(meta_data), std::move(path), 0);
 | 
			
		||||
    send_query(std::move(query),
 | 
			
		||||
               [SelfId = actor_id(this)](td::Result<tl_object_ptr<ton_api::storage_daemon_torrent>> R) {
 | 
			
		||||
                 if (R.is_error()) {
 | 
			
		||||
| 
						 | 
				
			
			@ -1183,6 +1366,59 @@ class StorageDaemonCli : public td::actor::Actor {
 | 
			
		|||
    return td::Status::OK();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  td::Status execute_get_speed_limits(bool json) {
 | 
			
		||||
    auto query = create_tl_object<ton_api::storage_daemon_getSpeedLimits>(0);
 | 
			
		||||
    send_query(std::move(query), [=, SelfId = actor_id(this)](
 | 
			
		||||
                                     td::Result<tl_object_ptr<ton_api::storage_daemon_speedLimits>> R) {
 | 
			
		||||
      if (R.is_error()) {
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
      if (json) {
 | 
			
		||||
        print_json(R.ok());
 | 
			
		||||
        td::actor::send_closure(SelfId, &StorageDaemonCli::command_finished, td::Status::OK());
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
      auto obj = R.move_as_ok();
 | 
			
		||||
      if (obj->download_ < 0.0) {
 | 
			
		||||
        td::TerminalIO::out() << "Download speed limit: unlimited\n";
 | 
			
		||||
      } else {
 | 
			
		||||
        td::TerminalIO::out() << "Download speed limit: " << td::format::as_size((td::uint64)obj->download_) << "/s\n";
 | 
			
		||||
      }
 | 
			
		||||
      if (obj->upload_ < 0.0) {
 | 
			
		||||
        td::TerminalIO::out() << "Upload speed limit:   unlimited\n";
 | 
			
		||||
      } else {
 | 
			
		||||
        td::TerminalIO::out() << "Upload speed limit:   " << td::format::as_size((td::uint64)obj->upload_) << "/s\n";
 | 
			
		||||
      }
 | 
			
		||||
      td::actor::send_closure(SelfId, &StorageDaemonCli::command_finished, td::Status::OK());
 | 
			
		||||
    });
 | 
			
		||||
    return td::Status::OK();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  td::Status execute_set_speed_limits(td::optional<double> download, td::optional<double> upload) {
 | 
			
		||||
    if (!download && !upload) {
 | 
			
		||||
      return td::Status::Error("No parameters are set");
 | 
			
		||||
    }
 | 
			
		||||
    auto query = create_tl_object<ton_api::storage_daemon_setSpeedLimits>();
 | 
			
		||||
    query->flags_ = 0;
 | 
			
		||||
    if (download) {
 | 
			
		||||
      query->flags_ |= 1;
 | 
			
		||||
      query->download_ = download.value();
 | 
			
		||||
    }
 | 
			
		||||
    if (upload) {
 | 
			
		||||
      query->flags_ |= 2;
 | 
			
		||||
      query->upload_ = upload.value();
 | 
			
		||||
    }
 | 
			
		||||
    send_query(std::move(query),
 | 
			
		||||
               [SelfId = actor_id(this)](td::Result<tl_object_ptr<ton_api::storage_daemon_success>> R) {
 | 
			
		||||
                 if (R.is_error()) {
 | 
			
		||||
                   return;
 | 
			
		||||
                 }
 | 
			
		||||
                 td::TerminalIO::out() << "Speed limits were set\n";
 | 
			
		||||
                 td::actor::send_closure(SelfId, &StorageDaemonCli::command_finished, td::Status::OK());
 | 
			
		||||
               });
 | 
			
		||||
    return td::Status::OK();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  td::Status execute_new_contract_message(td::Bits256 hash, std::string file, td::uint64 query_id,
 | 
			
		||||
                                          td::optional<std::string> provider_address, td::optional<std::string> rate,
 | 
			
		||||
                                          td::optional<td::uint32> max_span) {
 | 
			
		||||
| 
						 | 
				
			
			@ -1282,8 +1518,8 @@ class StorageDaemonCli : public td::actor::Actor {
 | 
			
		|||
 | 
			
		||||
  td::Status execute_get_provider_params(std::string address, bool json) {
 | 
			
		||||
    auto query = create_tl_object<ton_api::storage_daemon_getProviderParams>(address);
 | 
			
		||||
    send_query(std::move(query),
 | 
			
		||||
               [=, SelfId = actor_id(this)](td::Result<tl_object_ptr<ton_api::storage_daemon_provider_params>> R) {
 | 
			
		||||
    send_query(std::move(query), [=, SelfId = actor_id(this)](
 | 
			
		||||
                                     td::Result<tl_object_ptr<ton_api::storage_daemon_provider_params>> R) {
 | 
			
		||||
      if (R.is_error()) {
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
| 
						 | 
				
			
			@ -1297,8 +1533,9 @@ class StorageDaemonCli : public td::actor::Actor {
 | 
			
		|||
      td::TerminalIO::out() << "Accept new contracts: " << params->accept_new_contracts_ << "\n";
 | 
			
		||||
      td::TerminalIO::out() << "Rate (nanoTON per day*MB): " << params->rate_per_mb_day_ << "\n";
 | 
			
		||||
      td::TerminalIO::out() << "Max span: " << (td::uint32)params->max_span_ << "\n";
 | 
			
		||||
                 td::TerminalIO::out() << "Min file size: " << (td::uint64)params->minimal_file_size_ << "\n";
 | 
			
		||||
                 td::TerminalIO::out() << "Max file size: " << (td::uint64)params->maximal_file_size_ << "\n";
 | 
			
		||||
      auto min_size = (td::uint64)params->minimal_file_size_, max_size = (td::uint64)params->maximal_file_size_;
 | 
			
		||||
      td::TerminalIO::out() << "Min file size: " << td::format::as_size(min_size) << " (" << min_size << ")\n";
 | 
			
		||||
      td::TerminalIO::out() << "Max file size: " << td::format::as_size(max_size) << " (" << max_size << ")\n";
 | 
			
		||||
      td::actor::send_closure(SelfId, &StorageDaemonCli::command_finished, td::Status::OK());
 | 
			
		||||
    });
 | 
			
		||||
    return td::Status::OK();
 | 
			
		||||
| 
						 | 
				
			
			@ -1632,6 +1869,7 @@ class StorageDaemonCli : public td::actor::Actor {
 | 
			
		|||
    add_id(obj.torrent_->hash_);
 | 
			
		||||
    td::TerminalIO::out() << "BagID = " << obj.torrent_->hash_.to_hex() << "\n";
 | 
			
		||||
    td::TerminalIO::out() << "Index = " << hash_to_id_[obj.torrent_->hash_] << "\n";
 | 
			
		||||
    td::TerminalIO::out() << "Added: " << time_to_str(obj.torrent_->added_at_) << "\n";
 | 
			
		||||
    if (obj.torrent_->flags_ & 4) {  // fatal error
 | 
			
		||||
      td::TerminalIO::out() << "FATAL ERROR: " << obj.torrent_->fatal_error_ << "\n";
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -180,6 +180,9 @@ class StorageDaemon : public td::actor::Actor {
 | 
			
		|||
    dht_id_ = dht_id_full.compute_short_id();
 | 
			
		||||
    td::actor::send_closure(adnl_, &adnl::Adnl::add_id, dht_id_full, addr_list, static_cast<td::uint8>(0));
 | 
			
		||||
 | 
			
		||||
    LOG(INFO) << "Storage daemon ADNL id is " << local_id_;
 | 
			
		||||
    LOG(INFO) << "DHT id is " << dht_id_;
 | 
			
		||||
 | 
			
		||||
    if (client_mode_) {
 | 
			
		||||
      auto D = dht::Dht::create_client(dht_id_, db_root_, dht_config_, keyring_.get(), adnl_.get());
 | 
			
		||||
      D.ensure();
 | 
			
		||||
| 
						 | 
				
			
			@ -431,6 +434,52 @@ class StorageDaemon : public td::actor::Actor {
 | 
			
		|||
                            }));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void run_control_query(ton_api::storage_daemon_getTorrentPiecesInfo &query, td::Promise<td::BufferSlice> promise) {
 | 
			
		||||
    td::Bits256 hash = query.hash_;
 | 
			
		||||
    td::actor::send_closure(
 | 
			
		||||
        manager_, &StorageManager::with_torrent, hash,
 | 
			
		||||
        promise.wrap([query = std::move(query)](NodeActor::NodeState state) -> td::Result<td::BufferSlice> {
 | 
			
		||||
          Torrent &torrent = state.torrent;
 | 
			
		||||
          if (!torrent.inited_info()) {
 | 
			
		||||
            return td::Status::Error("Torrent info is not available");
 | 
			
		||||
          }
 | 
			
		||||
          td::uint64 total_pieces = torrent.get_info().pieces_count();
 | 
			
		||||
          td::uint64 range_l = std::min<td::uint64>(total_pieces, query.offset_);
 | 
			
		||||
          td::uint64 size = query.max_pieces_ != -1 ? std::min<td::uint64>(total_pieces - range_l, query.max_pieces_)
 | 
			
		||||
                                                    : total_pieces - range_l;
 | 
			
		||||
          td::BufferSlice piece_ready((size + 7) / 8);
 | 
			
		||||
          std::fill(piece_ready.data(), piece_ready.data() + piece_ready.size(), 0);
 | 
			
		||||
          for (td::uint64 i = range_l; i < range_l + size; ++i) {
 | 
			
		||||
            if (torrent.is_piece_ready(i)) {
 | 
			
		||||
              piece_ready.data()[(i - range_l) / 8] |= (1 << ((i - range_l) % 8));
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          auto result = create_tl_object<ton_api::storage_daemon_torrentPiecesInfo>();
 | 
			
		||||
          result->total_pieces_ = total_pieces;
 | 
			
		||||
          result->piece_size_ = torrent.get_info().piece_size;
 | 
			
		||||
          result->range_l_ = range_l;
 | 
			
		||||
          result->range_r_ = range_l + size;
 | 
			
		||||
          result->piece_ready_bitset_ = std::move(piece_ready);
 | 
			
		||||
 | 
			
		||||
          if ((query.flags_ & 1) && torrent.inited_header()) {
 | 
			
		||||
            result->flags_ = 1;
 | 
			
		||||
            auto range = torrent.get_header_parts_range();
 | 
			
		||||
            result->files_.push_back(
 | 
			
		||||
                create_tl_object<ton_api::storage_daemon_filePiecesInfo>("", range.begin, range.end));
 | 
			
		||||
            for (size_t i = 0; i < torrent.get_files_count().value(); ++i) {
 | 
			
		||||
              auto range = torrent.get_file_parts_range(i);
 | 
			
		||||
              result->files_.push_back(create_tl_object<ton_api::storage_daemon_filePiecesInfo>(
 | 
			
		||||
                  torrent.get_file_name(i).str(), range.begin, range.end));
 | 
			
		||||
            }
 | 
			
		||||
          } else {
 | 
			
		||||
            result->flags_ = 0;
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          return serialize_tl_object(result, true);
 | 
			
		||||
        }));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void run_control_query(ton_api::storage_daemon_setFilePriorityAll &query, td::Promise<td::BufferSlice> promise) {
 | 
			
		||||
    TRY_RESULT_PROMISE(promise, priority, td::narrow_cast_safe<td::uint8>(query.priority_));
 | 
			
		||||
    td::actor::send_closure(manager_, &StorageManager::set_all_files_priority, query.hash_, priority,
 | 
			
		||||
| 
						 | 
				
			
			@ -491,6 +540,24 @@ class StorageDaemon : public td::actor::Actor {
 | 
			
		|||
        });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void run_control_query(ton_api::storage_daemon_getSpeedLimits &query, td::Promise<td::BufferSlice> promise) {
 | 
			
		||||
    td::actor::send_closure(manager_, &StorageManager::get_speed_limits,
 | 
			
		||||
                            promise.wrap([](std::pair<double, double> limits) -> td::BufferSlice {
 | 
			
		||||
                              return create_serialize_tl_object<ton_api::storage_daemon_speedLimits>(limits.first,
 | 
			
		||||
                                                                                                     limits.second);
 | 
			
		||||
                            }));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void run_control_query(ton_api::storage_daemon_setSpeedLimits &query, td::Promise<td::BufferSlice> promise) {
 | 
			
		||||
    if (query.flags_ & 1) {
 | 
			
		||||
      td::actor::send_closure(manager_, &StorageManager::set_download_speed_limit, query.download_);
 | 
			
		||||
    }
 | 
			
		||||
    if (query.flags_ & 2) {
 | 
			
		||||
      td::actor::send_closure(manager_, &StorageManager::set_upload_speed_limit, query.upload_);
 | 
			
		||||
    }
 | 
			
		||||
    promise.set_result(create_serialize_tl_object<ton_api::storage_daemon_success>());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void run_control_query(ton_api::storage_daemon_getNewContractMessage &query, td::Promise<td::BufferSlice> promise) {
 | 
			
		||||
    td::Promise<std::pair<td::RefInt256, td::uint32>> P =
 | 
			
		||||
        [promise = std::move(promise), hash = query.hash_, query_id = query.query_id_,
 | 
			
		||||
| 
						 | 
				
			
			@ -779,6 +846,7 @@ class StorageDaemon : public td::actor::Actor {
 | 
			
		|||
      file->name_ = torrent.get_file_name(i).str();
 | 
			
		||||
      file->size_ = torrent.get_file_size(i);
 | 
			
		||||
      file->downloaded_size_ = torrent.get_file_ready_size(i);
 | 
			
		||||
      file->flags_ = 0;
 | 
			
		||||
      obj.files_.push_back(std::move(file));
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			@ -798,6 +866,7 @@ class StorageDaemon : public td::actor::Actor {
 | 
			
		|||
                              obj->active_upload_ = state.active_upload;
 | 
			
		||||
                              obj->download_speed_ = state.download_speed;
 | 
			
		||||
                              obj->upload_speed_ = state.upload_speed;
 | 
			
		||||
                              obj->added_at_ = state.added_at;
 | 
			
		||||
                              promise.set_result(std::move(obj));
 | 
			
		||||
                            });
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			@ -816,6 +885,7 @@ class StorageDaemon : public td::actor::Actor {
 | 
			
		|||
                                obj->torrent_->active_upload_ = state.active_upload;
 | 
			
		||||
                                obj->torrent_->download_speed_ = state.download_speed;
 | 
			
		||||
                                obj->torrent_->upload_speed_ = state.upload_speed;
 | 
			
		||||
                                obj->torrent_->added_at_ = state.added_at;
 | 
			
		||||
                                for (size_t i = 0; i < obj->files_.size(); ++i) {
 | 
			
		||||
                                  obj->files_[i]->priority_ =
 | 
			
		||||
                                      (i < state.file_priority.size() ? state.file_priority[i] : 1);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1342,7 +1342,7 @@ TEST(Torrent, Peer) {
 | 
			
		|||
    guard->push_back(td::actor::create_actor<ton::NodeActor>(
 | 
			
		||||
        "Node#1", 1, std::move(torrent),
 | 
			
		||||
        td::make_unique<TorrentCallback>(stop_watcher, complete_watcher),
 | 
			
		||||
        td::make_unique<PeerCreator>(peer_manager.get(), 1, gen_peers(1, 2)), nullptr));
 | 
			
		||||
        td::make_unique<PeerCreator>(peer_manager.get(), 1, gen_peers(1, 2)), nullptr, ton::SpeedLimiters{}));
 | 
			
		||||
    for (size_t i = 2; i <= peers_n; i++) {
 | 
			
		||||
      ton::Torrent::Options options;
 | 
			
		||||
      options.in_memory = true;
 | 
			
		||||
| 
						 | 
				
			
			@ -1351,7 +1351,7 @@ TEST(Torrent, Peer) {
 | 
			
		|||
          PSLICE() << "Node#" << i, i, std::move(other_torrent),
 | 
			
		||||
          td::make_unique<TorrentCallback>(stop_watcher, complete_watcher),
 | 
			
		||||
          td::make_unique<PeerCreator>(peer_manager.get(), i, gen_peers(i, 2)),
 | 
			
		||||
          nullptr);
 | 
			
		||||
          nullptr, ton::SpeedLimiters{});
 | 
			
		||||
 | 
			
		||||
      if (i == 3) {
 | 
			
		||||
        td::actor::create_actor<StatsActor>("StatsActor", node_actor.get()).release();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -764,9 +764,12 @@ storage.db.key.torrentMeta hash:int256 = storage.db.key.TorrentMeta;
 | 
			
		|||
storage.db.key.priorities hash:int256 = storage.db.key.Priorities;
 | 
			
		||||
storage.db.key.piecesInDb hash:int256 = storage.db.key.PiecesInDb;
 | 
			
		||||
storage.db.key.pieceInDb hash:int256 idx:long = storage.db.key.PieceInDb;
 | 
			
		||||
storage.db.key.config = storage.db.key.Config;
 | 
			
		||||
 | 
			
		||||
storage.db.config flags:# download_speed_limit:double upload_speed_limit:double = storage.db.Config;
 | 
			
		||||
storage.db.torrentList torrents:(vector int256) = storage.db.TorrentList;
 | 
			
		||||
storage.db.torrent root_dir:string active_download:Bool active_upload:Bool = storage.db.TorrentShort;
 | 
			
		||||
storage.db.torrentV2 flags:# root_dir:string added_at:int active_download:Bool active_upload:Bool = storage.db.TorrentShort;
 | 
			
		||||
storage.db.priorities actions:(vector storage.PriorityAction) = storage.db.Priorities;
 | 
			
		||||
storage.db.piecesInDb pieces:(vector long) = storage.db.PiecesInDb;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -794,6 +797,7 @@ storage.provider.db.microchunkTree data:bytes = storage.provider.db.MicrochunkTr
 | 
			
		|||
 | 
			
		||||
storage.daemon.queryError message:string = storage.daemon.QueryError;
 | 
			
		||||
storage.daemon.success = storage.daemon.Success;
 | 
			
		||||
 | 
			
		||||
storage.daemon.torrent
 | 
			
		||||
    hash:int256 flags:#
 | 
			
		||||
    // 0 - info ready
 | 
			
		||||
| 
						 | 
				
			
			@ -802,19 +806,29 @@ storage.daemon.torrent
 | 
			
		|||
    total_size:flags.0?long description:flags.0?string
 | 
			
		||||
    files_count:flags.1?long included_size:flags.1?long dir_name:flags.1?string
 | 
			
		||||
    downloaded_size:long
 | 
			
		||||
    root_dir:string active_download:Bool active_upload:Bool completed:Bool
 | 
			
		||||
    added_at:int root_dir:string active_download:Bool active_upload:Bool completed:Bool
 | 
			
		||||
    download_speed:double upload_speed:double
 | 
			
		||||
    fatal_error:flags.2?string
 | 
			
		||||
    = storage.daemon.Torrent;
 | 
			
		||||
 | 
			
		||||
storage.daemon.fileInfo
 | 
			
		||||
    name:string size:long
 | 
			
		||||
    name:string size:long flags:#
 | 
			
		||||
    priority:int
 | 
			
		||||
    downloaded_size:long
 | 
			
		||||
    = storage.daemon.FileInfo;
 | 
			
		||||
 | 
			
		||||
storage.daemon.torrentFull torrent:storage.daemon.torrent files:(vector storage.daemon.fileInfo) = storage.daemon.TorrentFull;
 | 
			
		||||
storage.daemon.torrentList torrents:(vector storage.daemon.torrent) = storage.daemon.TorrentList;
 | 
			
		||||
storage.daemon.torrentMeta meta:bytes = storage.daemon.TorrentMeta;
 | 
			
		||||
 | 
			
		||||
storage.daemon.filePiecesInfo name:string range_l:long range_r:long = storage.daemon.FilePiecesInfo;
 | 
			
		||||
storage.daemon.torrentPiecesInfo
 | 
			
		||||
    flags:# // 0 - with file ranges
 | 
			
		||||
    total_pieces:long piece_size:int
 | 
			
		||||
    range_l:long range_r:long piece_ready_bitset:bytes
 | 
			
		||||
    files:flags.0?(vector storage.daemon.filePiecesInfo) // files[0] is header
 | 
			
		||||
    = storage.daemon.TorrentPiecesInfo;
 | 
			
		||||
 | 
			
		||||
storage.daemon.newContractParams rate:string max_span:int = storage.daemon.NewContractParams;
 | 
			
		||||
storage.daemon.newContractParamsAuto provider_address:string = storage.daemon.NewContractParams;
 | 
			
		||||
storage.daemon.newContractMessage body:bytes rate:string max_span:int = storage.daemon.NewContractMessage;
 | 
			
		||||
| 
						 | 
				
			
			@ -827,6 +841,8 @@ storage.daemon.priorityPending = storage.daemon.SetPriorityStatus;
 | 
			
		|||
 | 
			
		||||
storage.daemon.keyHash key_hash:int256 = storage.daemon.KeyHash;
 | 
			
		||||
 | 
			
		||||
storage.daemon.speedLimits download:double upload:double = storage.daemon.SpeedLimits;
 | 
			
		||||
 | 
			
		||||
storage.daemon.providerConfig max_contracts:int max_total_size:long = storage.daemon.ProviderConfig;
 | 
			
		||||
storage.daemon.contractInfo address:string state:int torrent:int256 created_time:int file_size:long downloaded_size:long
 | 
			
		||||
    rate:string max_span:int client_balance:string contract_balance:string = storage.daemon.ContractInfo;
 | 
			
		||||
| 
						 | 
				
			
			@ -837,24 +853,32 @@ storage.daemon.providerAddress address:string = storage.daemon.ProviderAddress;
 | 
			
		|||
 | 
			
		||||
---functions---
 | 
			
		||||
storage.daemon.setVerbosity verbosity:int = storage.daemon.Success;
 | 
			
		||||
storage.daemon.createTorrent path:string description:string allow_upload:Bool copy_inside:Bool = storage.daemon.TorrentFull;
 | 
			
		||||
storage.daemon.addByHash hash:int256 root_dir:string start_download:Bool allow_upload:Bool priorities:(vector storage.PriorityAction) = storage.daemon.TorrentFull;
 | 
			
		||||
storage.daemon.addByMeta meta:bytes root_dir:string start_download:Bool allow_upload:Bool priorities:(vector storage.PriorityAction) = storage.daemon.TorrentFull;
 | 
			
		||||
storage.daemon.createTorrent path:string description:string allow_upload:Bool copy_inside:Bool flags:# = storage.daemon.TorrentFull;
 | 
			
		||||
storage.daemon.addByHash hash:int256 root_dir:string start_download:Bool allow_upload:Bool priorities:(vector storage.PriorityAction) flags:# = storage.daemon.TorrentFull;
 | 
			
		||||
storage.daemon.addByMeta meta:bytes root_dir:string start_download:Bool allow_upload:Bool priorities:(vector storage.PriorityAction) flags:# = storage.daemon.TorrentFull;
 | 
			
		||||
storage.daemon.setActiveDownload hash:int256 active:Bool = storage.daemon.Success;
 | 
			
		||||
storage.daemon.setActiveUpload hash:int256 active:Bool = storage.daemon.Success;
 | 
			
		||||
 | 
			
		||||
storage.daemon.getTorrents = storage.daemon.TorrentList;
 | 
			
		||||
storage.daemon.getTorrentFull hash:int256 = storage.daemon.TorrentFull;
 | 
			
		||||
storage.daemon.getTorrentMeta hash:int256 = storage.daemon.TorrentMeta;
 | 
			
		||||
storage.daemon.getTorrents flags:# = storage.daemon.TorrentList;
 | 
			
		||||
storage.daemon.getTorrentFull hash:int256 flags:# = storage.daemon.TorrentFull;
 | 
			
		||||
storage.daemon.getTorrentMeta hash:int256 flags:# = storage.daemon.TorrentMeta;
 | 
			
		||||
storage.daemon.getNewContractMessage hash:int256 query_id:long params:storage.daemon.NewContractParams = storage.daemon.NewContractMessage;
 | 
			
		||||
storage.daemon.getTorrentPeers hash:int256 = storage.daemon.PeerList;
 | 
			
		||||
storage.daemon.getTorrentPeers hash:int256 flags:# = storage.daemon.PeerList;
 | 
			
		||||
storage.daemon.getTorrentPiecesInfo hash:int256
 | 
			
		||||
    flags:# // 0 - with file ranges
 | 
			
		||||
    offset:long max_pieces:long
 | 
			
		||||
    = storage.daemon.TorrentPiecesInfo;
 | 
			
		||||
 | 
			
		||||
storage.daemon.setFilePriorityAll hash:int256 priority:int = storage.daemon.SetPriorityStatus;
 | 
			
		||||
storage.daemon.setFilePriorityByIdx hash:int256 idx:long priority:int = storage.daemon.SetPriorityStatus;
 | 
			
		||||
storage.daemon.setFilePriorityByName hash:int256 name:string priority:int = storage.daemon.SetPriorityStatus;
 | 
			
		||||
 | 
			
		||||
storage.daemon.removeTorrent hash:int256 remove_files:Bool = storage.daemon.Success;
 | 
			
		||||
storage.daemon.loadFrom hash:int256 meta:bytes path:string = storage.daemon.Torrent;
 | 
			
		||||
storage.daemon.loadFrom hash:int256 meta:bytes path:string flags:# = storage.daemon.Torrent;
 | 
			
		||||
 | 
			
		||||
storage.daemon.getSpeedLimits flags:# = storage.daemon.SpeedLimits;
 | 
			
		||||
storage.daemon.setSpeedLimits flags:# download:flags.0?double upload:flags.1?double = storage.daemon.Success;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
storage.daemon.importPrivateKey key:PrivateKey = storage.daemon.KeyHash;
 | 
			
		||||
storage.daemon.initProvider account_address:string = storage.daemon.Success;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
										
											Binary file not shown.
										
									
								
							
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue