Смотрел я тут несколько аудитов и отчетов, где присутствовал прокси паттерн и подумал, что вполне можно на канале повторить некоторый материал. Новички смогут что-то новое для себя найти и более продвинутые участники повторить материал.
Transparent Upgradeable Proxy (далее TUP) - это паттерн для обновления прокси контрактов, исключающий возможность столкновения селекторов функций.
Хороший прокси контракт должен обладать как минимум двумя следующими характеристиками:
- слотом для хранения адреса контракта Логики;
- механизмом, позволяющим администратору изменять адрес Логики;
Стандарт ERC1967 устанавливает, где должен находиться слот памяти, содержащий адрес контракта Логики, так, чтобы вероятность коллизии при хранении была минимальной. Однако он не определяет, как обновить сам контракт.
Столкновение селекторов функций (Function Selector Clashing)
Объявление публичных функций внутри прокси для обновления адреса Логики влечет за собой возможность столкновения селекторов функций.
Вот простой пример:
contract ProxyUnsafe {
function changeImplementation(
address newImplementation
) public {
// some code...
}
fallback(bytes calldata data) external payable (bytes memory) {
(bool ok, bytes memory data) = getImplementation().delegatecall(data);
require(ok, "delegatecall failed");
return data;
}
}
contract Implementation {
// an identical function is declared here -- they will clash
function changeImplementation(
address newImplementation
) public {
}
//...
}Помните, что fallback всегда проверяется в последнюю очередь?
Перед вызовом fallback контракт прокси проверит, совпадает ли 4-байтовый селектор функции с changeImplementation (или любой другой публичной функцией в прокси).
И если в прокси объявлена публичная функция, могут возникнуть два вида столкновений селекторов функций:
1. Если контракт Логики реализует функцию с той же подписью, что и функция в прокси, то эта функция не будет вызвана, так как вызов пойдет в сам прокси.
2. Если в контракте Логики есть функция с тем же селектором функций, что и публичная функция в прокси, она также будет невызываемой по той же причине. Этот сценарий представляет собой столкновение селекторов функций. Вероятность того, что у двух разных функций будет одинаковый селектор, равна 1 к 4,29 миллиарда; селектор функции состоит из 4 байт, поэтому существует 4,29 миллиарда возможностей. Это небольшая вероятность, но не нулевая. Например, у clash550254402() тот же селектор функции, что и у proxyAdmin().
А далее мы поговорим, как TUP помогает решить эту проблему.
#proxy #transparent