Polygon Warrior Polygon Security Dialysis: как обеспечить безопасность и стабильность кроссчейна?

Автор: Чэнду Ляньань
Оригинал: “Исследование безопасности кроссчейн-мостов (3)” | Безопасный диализ Polygon Warrior, как предотвратить открытие «ящика Пандоры»?

Добро пожаловать в серию статей «Исследование безопасности кроссчейн-мостов», подготовленную Chengdu Chain Security, в предыдущей статье (Cross-Chain Bridge Security Research (2) | Что приносит нам первое децентрализованное ограбление моста Nomad?), мы проводим профессиональный технический анализ протокола моста Nomad в деталях.

Сегодня исследовательская группа Chengdu Chain Security проведет безопасный диализ Polygon, полигонального воина, так что продолжайте читать.

Кто такой 1_Polygon?

Polygon — это решение для масштабирования уровня 2 Ethereum, целью которого является создание блокчейн-интернета Ethereum. Polygon предоставляет общую структуру, которая позволяет разработчикам использовать безопасность Ethereum для создания настраиваемых, ориентированных на приложения цепочек и предоставления функционально совместимой сети, сочетающей в себе множество различных схем масштабирования, таких как: zk-rollup, PoS и т. д. Среди них Polygon PoS в настоящее время является самым зрелым и известным решением для масштабирования на Polygon. Он использует сайдчейн для обработки транзакций для достижения цели повышения скорости транзакций и экономии потребления газа, а структура сети в основном включает в себя следующие три уровня:

多边形战士Polygon安全透析:如何保证跨链的安全性和稳定性?

Уровень Ethereum:

Ряд контрактов в основной сети Ethereum, в основном включая: контракты стейкинга, контрольной точки и вознаграждения, отвечают за функции управления стейкингом, связанные со ставками PoS, в том числе: обеспечение функции стейкинга нативного токена MATIC, чтобы любой, кто стейкает токен, мог присоединиться к системе в качестве валидатора, проверка преобразования сети Polygon для получения вознаграждения за стейкинг, наказание валидаторов за двойные подписи, простои валидатора и другое незаконное поведение, а также сохранение контрольных точек.

Слой Хеймдалля:

Уровень валидатора Proof-of-Stake, состоящий из набора узлов PoS Heimdall, отвечает за отправку контрольных точек сети Polygon в основную сеть Ethereum при прослушивании набора контрактов стейкинга, развернутых на Ethereum. Основной процесс выглядит следующим образом: сначала выбирается подмножество активных валидаторов в пуле валидаторов в качестве производителей блоков, которые будут отвечать за создание блоков на уровне Bor и их трансляцию, затем валидация корневого хэша Меркла и добавление подписей на основе контрольных точек, представленных Bor, и, наконец, и, наконец, инициатор будет отвечать за сбор всех подписей валидаторов для указанной контрольной точки, и если количество подписей достигнет более 2/3, контрольная точка будет отправлена на Ethereum.

Бор Слой:

Уровень блок-продюсеров, который состоит из группы блок-продюсеров, регулярно выбираемых комитетом валидаторов на уровне Heimdall, представляет собой подмножество валидаторов, отвечающих за агрегацию транзакций в сайдчейне Polygon и генерацию блоков. Этот слой периодически публикует контрольные точки в слое Хеймдалля, где контрольная точка представляет собой моментальный снимок цепочки Бора, как показано на рисунке ниже.

多边形战士Polygon安全透析:如何保证跨链的安全性和稳定性?

2_Polygon Функциональная совместимость

2.1 Контрольная точка

Механизм контрольных точек — это механизм синхронизации данных слоя Bor с Ethereum, где синхронизированные данные являются контрольной точкой, то есть снимком данных блока слоя Bor, содержащимися в интервале контрольной точки, исходный код выглядит следующим образом:

多边形战士Polygon安全透析:如何保证跨链的安全性和稳定性?

Proposer: Proposer, который также выбирается валидаторами, блок-продюсерами и пропозиторами являются подмножеством валидаторов, и их обязанности определяются их долей в общем пуле

RootHash: это хэш Меркла, сгенерированный из блока Bor между StartBlock и EndBlock

Ниже приведен псевдокод для блока Bor с номерами от 1 до n для генерации значения RootHash:

多边形战士Polygon安全透析:如何保证跨链的安全性和稳定性?

Таким образом, это значение является корневым хеш-значением дерева Меркеля, которое состоит из номера блока в заголовке блока Bor, метки времени блока, корневого хеш-значения дерева транзакций tx и хеш-значения keccak256, вычисленного из корневого хэша дерева поступлений.

AccountRootHash: Хэш Меркла информации об учетной записи, связанной с валидатором, который должен быть отправлен в каждую контрольную точку в Ethereum, и хэш-значение отдельной информации об учетной записи вычисляется следующим образом:

多边形战士Polygon安全透析:如何保证跨链的安全性和稳定性?

AccountRootHash генерируется из корневого хэша дерева Меркла учетной записи так же, как и значение RootHash.

2.2 StateSync

StateSync относится к синхронизации данных Ethereum с цепочкой Polygon Matic, которая в основном разделена на следующие этапы:

  1. Во-первых, контракт на Ethereum запустит функцию syncState() в StateSender.sol для синхронизации состояний

  2. Функция syncState() выдаст событие следующим образом:

多边形战士Polygon安全透析:如何保证跨链的安全性和稳定性?

  1. Все валидаторы в слое Heimdall получат событие, а один из валидаторов упакует транзакцию в блок heimdall и добавит ее в список синхронизации ожидающих состояний;

  2. Узел уровня bor получит вышеуказанный список для синхронизации через API, и передаст его контракту уровня bor для дальнейшей обработки бизнес-логики.

2.3 Многоугольный мост

Polygon Bridge обеспечивает двусторонний кроссчейн-канал между Polygon и Ethereum, упрощая пользователям передачу токенов между двумя разными платформами без сторонних угроз и ограничений ликвидности рынка. Существует два типа Polygon Bridge, PoS и Plasma, и оба имеют следующие сходства при передаче активов между Polygon и Ethereum:

  1. Для начала нужно сопоставить токен на Ethereum с Polygon, как показано на изображении ниже:

多边形战士Polygon安全透析:如何保证跨链的安全性和稳定性?

  1. Также используется технология двусторонней анкеровки (Two-way Peg), т.е.

a: Все токен-активы, переведенные из Ethereum, сначала будут заблокированы на Ethereum, и такое же количество сопоставленных токенов будет отчеканено на Polygon;

b: Для того, чтобы вывести активы токенов в Ethereum, вам сначала нужно сжечь эти сопоставленные токены на Polygon, а затем разблокировать активы, заблокированные в Ethereum;

На следующем рисунке показано сравнение PoS Bridge и Plasma Bridge:

多边形战士Polygon安全透析:如何保证跨链的安全性和稳定性?

Как видно из приведенного выше рисунка, с точки зрения безопасности PoS Bridge полагается на безопасность внешнего набора валидаторов, в то время как Plasma полагается на безопасность основной цепочки Ethereum. В то же время, когда пользователи совершают кроссчейн-переводы активов (например, переводят токены из Polygon в Ethereum), PoS требует только интервала контрольных точек, от 20 минут до 3 часов, в то время как Plasma требует 7-дневного периода оспаривания споров. В то же время PoS поддерживает больше стандартных токенов, в то время как Plasma поддерживает только три типа, в том числе: ETH, ERC20, ERC721.

3_Cross-цепочка сообщений — PoS-мост

PoS Bridge в основном состоит из двух функций: Депозит относится к переводу активов пользователей с Ethereum на Polygon, а Вывод средств относится к выводу активов с Polygon на Ethereum.

Депозит

Ниже приведен пример пользователя Алисы, использующего PoS Bridge для отправки токенов со своей учетной записи Ethereum на свою учетную запись Polygon:

  1. Если активы токенов, которые вы хотите передать, являются ERC20, ERC721 или ERC1155, вам необходимо авторизовать токен, который вы хотите перевести, через функцию approtect. Как показано ниже, соответствующее количество токенов авторизуется в контракте erc20Prefer путем вызова метода approtect в контракте токена на Ethereum.

多边形战士Polygon安全透析:如何保证跨链的安全性和稳定性?

Функция approtect имеет два параметра:

  • Spender: адрес назначения, по которому пользователь разрешает тратить токены.

  • amount: Количество токенов, которые можно потратить

  1. После того, как вышеуказанная транзакция авторизации подтверждена, пользователь блокирует токен в контракте erc20Preliminary на Ethereum, вызывая метод depositFor() контракта RootChainManager. Здесь, если тип передаваемого актива — ETH, вызывается depositEtherFor(). Подробная информация приведена ниже:

多边形战士Polygon安全透析:如何保证跨链的安全性和稳定性?

Функция depositFor имеет три параметра:

  • user: Адрес пользователя, получившего токены депозита на Polygon

  • rootToken: адрес токена в основной цепочке Ethereum

  • depositData: Количество токенов, закодированных ABI

Ниже приведен конкретный код функции depositFor в контракте RootChainManager:

多边形战士Polygon安全透析:如何保证跨链的安全性和稳定性?

多边形战士Polygon安全透析:如何保证跨链的安全性和稳定性?

Анализируя исходный код, можно увидеть, что функция сначала получает адрес контракта предиката, соответствующий токену, а затем вызывает свою функцию lockTokens() для блокировки токена в контракте. Наконец, syncState() будет вызван для синхронизации состояния с помощью _stateSender, который может быть вызван только отправителем состояния, установленным администратором.

  1. Функция syncState() в StateSender.sol отправит событие StateSynced, а именно:

多边形战士Polygon安全透析:如何保证跨链的安全性和稳定性?

Первый параметр — это индекс порядкового номера журнала, второй параметр используется для проверки того, является ли вызывающий объект зарегистрированным легитимным адресом контракта, а третий параметр — данные, которые необходимо синхронизировать с состоянием. Транзакция добавляется в блок Хеймдалля и добавляется в список синхронизации ожидающих состояний.

多边形战士Polygon安全透析:如何保证跨链的安全性和稳定性?

  1. Затем, после того, как узел bor в цепочке Polygon Matic получит событие StateSynced в списке синхронизации состояний через API, контракт ChildChainManager в цепочке вызовет функцию onStateReceive(), которая используется для получения данных синхронизации, загруженных из Ethereum, и перейдет к следующему шагу в соответствии с типом бизнес-логики синхронизации состояний:

多边形战士Polygon安全透析:如何保证跨链的安全性和稳定性?

data: bytes32 syncType и bytes syncData. При сопоставлении syncType syncData является закодированным адресом rootToken, адресом childToken и bytes32 tokenType, а если syncType имеет значение deposit, syncData является закодированным адресом пользователя. Адрес rootToken и depositData типа bytes. depositData — это количество в REC20 и tokenId в ERC721.

多边形战士Polygon安全透析:如何保证跨链的安全性和稳定性?

  1. Так как это депозитный бизнес, то будет вызвана функция _syncDeposit(). Эта функция сначала декодирует syncData в соответствующем формате, чтобы получить соответствующий rootToken, адрес пользователя и depositData. Затем проверьте, есть ли у rootToken соответствующий токен отображения на полигоне, и вызовите функцию deposit() childToken, если он есть.

多边形战士Polygon安全透析:如何保证跨链的安全性和稳定性?

  1. Здесь мы возьмем контракт токена ERC20 в качестве примера, чтобы показать, как депонировать контракт токена сопоставления. Эта функция переводит соответствующее количество токенов на счет пользователя.

多边形战士Polygon安全透析:如何保证跨链的安全性和稳定性?

Функция имеет два параметра:

  • user: Адрес пользователя, который вносит депозит

  • depositData: Сумма, закодированная в ABI

Вывод средств

Ниже приведен пример того, как пользователь Алиса использует PoS Bridge для вывода средств, внесенных на ее счет Polygon, на свой счет Ethereum:

  1. Когда пользователь выводит средства, ему необходимо сжечь соответствующее количество сопоставленных токенов, вызвав функцию withdraw() контракта токена сопоставления в цепочке Polygon.

多边形战士Polygon安全透析:如何保证跨链的安全性和稳定性?

Withdraw содержит только один параметр: количество токенов, которые будут сожжены. Функция withdraw() в соответствующем контракте токена выглядит следующим образом:

多边形战士Polygon安全透析:如何保证跨链的安全性和稳定性?

  1. Вышеуказанные транзакции будут включены в контрольную точку примерно через 20 минут или 3 часа, и валидатор отправит их в Ethereum.

  2. После того, как транзакция будет добавлена в контрольную точку и отправлена в Ethereum, будет вызвана функция exit() контракта RootChainManager на Ethereum, которая подтвердит действительность транзакции вывода средств на Polygon, проверив содержимое отправленной контрольной точки, и запустит соответствующий контракт Predicate для разблокировки депонированных токенов пользователя.

Входные данные Proof proof, передаваемые в функцию, включают следующие данные:

  • headerNumber: Содержит заголовок блока контрольной точки для транзакции вывода средств.

  • blockProof: Доказать, что заголовок блока в дочерней цепочке является конечным узлом корня Меркла, который зафиксировал

  • blockNumber: номер блока в дочерней цепочке, содержащей транзакцию вывода средств.

  • blockTime: Временная метка блока транзакции вывода средств

  • txRoot: Корневое значение дерева транзакций блока

  • receiptRoot: Корневое значение дерева поступлений блока

  • Квитанция: квитанция о транзакции по выводу средств.

  • receiptProof: подтверждение Merck о получении транзакции по выводу средств.

branchMask: путь к квитанции, представленный 32 битами в дереве квитанции.

  • receiptLogIndex: Индекс журнала, считываемый из дерева квитанций

Ниже приведена основная логика функции, которая в основном состоит из трех частей: первая часть предназначена для проверки действительности получения транзакции вывода, вторая часть — для проверки того, содержит ли контрольная точка блок транзакции, и третья часть — для вызова функции exitTokens() в предикатном контракте для отправки заблокированных токенов пользователю.

多边形战士Polygon安全透析:如何保证跨链的安全性和稳定性?

  1. Возьмем в качестве примера контракт ERC20Predicate, то есть после расшифровки получателя, отправителя и количества токенов, отправленных из журнала, пользователю будет отправлено заданное количество токенов.

多边形战士Polygon安全透析:如何保证跨链的安全性和稳定性?

Согласно анализу исходного кода процесса обмена сообщениями между PoS Bridge, вызовы функций всего процесса могут быть вызваны только ролью, указанной валидатором, поэтому безопасность кроссчейна гарантируется только PoS (нотариусом).

Обмен сообщениями из 4_Cross — Plasma Bridge

Plasma Bridge также включает в себя две функции: Депозит и Вывод средств, как показано на следующем рисунке:

多边形战士Polygon安全透析:如何保证跨链的安全性和稳定性?

Polygon Plasma немного отличается от реализации Bitcoin Plasma MVP, представленной в первой статье нашей серии кроссчейн-мостов, которая в основном использует модель Plasma MoreVP на основе учетной записи. По сравнению с Plasma, алгоритм был частично улучшен в части вывода.

Поскольку передача токенов ERC20 и ERC721 осуществляется через журнал событий, похожий на Bitcoin UTXO, давайте сначала представим событие:

  • input1: Баланс счета отправителя до перевода

  • input2: Остаток на счете получателя до перевода

  • output1: Баланс счета отправителя после перевода

  • output2: Баланс счета получателя после перевода

多边形战士Polygon安全透析:如何保证跨链的安全性和稳定性?

Во-вторых, из-за того, что оригинальный MVP Plasma генерируется одним оператором или небольшим количеством блок-продюсеров, на Polygon существует два сценария атаки:

Зло Оперативника:

Предыдущая статья (Исследование безопасности кроссчейн-моста (2) | Nomad cross-chain bridge) упоминает, что когда транзакция пользователя упаковывается в блок Plasma Оператором, происходит недоступность данных вне сети. Поэтому, когда пользователь совершает транзакцию выхода, если он начинает выводить средства из более старой транзакции, Оператор может оспорить ее одной из своих последних транзакций, и оспаривание будет успешным. В то же время, благодаря механизму контрольных точек PoS, используемому в Plasma, если Оператор вступает в сговор с валидаторами, чтобы творить зло, они могут даже подделать некоторые переходы состояний и отправить их в Ethereum.

Зло пользователя:

Пользователи продолжают тратить токены на Polygon после инициирования транзакции выхода, аналогично кроссчейн-двойному расходованию.

Таким образом, алгоритм Plasma MoreVp от Polygon использует другой алгоритм для вычисления приоритета выхода, который заключается в выходе из самой последней транзакции. Поскольку этот метод использует событие LogTransfer, аналогичное UTXO, если допустимая транзакция пользователя использует правильные входные данные input1 и input2, даже если некоторые вредоносные транзакции упаковываются перед транзакцией пользователя, транзакция пользователя может быть обработана правильно, поскольку транзакция пользователя поступает только из допустимых входных данных. Соответствующий псевдокод выглядит следующим образом:

多边形战士Polygon安全透析:如何保证跨链的安全性和稳定性?

Депозит

Возьмем, к примеру, пользователя Алису, использующего Plasma Bridge для отправки токенов со своего аккаунта Ethereum на аккаунт Polygon:

  1. Прежде всего, пользователям также необходимо авторизовать токен-активы, которые им необходимо перевести в depositManager контракта Polygon на основной цепочке (Ethereum) через функцию approtect.

  2. После подтверждения авторизованной транзакции пользователь вызывает функцию erc20token.deposit(), чтобы активировать функцию depositERC20ForUser() контракта depositManager и внести активы токенов ERC20 пользователя.

多边形战士Polygon安全透析:如何保证跨链的安全性和稳定性?

  1. Когда основная сеть Ethereum подтвердит транзакцию депозита, она создаст блок, содержащий только эту транзакцию, и отправит его на контракт childChain в сети Polygon с помощью механизма синхронизации состояний, отчеканит такое же количество нанесенных монет и внесет их на счет пользователя на Polygon.

多边形战士Polygon安全透析:如何保证跨链的安全性和稳定性?

Примечание: Согласно анализу исходного кода контракта childChain, Plasma поддерживает только три типа, в том числе: ETH, ERC20 и ERC721.

Выйти

Когда пользователь захочет использовать мост Plasma для вывода активов с Polygon на Ethereum, он выполнит следующие шаги:

  1. Пользователь сжигает сопоставленные активы токенов в цепочке Polygon, вызывая функцию withdraw() сопоставленной монеты на Polygon:

多边形战士Polygon安全透析:如何保证跨链的安全性和稳定性?

Вы также можете вызвать реализацию интерфейса withdrawStart() клиента Plasma на Polygon.

  1. Пользователь может вызвать функцию startExitWithBurntTokens() в контракте ERC20Predicate, которая сначала вызовет WithdrawManager.verifyInclusion(), чтобы проверить, содержит ли контрольная точка транзакцию вывода и соответствующую квитанцию, код выглядит следующим образом:

多边形战士Polygon安全透析:如何保证跨链的安全性和稳定性?

После того, как проверка будет пройдена, будет вызван WithdrawManager.addExitToQueue() для вставки его в очередь сообщений в порядке приоритета:

多边形战士Polygon安全透析:如何保证跨链的安全性和稳定性?

Наконец, addExitToQueue() вызывает _addExitToQueue() для создания NFT в качестве ваучера на возврат:

多边形战士Polygon安全透析:如何保证跨链的安全性和稳定性?

  1. Пользователь ожидает 7-дневный период челленджа

  2. После завершения периода челленджа можно вызвать функцию WithdrawManager.processExits() для отправки токенов пользователю.

Функция разделена на два шага: сначала подтвердить, прошла ли транзакция вывода в очереди сообщений 7-дневный период вызова, и если период вызова прошел, удалить транзакцию из очереди:

多边形战士Polygon安全透析:如何保证跨链的安全性和稳定性?

Затем определите, был ли NFT-ваучер на возврат был удален в течение периода челленджа, и если нет, NFT будет уничтожен, а соответствующие активы возвращены пользователю:

多边形战士Polygon安全透析:如何保证跨链的安全性和稳定性?

5_Polygon Уязвимость Plasma Bridge к двойному расходованию

5 октября 2021 года White Hat Герхард Вагнер сообщил об уязвимости Polygon, которая могла привести к атаке с двойным расходованием на сумму 850 миллионов долларов, за что White Hat получила официальное вознаграждение за ошибку в размере 2 000 000 долларов от Polygon.

Из приведенного выше введения в Plasma Bridge мы знаем, что полный процесс транзакции вывода выглядит следующим образом:

- Пользователь инициирует транзакцию Withdraw на Polygon, которая сжигает токены пользователя на Polygon;

- Через интервал контрольной точки (примерно 30 минут) подождите, пока транзакция вывода средств будет включена в контрольную точку;

- Более 2/3 валидаторов подписывают и отправляют его в Ethereum, после чего пользователь вызывает startExitWithBurntTokens() в контракте ERC20PredicateBurnOnly, чтобы проверить, содержит ли контрольная точка транзакции сжигания;

- Если верификация пройдена, будет отчеканен ваучер на возврат NFT и отправлен пользователю

Пользователи ожидают 7-дневный период испытания

- Вызовите WithdrawManager.processExits(), чтобы уничтожить NFT и вернуть деньги пользователю

Примечание: Чтобы предотвратить повторное воспроизведение транзакций (атаки двойной траты), Polygon использует NFT в качестве доказательства возврата, чтобы однозначно идентифицировать транзакцию вывода средств. Однако из-за дефекта генерации NFT ID злоумышленники могут создавать параметры для генерации нескольких NFT с разными идентификаторами, используя одну и ту же действительную транзакцию вывода, а затем использовать эти NFT для транзакций возврата, тем самым достигая «атаки двойного расходования».

Вот более подробное описание того, как генерировать NFT:

  1. Из приведенного выше анализа исходного кода видно, что addExitToQueue() вызовет _addExitToQueue() для минтинга NFT:

多边形战士Polygon安全透析:如何保证跨链的安全性和稳定性?

Согласно анализу параметров, exitid = priority, то идентификатор NFT генерируется левым сдвигом приоритета возраста в Plasma Bridge.

多边形战士Polygon安全透析:如何保证跨链的安全性和稳定性?

  1. Как видно из приведенного выше анализа исходного кода, age — это возвращаемое значение функции WithdrawManager.verifyInclusion(), которая сначала проверит валидность транзакции вывода, а затем сгенерирует соответствующий возраст, если проверка пройдена. В логике валидации значение, декодируемое данными контролируемого параметра, имеет вид branchMaskBytes:

多边形战士Polygon安全透析:如何保证跨链的安全性和稳定性?

Это значение также используется при генерации возраста:

多边形战士Polygon安全透析:如何保证跨链的安全性和稳定性?

  1. Отследим функцию MerklePatriciaProof.verify(), вызываемую в логике проверки транзакций, и найдем, что функция вызывает _getNibbleArray() для перекодирования branchMaskBytes:

多边形战士Polygon安全透析:如何保证跨链的安全性和稳定性?

  1. Продолжаем отслеживать функцию декодирования, которая отбрасывает часть значения при перекодировании branchMaskBytes, и такой способ потери значения приведет к тому, что разные значения будут перекодированы для получения одного и того же декодированного значения. В частности, если первый шестнадцатеричный бит (половина байта) входящего значения b в кодировке hp равен 1 или 3, анализируется второй шестнадцатеричный бит. В противном случае первый байт просто игнорируется.

多边形战士Polygon安全透析:如何保证跨链的安全性和稳定性?

Если злоумышленник конструирует параметр branchMaskBytes так, что первый шестнадцатеричный бит не равен 1 и 3, то существует 14*16 = 224 способа получить одно и то же перекодированное значение.

Конкретный процесс атаки выглядит следующим образом:

- Внесите большое количество ETH/токенов в Polygon через Polygon Plasma

Выведите средства на Polygon и дождитесь 7-дневного периода оспаривания

- Изменить первый байт параметра branchMaskBytes в транзакции вывода (одна и та же действительная транзакция может быть повторно отправлена до 223 раз) и инициировать транзакцию вывода повторно

Таким образом, эта уязвимость в основном связана с проблемой в разработке алгоритма идентификации NFT, который генерирует ваучер на возврат для предотвращения повторного воспроизведения, в результате чего одна и та же транзакция возврата может генерировать разные NFT, что приводит к атаке двойного расходования. Получается, что первый байт закодированной маски ветвления всегда должен быть 0x00. Исправление заключается в том, чтобы проверить, является ли первый байт закодированной маски ветви 0x00, и не рассматривать его как неправильную маску.

多边形战士Polygon安全透析:如何保证跨链的安全性和稳定性?

Что ж, сегодняшний обмен окончен, в следующем выпуске исследовательская группа Chengdu Chain Security Security представит исследование безопасности еще одного кроссчейн-проекта, пожалуйста, с нетерпением ждите этого.

Посмотреть Оригинал
На этой странице может содержаться сторонний контент, который предоставляется исключительно в информационных целях (не в качестве заявлений/гарантий) и не должен рассматриваться как поддержка взглядов компании Gate или как финансовый или профессиональный совет. Подробности смотрите в разделе «Отказ от ответственности» .
  • Награда
  • комментарий
  • Репост
  • Поделиться
комментарий
0/400
Нет комментариев
  • Закрепить