Перейти к содержанию

msg.sender в Solidity: кто вызывает функцию

Адрес издателя уже хранится в экземпляре. Чтобы разрешить ему публикацию, нужно определить адрес участника, который обращается к функции сейчас. В Solidity эту роль играет msg.sender. Разберём значение на прямом вызове и на цепочке через другой контракт.

На сайте можно показывать имя «редактор», но контракт не читает эту подпись. Проверка работает независимо от интерфейса. Поэтому её нужно понять даже тому, кто собирается выполнять все действия только через собственную страницу управления библиотекой.

Текущая граница вызова

В простом случае адрес P отправляет транзакцию непосредственно в реестр C. В исполняемом методе реестра msg.sender равен P. Если издателем выбран именно P, адреса совпадают. Если читатель R вызывает тот же метод, msg.sender равен R, и совпадения нет.

if (msg.sender != publisher) revert Unauthorized();

Это фрагмент будущего модификатора. Он не проверяет наличие кошелька в браузере и не обращается к серверной сессии. Участник может пользоваться другим клиентом, но значение отправителя определяется исполнением EVM. Поэтому скрытие кнопки публикации полезно для удобства интерфейса, а не как единственная мера ограничения.

Теперь добавим посредника. Пусть читатель обращается к редакционному контракту M, а тот вызывает реестр. Внутри M отправитель может быть R; внутри C отправитель — M. Значение относится к непосредственной границе каждого вызова, а не к одному постоянному имени пользователя на всю транзакцию.

R → M.requestPublication()
    M видит msg.sender = R

M → C.publish(17, H1)
    C видит msg.sender = M

Если реестр доверяет M, именно M должен обеспечить собственные правила назначения действия. Нельзя сделать общедоступный метод посредника, который без проверки публикует любые параметры от имени привилегированного M. Реестр честно проверит свой адрес, но система в целом получит неправильные полномочия через другой путь.

Адрес издателя может принадлежать контракту

В исходном CourseCatalog можно назначить контракт управления издателем. Например, мультиподпись согласует действие несколькими участниками и вызывает publish со своего адреса. Тогда реестр не обязан знать список внутренних подписантов. Он видит субъект полномочия, выбранный конструктором.

Это позволяет разделить роли систем. Реестр хранит выпуски, управление решает, когда от имени издателя допустим вызов. Но такая композиция требует прочитать обе модели. Ошибка в управлении не исчезает благодаря короткой и правильной проверке в реестре. При потере внутренних ключей возможность восстановления также зависит от механизма управления.

У смарт-аккаунтов и современных механизмов делегирования есть дополнительные особенности. Поэтому не стоит ограничивать доступ только участниками без кода или делать проверку типа аккаунта вместо проверки разрешения. Для текущей задачи достаточно сравнения с явно назначенным субъектом. Его техническое устройство не меняет смысл этого условия.

Почему не tx.origin

tx.origin относится к происхождению транзакции в предусмотренной моделью языка форме. Его использование для полномочий даёт другой результат при посредниках. Даже если первоначальный участник является издателем, промежуточный контракт может попытаться выполнить действие, которое издатель непосредственно реестру не поручал.

Для учебной библиотеки требуется разрешение непосредственному субъекту publisher, а не любому коду в цепочке, начатой его участием. Поэтому базовая проверка использует msg.sender. Подробный разбор ошибочной модели будет в отдельном курсе безопасности; здесь важно сохранить правильное назначение переменной с первого листинга.

Также не нужно записывать publisher = tx.origin в конструкторе ради удобства технического размещения. Адрес издателя передаётся явным аргументом. Это поддерживает мультиподпись и не связывает право с неявным происхождением всей транзакции.

Порядок выполнения проверок

Проверка отправителя должна происходить на каждом пути изменения. Если автор добавит альтернативную функцию publishFor, которая пишет тот же mapping без условия, защита publish не распространяется на неё автоматически. Полномочие является свойством маршрутов к состоянию, а не единственной красивой строки в файле.

Учитывайте и внутренние вызовы. Внутренняя вспомогательная функция исполняется в том же контексте, тогда как обращение this.someFunction() создаёт внешний вызов к собственному адресу и может изменить непосредственного отправителя в вызываемом методе. Эти формы не следует заменять друг на друга без разбора условий доступа и стоимости.

Маршрут Sender внутри реестра Пройдёт при publisher=P
P напрямую вызывает C P Да
R напрямую вызывает C R Нет
P вызывает M, M вызывает C M Только если издателем выбран M
C внешне вызывает свой метод C Только при подходящей отдельной модели

Последняя строка не предлагает назначать реестр собственным издателем. Она показывает, почему внешний вызов самого себя имеет собственный контекст. Для обычной организации логики используем внутренний помощник, когда именно такая композиция нужна.

Ожидаемое наблюдение

Сохраним начальную запись H1,true. Вызов читателя с H2 должен откатиться до присваивания. Getter после этого по-прежнему возвращает H1,true. Успешный вызов издателя с допустимыми параметрами даёт H2,true. Это сравнение показывает и право, и отсутствие частичного изменения при отказе.

В интерфейсе можно предварительно прочитать издателя и сравнить его с выбранным аккаунтом. Так пользователю проще понять, почему кнопка недоступна. Но окончательное решение принимает контракт при исполнении. Если браузер ошибся в своём сравнении, он не должен получить возможность обойти проверку.

Мы определили субъект текущего вызова и способ сопоставить его с сохранённым издателем. Следующая страница соберёт повторяющееся условие в модификатор, чтобы обе изменяющие функции использовали одну ясную форму. Переменные сообщения доступны в справочнике Solidity.

Сопоставим права двух контрактов

Пусть M назначен издателем реестра, а внутри M редактором указан P. Издательский адрес C видит M, а внутренний метод M проверяет P. Система получает два последовательных условия. Если M разрешает читателю R вызвать административную операцию без проверки, C не может распознать ошибку: для него отправитель всё ещё правильный M.

Такой разбор помогает распределить ответственность. CourseCatalog не обязан знать внутренние роли каждого возможного кошелька, но при выборе издателя редакция должна понимать его механизм. Компоненты соединяются по адресам и вызовам, а не по одинаковым названиям ролей.

В отчёте о действии полезно сохранить непосредственный адрес вызова и понятную редакционную метку отдельно. Метка «редактор Иван» может измениться, адрес остаётся техническим субъектом конкретного события. Если данные имеют разные источники, они не должны замещать друг друга.

Для дополнительного случая представьте сервер, который отправляет транзакцию собственным техническим аккаунтом после входа пользователя. Тогда msg.sender соответствует серверному аккаунту. Пользовательская сессия влияет на решение сервера, но не переносится в EVM автоматически. Такой посредник требует своих проверок и прозрачного описания доверия.

Оглавление курса. Листинги, расчёты и наблюдения здесь разобраны как ожидаемое поведение; компиляция, учебные программы и проверки при написании не запускались.

Оглавление серии