mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 22:54:01 +02:00
Move blocks order checking to tde2e_api module.
This commit is contained in:
parent
ebe11fdb1e
commit
33a15e69bb
4 changed files with 198 additions and 52 deletions
|
@ -54,10 +54,7 @@ constexpr auto kFixManualLargeVideoDuration = 5 * crl::time(1000);
|
||||||
constexpr auto kFixSpeakingLargeVideoDuration = 3 * crl::time(1000);
|
constexpr auto kFixSpeakingLargeVideoDuration = 3 * crl::time(1000);
|
||||||
constexpr auto kFullAsMediumsCount = 4; // 1 Full is like 4 Mediums.
|
constexpr auto kFullAsMediumsCount = 4; // 1 Full is like 4 Mediums.
|
||||||
constexpr auto kMaxMediumQualities = 16; // 4 Fulls or 16 Mediums.
|
constexpr auto kMaxMediumQualities = 16; // 4 Fulls or 16 Mediums.
|
||||||
constexpr auto kShortPollChainBlocksTimeout = 5 * crl::time(1000);
|
|
||||||
constexpr auto kShortPollChainBlocksPerRequest = 50;
|
constexpr auto kShortPollChainBlocksPerRequest = 50;
|
||||||
constexpr auto kSubChain0 = 0;
|
|
||||||
constexpr auto kSubChain1 = 1;
|
|
||||||
|
|
||||||
[[nodiscard]] const Data::GroupCallParticipant *LookupParticipant(
|
[[nodiscard]] const Data::GroupCallParticipant *LookupParticipant(
|
||||||
not_null<PeerData*> peer,
|
not_null<PeerData*> peer,
|
||||||
|
@ -651,11 +648,10 @@ GroupCall::GroupCall(
|
||||||
_e2e = std::make_shared<TdE2E::Call>(
|
_e2e = std::make_shared<TdE2E::Call>(
|
||||||
TdE2E::MakeUserId(_peer->session().user()));
|
TdE2E::MakeUserId(_peer->session().user()));
|
||||||
}
|
}
|
||||||
for (auto i = 0; i != kSubChainsCount; ++i) {
|
_e2e->subchainRequests(
|
||||||
_subchains[i].timer.setCallback([=] {
|
) | rpl::start_with_next([=](TdE2E::Call::SubchainRequest request) {
|
||||||
checkChainBlocksRequest(i);
|
requestSubchainBlocks(request.subchain, request.height);
|
||||||
});
|
}, _lifetime);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_muted.value(
|
_muted.value(
|
||||||
|
@ -1478,8 +1474,8 @@ void GroupCall::rejoin(not_null<PeerData*> as) {
|
||||||
real->reloadIfStale();
|
real->reloadIfStale();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
checkChainBlocksRequest(kSubChain0);
|
requestSubchainBlocks(0, 0);
|
||||||
checkChainBlocksRequest(kSubChain1);
|
requestSubchainBlocks(1, 0);
|
||||||
}).fail([=](const MTP::Error &error) {
|
}).fail([=](const MTP::Error &error) {
|
||||||
_joinState.finish();
|
_joinState.finish();
|
||||||
|
|
||||||
|
@ -1500,37 +1496,25 @@ void GroupCall::rejoin(not_null<PeerData*> as) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void GroupCall::checkChainBlocksRequest(int subchain) {
|
void GroupCall::requestSubchainBlocks(int subchain, int height) {
|
||||||
Expects(subchain >= 0 && subchain < kSubChainsCount);
|
Expects(subchain >= 0 && subchain < kSubChainsCount);
|
||||||
|
|
||||||
auto &state = _subchains[subchain];
|
auto &state = _subchains[subchain];
|
||||||
if (state.requestId) {
|
_api.request(base::take(state.requestId)).cancel();
|
||||||
return;
|
|
||||||
}
|
|
||||||
const auto now = crl::now();
|
|
||||||
const auto left = state.lastUpdate + kShortPollChainBlocksTimeout - now;
|
|
||||||
if (left > 0) {
|
|
||||||
if (!state.timer.isActive()) {
|
|
||||||
state.timer.callOnce(left);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
state.requestId = _api.request(MTPphone_GetGroupCallChainBlocks(
|
state.requestId = _api.request(MTPphone_GetGroupCallChainBlocks(
|
||||||
inputCall(),
|
inputCall(),
|
||||||
MTP_int(subchain),
|
MTP_int(subchain),
|
||||||
MTP_int(state.height),
|
MTP_int(height),
|
||||||
MTP_int(kShortPollChainBlocksPerRequest)
|
MTP_int(kShortPollChainBlocksPerRequest)
|
||||||
)).done([=](const MTPUpdates &result) {
|
)).done([=](const MTPUpdates &result) {
|
||||||
auto &state = _subchains[subchain];
|
auto &state = _subchains[subchain];
|
||||||
state.lastUpdate = crl::now();
|
|
||||||
state.requestId = 0;
|
|
||||||
_peer->session().api().applyUpdates(result);
|
_peer->session().api().applyUpdates(result);
|
||||||
state.timer.callOnce(kShortPollChainBlocksTimeout + 1);
|
state.requestId = 0;
|
||||||
|
_e2e->subchainBlocksRequestFinished(subchain);
|
||||||
}).fail([=](const MTP::Error &error) {
|
}).fail([=](const MTP::Error &error) {
|
||||||
auto &state = _subchains[subchain];
|
auto &state = _subchains[subchain];
|
||||||
state.lastUpdate = crl::now();
|
|
||||||
state.requestId = 0;
|
state.requestId = 0;
|
||||||
state.timer.callOnce(kShortPollChainBlocksTimeout + 1);
|
_e2e->subchainBlocksRequestFinished(subchain);
|
||||||
}).send();
|
}).send();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2123,15 +2107,11 @@ void GroupCall::handleUpdate(const MTPDupdateGroupCallChainBlocks &data) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto &entry = _subchains[subchain];
|
auto &entry = _subchains[subchain];
|
||||||
entry.lastUpdate = crl::now();
|
const auto inpoll = entry.requestId != 0;
|
||||||
entry.height = data.vnext_offset().v;
|
const auto next = data.vnext_offset().v;
|
||||||
entry.timer.callOnce(kShortPollChainBlocksTimeout + 1);
|
auto now = next - int(data.vblocks().v.size());
|
||||||
for (const auto &block : data.vblocks().v) {
|
for (const auto &block : data.vblocks().v) {
|
||||||
const auto result = _e2e->apply({ block.v });
|
_e2e->apply(subchain, now++, { block.v }, inpoll);
|
||||||
if (result == TdE2E::Call::ApplyResult::BlockSkipped) {
|
|
||||||
AssertIsDebug();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -477,9 +477,6 @@ private:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
struct SubChainState {
|
struct SubChainState {
|
||||||
crl::time lastUpdate = 0;
|
|
||||||
base::Timer timer;
|
|
||||||
int height = 0;
|
|
||||||
mtpRequestId requestId = 0;
|
mtpRequestId requestId = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -543,7 +540,7 @@ private:
|
||||||
void rejoinPresentation();
|
void rejoinPresentation();
|
||||||
void leavePresentation();
|
void leavePresentation();
|
||||||
void checkNextJoinAction();
|
void checkNextJoinAction();
|
||||||
void checkChainBlocksRequest(int subchain);
|
void requestSubchainBlocks(int subchain, int height);
|
||||||
|
|
||||||
void audioLevelsUpdated(const tgcalls::GroupLevelsUpdate &data);
|
void audioLevelsUpdated(const tgcalls::GroupLevelsUpdate &data);
|
||||||
void setInstanceConnected(tgcalls::GroupNetworkState networkState);
|
void setInstanceConnected(tgcalls::GroupNetworkState networkState);
|
||||||
|
|
|
@ -8,14 +8,24 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "tde2e/tde2e_api.h"
|
#include "tde2e/tde2e_api.h"
|
||||||
|
|
||||||
#include "base/assertion.h"
|
#include "base/assertion.h"
|
||||||
|
#include "base/debug_log.h"
|
||||||
|
|
||||||
#include <tde2e/td/e2e/e2e_api.h>
|
#include <tde2e/td/e2e/e2e_api.h>
|
||||||
|
|
||||||
|
#define LOG_ERROR(error) \
|
||||||
|
LOG(("TdE2E Error %1: %2").arg(int(error.code)).arg(error.message.c_str()))
|
||||||
|
|
||||||
|
#define LOG_AND_FAIL(error, reason) \
|
||||||
|
LOG_ERROR(error); \
|
||||||
|
fail(reason)
|
||||||
|
|
||||||
namespace TdE2E {
|
namespace TdE2E {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
constexpr auto kPermissionAdd = 1;
|
constexpr auto kPermissionAdd = 1;
|
||||||
constexpr auto kPermissionRemove = 2;
|
constexpr auto kPermissionRemove = 2;
|
||||||
|
constexpr auto kShortPollChainBlocksTimeout = 5 * crl::time(1000);
|
||||||
|
constexpr auto kShortPollChainBlocksWaitFor = crl::time(1000);
|
||||||
|
|
||||||
[[nodiscard]] tde2e_api::Slice Slice(const QByteArray &data) {
|
[[nodiscard]] tde2e_api::Slice Slice(const QByteArray &data) {
|
||||||
return {
|
return {
|
||||||
|
@ -38,6 +48,11 @@ Call::Call(UserId myUserId)
|
||||||
memcpy(&_myKey, key.value().data(), sizeof(_myKey));
|
memcpy(&_myKey, key.value().data(), sizeof(_myKey));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Call::fail(CallFailure reason) {
|
||||||
|
_failure = reason;
|
||||||
|
_failures.fire_copy(reason);
|
||||||
|
}
|
||||||
|
|
||||||
PublicKey Call::myKey() const {
|
PublicKey Call::myKey() const {
|
||||||
return _myKey;
|
return _myKey;
|
||||||
}
|
}
|
||||||
|
@ -67,22 +82,135 @@ Block Call::makeZeroBlock() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Call::create(const Block &last) {
|
void Call::create(const Block &last) {
|
||||||
tde2e_api::call_create(std::int64_t(_myKeyId.v), Slice(last.data));
|
const auto id = tde2e_api::call_create(
|
||||||
|
std::int64_t(_myKeyId.v),
|
||||||
|
Slice(last.data));
|
||||||
|
if (!id.is_ok()) {
|
||||||
|
LOG_AND_FAIL(id.error(), CallFailure::Unknown);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto i = 0; i != kSubChainsCount; ++i) {
|
||||||
|
auto &entry = _subchains[i];
|
||||||
|
entry.waitingTimer.setCallback([=] {
|
||||||
|
checkWaitingBlocks(i, true);
|
||||||
|
});
|
||||||
|
entry.shortPollTimer.setCallback([=] {
|
||||||
|
shortPoll(i);
|
||||||
|
});
|
||||||
|
entry.shortPollTimer.callOnce(kShortPollChainBlocksTimeout);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Call::ApplyResult Call::apply(const Block &block) {
|
void Call::apply(
|
||||||
|
int subchain,
|
||||||
|
int index,
|
||||||
|
const Block &block,
|
||||||
|
bool fromShortPoll) {
|
||||||
|
Expects(subchain >= 0 && subchain < kSubChainsCount);
|
||||||
|
|
||||||
|
if (!subchain && !_id.v) {
|
||||||
|
create(block);
|
||||||
|
}
|
||||||
|
if (failed()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto &entry = _subchains[subchain];
|
||||||
|
if (!fromShortPoll) {
|
||||||
|
entry.lastUpdate = crl::now();
|
||||||
|
if (index > entry.height + 1) {
|
||||||
|
entry.waiting.emplace(index, block);
|
||||||
|
checkWaitingBlocks(subchain);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const auto result = tde2e_api::call_apply_block(
|
const auto result = tde2e_api::call_apply_block(
|
||||||
std::int64_t(_id.v),
|
std::int64_t(_id.v),
|
||||||
Slice(block.data));
|
Slice(block.data));
|
||||||
|
|
||||||
if (!result.is_ok()) {
|
if (!result.is_ok()) {
|
||||||
const auto error = result.error();
|
LOG_AND_FAIL(result.error(), CallFailure::Unknown);
|
||||||
(void)error;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
return result.is_ok()
|
entry.height = std::max(entry.height, index);
|
||||||
? ApplyResult::Success
|
checkWaitingBlocks(subchain);
|
||||||
: ApplyResult::BlockSkipped;
|
}
|
||||||
|
|
||||||
|
void Call::checkWaitingBlocks(int subchain, bool waited) {
|
||||||
|
Expects(subchain >= 0 && subchain < kSubChainsCount);
|
||||||
|
|
||||||
|
if (failed()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto &entry = _subchains[subchain];
|
||||||
|
if (entry.shortPolling) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto &waiting = entry.waiting;
|
||||||
|
entry.shortPollTimer.cancel();
|
||||||
|
while (!waiting.empty()) {
|
||||||
|
const auto level = waiting.begin()->first;
|
||||||
|
if (level > entry.height + 1) {
|
||||||
|
if (waited) {
|
||||||
|
shortPoll(subchain);
|
||||||
|
} else {
|
||||||
|
entry.waitingTimer.callOnce(kShortPollChainBlocksWaitFor);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
} else if (level == entry.height + 1) {
|
||||||
|
const auto result = tde2e_api::call_apply_block(
|
||||||
|
std::int64_t(_id.v),
|
||||||
|
Slice(waiting.begin()->second.data));
|
||||||
|
if (!result.is_ok()) {
|
||||||
|
LOG_AND_FAIL(result.error(), CallFailure::Unknown);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
entry.height = level;
|
||||||
|
}
|
||||||
|
waiting.erase(waiting.begin());
|
||||||
|
}
|
||||||
|
entry.waitingTimer.cancel();
|
||||||
|
entry.shortPollTimer.callOnce(kShortPollChainBlocksTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Call::shortPoll(int subchain) {
|
||||||
|
Expects(subchain >= 0 && subchain < kSubChainsCount);
|
||||||
|
|
||||||
|
auto &entry = _subchains[subchain];
|
||||||
|
entry.waitingTimer.cancel();
|
||||||
|
entry.shortPollTimer.cancel();
|
||||||
|
entry.shortPolling = true;
|
||||||
|
_subchainRequests.fire({ subchain, entry.height });
|
||||||
|
}
|
||||||
|
|
||||||
|
rpl::producer<Call::SubchainRequest> Call::subchainRequests() const {
|
||||||
|
return _subchainRequests.events();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Call::subchainBlocksRequestFinished(int subchain) {
|
||||||
|
Expects(subchain >= 0 && subchain < kSubChainsCount);
|
||||||
|
|
||||||
|
if (failed()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto &entry = _subchains[subchain];
|
||||||
|
entry.shortPolling = false;
|
||||||
|
checkWaitingBlocks(subchain);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<CallFailure> Call::failed() const {
|
||||||
|
return _failure;
|
||||||
|
}
|
||||||
|
|
||||||
|
rpl::producer<CallFailure> Call::failures() const {
|
||||||
|
if (_failure) {
|
||||||
|
return rpl::single(*_failure);
|
||||||
|
}
|
||||||
|
return _failures.events();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<uint8_t> Call::encrypt(const std::vector<uint8_t> &data) const {
|
std::vector<uint8_t> Call::encrypt(const std::vector<uint8_t> &data) const {
|
||||||
|
|
|
@ -8,6 +8,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "base/basic_types.h"
|
#include "base/basic_types.h"
|
||||||
|
#include "base/timer.h"
|
||||||
|
|
||||||
|
#include <rpl/producer.h>
|
||||||
|
#include <rpl/event_stream.h>
|
||||||
|
|
||||||
|
#include <crl/crl_time.h>
|
||||||
|
|
||||||
namespace TdE2E {
|
namespace TdE2E {
|
||||||
|
|
||||||
|
@ -39,6 +45,10 @@ struct Block {
|
||||||
QByteArray data;
|
QByteArray data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class CallFailure {
|
||||||
|
Unknown,
|
||||||
|
};
|
||||||
|
|
||||||
class Call final {
|
class Call final {
|
||||||
public:
|
public:
|
||||||
explicit Call(UserId myUserId);
|
explicit Call(UserId myUserId);
|
||||||
|
@ -49,11 +59,21 @@ public:
|
||||||
|
|
||||||
void create(const Block &last);
|
void create(const Block &last);
|
||||||
|
|
||||||
enum class ApplyResult {
|
void apply(
|
||||||
Success,
|
int subchain,
|
||||||
BlockSkipped
|
int index,
|
||||||
|
const Block &block,
|
||||||
|
bool fromShortPoll);
|
||||||
|
|
||||||
|
struct SubchainRequest {
|
||||||
|
int subchain = 0;
|
||||||
|
int height = 0;
|
||||||
};
|
};
|
||||||
[[nodiscard]] ApplyResult apply(const Block &block);
|
[[nodiscard]] rpl::producer<SubchainRequest> subchainRequests() const;
|
||||||
|
void subchainBlocksRequestFinished(int subchain);
|
||||||
|
|
||||||
|
[[nodiscard]] std::optional<CallFailure> failed() const;
|
||||||
|
[[nodiscard]] rpl::producer<CallFailure> failures() const;
|
||||||
|
|
||||||
[[nodiscard]] std::vector<uint8_t> encrypt(
|
[[nodiscard]] std::vector<uint8_t> encrypt(
|
||||||
const std::vector<uint8_t> &data) const;
|
const std::vector<uint8_t> &data) const;
|
||||||
|
@ -61,10 +81,31 @@ public:
|
||||||
const std::vector<uint8_t> &data) const;
|
const std::vector<uint8_t> &data) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
static constexpr int kSubChainsCount = 2;
|
||||||
|
|
||||||
|
struct SubChainState {
|
||||||
|
base::Timer shortPollTimer;
|
||||||
|
base::Timer waitingTimer;
|
||||||
|
crl::time lastUpdate = 0;
|
||||||
|
base::flat_map<int, Block> waiting;
|
||||||
|
bool shortPolling = true;
|
||||||
|
int height = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
void fail(CallFailure reason);
|
||||||
|
|
||||||
|
void checkWaitingBlocks(int subchain, bool waited = false);
|
||||||
|
void shortPoll(int subchain);
|
||||||
|
|
||||||
CallId _id;
|
CallId _id;
|
||||||
UserId _myUserId;
|
UserId _myUserId;
|
||||||
PrivateKeyId _myKeyId;
|
PrivateKeyId _myKeyId;
|
||||||
PublicKey _myKey;
|
PublicKey _myKey;
|
||||||
|
std::optional<CallFailure> _failure;
|
||||||
|
rpl::event_stream<CallFailure> _failures;
|
||||||
|
|
||||||
|
SubChainState _subchains[kSubChainsCount];
|
||||||
|
rpl::event_stream<SubchainRequest> _subchainRequests;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue