<?xml version="1.0" encoding="utf-8" ?><rss version="2.0" xmlns:tt="http://teletype.in/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:media="http://search.yahoo.com/mrss/"><channel><title>Nikita [Crypton]</title><generator>teletype.in</generator><description><![CDATA[jetix37.eth | Crypton Prime, Calendar, Research]]></description><image><url>https://img1.teletype.in/files/ce/c7/cec735e0-803e-462b-b970-cb6c4417b198.png</url><title>Nikita [Crypton]</title><link>https://teletype.in/@jetix37</link></image><link>https://teletype.in/@jetix37?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=jetix37</link><atom:link rel="self" type="application/rss+xml" href="https://teletype.in/rss/jetix37?offset=0"></atom:link><atom:link rel="next" type="application/rss+xml" href="https://teletype.in/rss/jetix37?offset=10"></atom:link><atom:link rel="search" type="application/opensearchdescription+xml" title="Teletype" href="https://teletype.in/opensearch.xml"></atom:link><pubDate>Mon, 06 Apr 2026 19:56:57 GMT</pubDate><lastBuildDate>Mon, 06 Apr 2026 19:56:57 GMT</lastBuildDate><item><guid isPermaLink="true">https://teletype.in/@jetix37/create-mev-bot-solidity</guid><link>https://teletype.in/@jetix37/create-mev-bot-solidity?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=jetix37</link><comments>https://teletype.in/@jetix37/create-mev-bot-solidity?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=jetix37#comments</comments><dc:creator>jetix37</dc:creator><title>Создаем MEV бота на Solidity</title><pubDate>Sun, 12 Mar 2023 16:56:10 GMT</pubDate><media:content medium="image" url="https://img1.teletype.in/files/04/80/04809b0a-2aef-4f0b-b103-9a3d7160ca1a.png"></media:content><category>Ethereum</category><description><![CDATA[<img src="https://image2.b-ch.com/ttl2/7398/7398010a.jpg"></img>Оригинал — https://www.blocknative.com/blog/mev-and-creating-a-basic-arbitrage-bot-on-ethereum-mainnet]]></description><content:encoded><![CDATA[
  <section style="background-color:hsl(hsl(0, 0%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="epwe">Оригинал — <a href="https://www.blocknative.com/blog/mev-and-creating-a-basic-arbitrage-bot-on-ethereum-mainnet" target="_blank">https://www.blocknative.com/blog/mev-and-creating-a-basic-arbitrage-bot-on-ethereum-mainnet</a></p>
    <p id="0KL4">Канал — <a href="https://t.me/jetix37eth" target="_blank">https://t.me/jetix37eth</a></p>
  </section>
  <figure id="pLGz" class="m_original">
    <img src="https://image2.b-ch.com/ttl2/7398/7398010a.jpg" width="1920" />
  </figure>
  <p id="bGqC"><strong>MEV</strong>, или <strong>Максимальная Извлекаемая Ценность</strong>— это максимальное значение, которое может быть извлечено из блока. Хотя традиционно это была возможность, в которой могли участвовать только майнеры — именно поэтому ее иногда называли <strong>Miner Extractable Value</strong>, — демократизация мемпула предоставила трейдерам больше возможностей извлекать ценность, влияя на то, что попадает в блок. </p>
  <p id="MAKy">Поисковики <strong>MEV </strong>могут легко повлиять на то, какие транзакции включаются и исключаются, или даже изменить порядок транзакций в блоке, увидев, что уже есть в мемпуле, а затем соответствующим образом рассчитать время и цену своих транзакций.</p>
  <p id="tkeW">Эти поисковики обычно запускают алгоритмы для обнаружения новых прибыльных возможностей и заставляют ботов автоматически отправлять любые прибыльные транзакции в сеть. </p>
  <p id="Ttyg">Здесь подробно рассказывается о моей попытке создать одного из этих ботов и уроках, которые я извлек на этом пути. Вы можете рассматривать это как руководство для начинающих, которое поможет вам раскрыть свои творческие способности и начать думать о других стратегиях, уникальных для вас.</p>
  <h2 id="OBnt">Арбитраж в MEV</h2>
  <p id="mMVN">Арбитраж — это стратегия, которая позволяет трейдерам получать прибыль от разницы цен между одним и тем же активом на разных рынках. В традиционном финансовом мире это создает возможность совершить две сделки, чтобы привести две биржи в равновесие — и получить за это небольшую прибыль.</p>
  <p id="f0Qv">Допустим, Вы начали с актива <strong>A</strong> и продали его за актив <strong>B</strong> на бирже, где актив <strong>B</strong> дешевле. Затем вы бы взяли актив и продали его за актив <strong>А</strong> на другой бирже, получив взамен больше актива <strong>А</strong>, чем когда вы начинали. </p>
  <p id="o4Sc">Этот базовый пример чрезвычайно конкурентен в традиционных финансах, и многие высокочастотные трейдеры соревнуются друг с другом, чтобы первыми воспользоваться возможностью арбитража.</p>
  <p id="Q2Ns">В мире <strong>MEV </strong>все немного сложнее. В криптовалютной сфере транзакции отправляются в мемпул — который, по большей части, является общедоступным — для включения в следующий блок. Это означает, что трейдеры могут видеть отложенные транзакции в мемпуле и знать, каковы будут последствия, как только они будут завершены.</p>
  <p id="rpqs">Эта дополнительная наглядность приводит к тому, что арбитраж <strong>MEV </strong>работает несколько иначе, чем традиционный арбитраж, поскольку трейдерам не нужно ждать, пока транзакции будут запущены в блокчейне, чтобы найти возможность. В результате поисковикам MEV требуется поток данных мемпула в режиме реального времени для анализа каждой транзакции по мере ее поступления.</p>
  <p id="dde3">Как только вы обнаружите транзакцию, которая может вызвать возможность арбитража, когда она попадет в цепочку, вы можете отправить две свои транзакции, которые завершат возможность арбитража, непосредственно в мемпуд или в <a href="https://docs.flashbots.net/" target="_blank">Flashbots</a> в виде пакета. Если все сделано правильно, это означает, что транзакция мемпула, которая создает возможность + все ваши две транзакции будут добыты в одном блоке. </p>
  <p id="mzFV">В результате, в тот момент, когда создается возможность арбитража, она также завершается следующими двумя транзакциями — вашими двумя транзакциями.</p>
  <hr />
  <p id="Fb5X">Давайте разберем это на совершенно нереалистичном, но, надеюсь, проливающем свет примере.</p>
  <figure id="uJD1" class="m_original">
    <img src="https://www.blocknative.com/hs-fs/hubfs/MEV-Arbitrage-1.png?width=936&name=MEV-Arbitrage-1.png" width="936" />
  </figure>
  <p id="jNGW">Предположим, вы отслеживаете мемпул и обнаруживаете транзакцию, которая повлияет на пул ликвидности в <strong>Uniswap V2 </strong>для токенов <strong>A</strong> и <strong>B</strong>. Поскольку вы знаете, что пулы ликвидности регулируются уравнением <strong>x * y = k</strong>, тогда вы знаете, когда видите, что кто-то отправляет обмен <strong>2 токена A </strong>на как можно <strong>большее </strong>количество <strong>токена B</strong>, вы можете рассчитать (основываясь на резервах этих токенов в пуле ликвидности, которые в данном случае равны 10 и 10), что он получат взамен <strong>1,7 </strong>токена <strong>B</strong>.</p>
  <p id="n1z4">Ключевым моментом является то, что вы знаете, что эта транзакция повлияет на цену между токенами <strong>A</strong> и <strong>B</strong> на <strong>Uniswap V2</strong>, но не на другой DEX, такой как <strong>Sushiswap</strong>… по крайней мере, до тех пор, пока кто-нибудь не воспользуется возможностью арбитража. </p>
  <p id="Wcmd">В тот момент, когда эта транзакция будет добыта, токен <strong>A</strong> будет дешевле на <strong>Uniswap</strong>, чем на <strong>Sushiswap</strong>, поэтому тот, кто может продать токен <strong>A</strong> на <strong>Sushiswap </strong>за токен<strong> B</strong>, а затем продать эти токены <strong>B</strong> на <strong>Uniswap </strong>за токен <strong>A</strong>, должен в конечном итоге получить больше токена <strong>A</strong>, чем они начали. И это именно то, что иллюстрирует этот пример.</p>
  <p id="TgJU">Существует некоторая конкретная математика, которую вы можете выполнить, чтобы рассчитать оптимальную сумму для торговли туда и обратно, но она выходит за рамки этой статьи.</p>
  <h2 id="Zp5E">Поиск возможностей для арбитража</h2>
  <p id="k5Ac">Проводя некоторое исследование различных возможностей в MEV, я наткнулся на готовый смарт-контракт в <a href="https://github.com/paco0x/amm-arbitrageur/blob/master/contracts/FlashBot.sol" target="_blank">этом потрясающем репозитории github</a>, который расскажет вам, как оптимально рассчитать правильную сумму, необходимую для арбитража между двумя <strong>Uniswap V2 дексами</strong> — например, <strong>Uniswap V2</strong> и <strong>Sushiswap</strong>.</p>
  <p id="7CuS">Ключевой момент этого смарт-контракта заключается в том, что вы можете предоставить ему два пула — один пул <strong>Uniswap </strong>и один пул <strong>Sushiswap </strong>—и сообщить, есть ли какая-либо прибыль от арбитража между двумя пулами. Однако расчет выполняется путем анализа резервов этих двух пулов в их текущем состоянии (т.е. последнего состояния блока).</p>
  <p id="asqp">Но что, если бы мы знали, как изменятся эти пулы до следующего блока? Тогда мы знали бы о возможностях арбитража до того, как они появятся в цепочке, и мы могли бы отправить свою арбитражную транзакцию, чтобы воспользоваться этими возможностями. </p>
  <p id="A4rK">Теперь моей целью в этом проекте было написать базовый скрипт, который модифицирует этот смарт-контракт для успешного обнаружения возможностей арбитража с использованием расчета изменения чистого баланса платформы <a href="https://www.blocknative.com/simulation-platform" target="_blank">Blocknative Simulation Platform</a>.</p>
  <h2 id="MYou">Пишем базовый скрипт</h2>
  <p id="20R1">Этот пост не предназначен для того, чтобы узнать о Solidity. Существует множество отличных постов и руководств, но для того, чтобы по-настоящему конкурировать за возможности <strong>MEV </strong>(по крайней мере, в мире EVM), вам нужно знать Solidity.</p>
  <p id="lrzz">Тем не менее, я использовал свои ограниченные знания о Solidity, чтобы обновить смарт-контракт в соответствии с моими потребностями. Чтобы рассчитать прибыль от арбитражной возможности в реальном времени, мне нужно было бы каким-то образом снабдить мою функцию <strong>getProfit </strong>корректировкой пула ликвидности. Эта корректировка происходит, когда я вижу транзакцию обмена в мемпуле, которая повлияет на пул ликвидности <strong>Uniswap </strong>или <strong>Sushiswap</strong>.</p>
  <p id="vbvG">Я учитываю влияние транзакции мемпула при расчете потенциальной прибыли для возможности арбитража. Чтобы включить эту корректировку, я создал новую структуру под названием <strong>Adjustments</strong>, в которой будет размещен пул, который необходимо скорректировать, направление, в котором его необходимо скорректировать, и количество корректировок для каждого токена в паре токенов. </p>
  <p id="ikB7">Заметьте, что <strong>getProfit </strong>полагается на <strong>getOrderedReserves </strong>для расчета резервов каждого пула и правильного направления сделок. Поэтому я добавил некоторую логику в <strong>getOrderedReserves</strong>, которая принимала бы данные мемпула при обмене токенами в <strong>Uniswap </strong>или <strong>Sushiswap</strong>, а затем корректировала резервы токенов этой пары, чтобы при вызове <strong>getProfit </strong>он включал данные мемпула, а не только последнее состояние цепочки.</p>
  <figure id="KV9n" class="m_original">
    <img src="https://img1.teletype.in/files/0c/b2/0cb237b2-1ce8-48ba-a963-8309612ed254.png" width="889" />
  </figure>
  <p id="5UuJ">Теперь, когда я настроил смарт-контракт так, чтобы он мог включать данные мемпула в реальном времени, я могу завершить свой скрипт, используя mempool API от <strong>Blocknative </strong>и<strong> flashbots private relay</strong>.</p>
  <p id="Wmn9">Используя <strong>Simulation Platform</strong>, каждое моделирование выполняется изолированно от текущего состояния блока, что означает, что существует некоторая степень вероятности, связанная с полезными нагрузками &quot;ожидающего моделирования&quot;. Однако, если транзакция находится в верхней части блока, то это фактически означает, что она выполняется в отношении текущего состояния блока, поскольку между текущим состоянием блока и этим не будет никаких транзакций. </p>
  <p id="Urdq">Лучший способ гарантировать, что он попадет в начало блока (или, по крайней мере, близко к нему), - это использовать частную ретрансляцию flash-ботов.</p>
  <p id="lmcd">Для этого проекта я просмотрел ожидающие события моделирования на маршрутизаторах <strong>Uniswap V2</strong> и <strong>Sushiswap</strong>, используя <strong>Javascript SDK</strong> от <strong>Blocknative</strong>. Ожидающие загрузки моделирования включают изменения чистого баланса по всем задействованным адресам (например, пул ликвидности), что позволяет мне сделать две вещи:</p>
  <ul id="kohz">
    <li id="P4zy">Проверить, существует ли эта пара на другой бирже</li>
    <li id="8xRY">Использовать изменения баланса в качестве входных данных в качестве &quot;корректировок&quot; в моей функции <strong>getProfit </strong>в моем смарт-контракте</li>
  </ul>
  <p id="GK5L">Когда я получаю эти события, я перебираю список <strong>netBalanceChange </strong>в нашей полезной нагрузке <strong>Simulation Platform</strong> и пропускаю, когда адрес равен самому фактическому маршрутизатору (потому что я ищу только адреса пула ликвидности) или если<strong> netBalanceChange[index].address</strong> не содержит 2 элементов (если у него есть только 1 элемент, то это не может быть пул, потому что пул будет иметь 2 или более резервов токенов с изменениями чистого баланса).</p>
  <figure id="dlDr" class="m_original">
    <img src="https://www.blocknative.com/hubfs/image-png-Feb-24-2022-11-59-10-09-PM.png" width="789" />
  </figure>
  <p id="BL7j">Если один из адресов в поле <strong>netBalanceChange </strong>не является одним из адресов маршрутизатора и имеет два разных адреса токенов, на которых изменяется баланс сети, то это должен быть адрес пула. Теперь у меня есть адрес пула, задействованные адреса токенов и новые изменения баланса каждого токена. </p>
  <p id="k9VZ">Теперь я могу проверить, каков адрес пула ликвидности другого DEX, чтобы я мог указать его в своем смарт-контракте. Если у другого DEX нет пула ликвидности с той же парой токенов, то я не могу осуществлять арбитраж между ними, поэтому я двигаюсь дальше.</p>
  <figure id="dLvq" class="m_original">
    <img src="https://img1.teletype.in/files/41/1f/411f0aa7-fc03-4aee-b896-df5cf1862fbf.png" width="936" />
  </figure>
  <p id="hDpJ">Аналогично, если у другого DEX действительно есть пул ликвидности с той же парой токенов, то я могу осуществлять арбитраж между ними. Используя мой скрипт, это означает, что я обнаружил отложенный обмен либо на <strong>Uniswap</strong>, либо на <strong>Sushiswap </strong>в мемпуле, и я обнаружил тот же пул ликвидности на другом DEX. Теперь у меня есть вся информация, необходимая для предоставления моего смарт-контракта, чтобы увидеть, есть ли возможность получения прибыли.</p>
  <p id="Zwvk">Результат, который я получаю от своего смарт-контракта, не включает газ, поэтому я произвожу наивный расчет, чтобы увидеть, какова будет общая стоимость газа (включая мои дополнительные чаевые майнерам на ретрансляции <strong>flashbots</strong>). Я знал, что две мои транзакции обойдутся примерно в 240 тысяч газа, поэтому я захардкодил это для расчета чистой прибыли. </p>
  <p id="WXDa">Что касается моей цены на газ, я предполагал, что мне придется отказаться от 95% своей валовой прибыли, чтобы участвовать в частной эстафете. 95% были захардкожены.</p>
  <p id="t8ql">Если чистый расчет больше 0, я успешно нашел возможность арбитража, где я получу чистый 5% и отдам майнеру 95%!</p>
  <figure id="ZWLP" class="m_original">
    <img src="https://img2.teletype.in/files/5c/52/5c52c534-d701-4b04-ab09-61103e9b125b.png" width="1107" />
  </figure>
  <h2 id="72s0">Создаем flashbots-пакет для арбитража</h2>
  <p id="3RSQ">Затем мне нужно было создать свой пакет <strong>flashbots </strong>для отправки на ретранслятор. Поскольку обнаруженная транзакция мемпула может быть транзакцией типа 0 или типа 2, у меня должны быть сценарии для обоих.</p>
  <figure id="GEne" class="m_original">
    <img src="https://img1.teletype.in/files/c2/87/c287017c-3304-4b5a-a674-99c80993423c.png" width="999" />
  </figure>
  <p id="426K">Я использовал свой модифицированный смарт-контракт для построения своей транзакции, которая включала бы либо две внутренние транзакции, либо два обмена между двумя DEX.</p>
  <figure id="DHqK" class="m_original">
    <img src="https://img3.teletype.in/files/ec/2d/ec2dcc85-982e-4a1b-ad2c-1f67cbdb70a1.png" width="1154" />
  </figure>
  <p id="Lr1O">Создав транзакцию, я затем создал пакет <strong>flashbots </strong>и смоделировал его, чтобы гарантировать, что транзакция не завершится неудачей по какой-либо причине. Я также создал окончательный расчет чистой прибыли, чтобы убедиться, что я все еще получаю прибыль. Если моя транзакция прошла симуляцию, и я все еще получал прибыль, то я отправлял ее на ретрансляцию <strong>flashbots </strong>для включения.</p>
  <figure id="9S8d" class="m_original">
    <img src="https://img3.teletype.in/files/62/e7/62e7259a-3d58-42c3-bf19-ee4bb3c9fccd.png" width="1127" />
  </figure>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@jetix37/create-solidity-oracle</guid><link>https://teletype.in/@jetix37/create-solidity-oracle?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=jetix37</link><comments>https://teletype.in/@jetix37/create-solidity-oracle?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=jetix37#comments</comments><dc:creator>jetix37</dc:creator><title>Создаем оракула на Solidity</title><pubDate>Wed, 08 Mar 2023 19:23:22 GMT</pubDate><media:content medium="image" url="https://img4.teletype.in/files/3e/36/3e36d2b0-cf43-4b0a-9fa6-8d142ad17bed.png"></media:content><category>Ethereum</category><description><![CDATA[<img src="https://img3.teletype.in/files/e1/b1/e1b19476-beb2-426b-b98e-b156dbe5a811.jpeg"></img>Оригинал — https://noahliechti.hashnode.dev/an-effective-way-to-build-your-own-oracle-with-solidity]]></description><content:encoded><![CDATA[
  <section style="background-color:hsl(hsl(323, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="E0fX">Оригинал — <a href="https://noahliechti.hashnode.dev/an-effective-way-to-build-your-own-oracle-with-solidity" target="_blank">https://noahliechti.hashnode.dev/an-effective-way-to-build-your-own-oracle-with-solidity</a></p>
    <p id="IhZn">Канал — <a href="https://t.me/jetix37eth" target="_blank">https://t.me/jetix37eth</a></p>
  </section>
  <figure id="4gVG" class="m_original">
    <img src="https://img3.teletype.in/files/e1/b1/e1b19476-beb2-426b-b98e-b156dbe5a811.jpeg" width="1920" />
  </figure>
  <p id="tZrp">Если вы ранее создавали смарт-контракт, возможно, вы уже знаете, как запрашивать информацию, такую как цены или случайные числа, в вашем контракте. Но как насчет внешних данных, которые не предоставляются обычными оракулами? Как мы можем получить доступ к папиным шуткам или вытянуть случайную карту?</p>
  <hr />
  <h2 id="lWWN">Как работают Оракулы?</h2>
  <p id="zuzt">Для выполнения своей основной цели смарт-контрактам может требоваться связь с внешним миром. Оракулы дают эту функциональность. Оракул состоит из компонента, связанного с блокчейном (смарт-контракт), и компонента вне блокчейна, который может запрашивать API. Затем серверная часть периодически отправляет транзакции для обновления состояния смарт-контракта. </p>
  <p id="3za4">Важно понимать, что ваш контракт просит оракула выполнить вызов API. Он не выполняет вызов самостоятельно.</p>
  <p id="hcjF">Есть две причины использовать оракулов:</p>
  <ul id="oLae">
    <li id="S5gx">Вам нужна информация, которая не может быть предоставлена из блокчейна.</li>
    <li id="tET5">Вам нужна информация, которая будет предоставлена в определенное время в будущем.</li>
  </ul>
  <hr />
  <h2 id="xUbc">Идея</h2>
  <p id="1lmK">Создадим минимально жизнеспособного оракула, который позволит контракту извлекать случайные карты из стандартной колоды из 52 карт.</p>
  <section style="background-color:hsl(hsl(170, 33%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <h3 id="chyq">Минимальные требования</h3>
    <ul id="shhk">
      <li id="0ZvU">Использовать API <a href="https://deckofcardsapi.com/" target="_blank">deckofcardsapi.com</a></li>
      <li id="1Pg3">Запрос из контракта <strong>CardsClient</strong> стоит 0.001 $ETH</li>
      <li id="oc1r">Контракт <strong>CardsClient</strong> имеет <strong>callback</strong>-функцию <strong>fulfill</strong></li>
      <li id="GDAF"><strong>CardsClient</strong> разрешает только один ожидающий запрос</li>
      <li id="lk8W">Выпавшие кадры идут как <strong>bytes2</strong> (Туз Пик -&gt; AS -&gt; <strong>0x4153</strong>)</li>
      <li id="Sf7B">Когда <strong>CardsOracle</strong> получает запрос через <strong>receiveRequest</strong>, он вызывает событие <strong>OracleRequest</strong></li>
      <li id="HC0m">Бекенд должен слушать <strong>OracleRequest</strong>, запрашивать API и высылать результат в <strong>CardsOracle</strong> через функцию <strong>fulfillRequest</strong></li>
      <li id="BoXn">Функция <strong>fulfillRequest</strong> может вызваться только на беке</li>
    </ul>
  </section>
  <hr />
  <h2 id="AScY">Реализация</h2>
  <section style="background-color:hsl(hsl(170, 33%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="4SY7">Здесь полный код реализации: <a href="https://github.com/noahliechti/minimal-viable-oracle" target="_blank">https://github.com/noahliechti/minimal-viable-oracle</a></p>
  </section>
  <h3 id="5GKK">Архитектура</h3>
  <figure id="GmCc" class="m_original">
    <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1652908971391/Gbvyo3D5S.png?auto=compress,format&format=webp" width="2220" />
  </figure>
  <h3 id="FLCP">Смарт-контракты</h3>
  <p id="hw9D">Моя реализация состоит из двух контрактов и одного интерфейса. Контракт, который взаимодействует с оракулом, называется <strong>CardsClient</strong>, а ончейн компонент, предоставляющий эту услугу, называется <strong>CardsOracle</strong>. Контракты были скомпилированы с версией <strong>0.8.7</strong> и также должны работать до версии <strong>0.8.15</strong>.</p>
  <hr />
  <h3 id="V9XB">CardsClient</h3>
  <p id="vpH7">Чтобы получить общее представление, я покажу вам переменные состояния и конструктор контракта CardsClient:</p>
  <figure id="ecM8" class="m_original">
    <img src="https://img3.teletype.in/files/2d/39/2d39e407-8b6a-4dc7-9ffe-4383c8f75a79.png" width="663" />
  </figure>
  <p id="Nk6l"><strong>Owner</strong> — это адрес, который создал контракт. Таких контрактов может быть несколько, поскольку всем разрешено использовать нашего оракула. </p>
  <p id="oOZ7">Адрес оракула должен быть <strong>payable</strong>, потому что мы платим оракулу 0.001 ETH за каждый сделанный нами запрос. Причина взимания платы заключается в том, что оракул должен совершить вторичную транзакцию к контракту CardsClient, которая стоит некоторое количество ETH. </p>
  <p id="gPS6">Результаты нашего запроса будут сохранены в массиве переменных <strong>bytes2</strong>. Каждая карта может быть эффективно представлена в двух байтах памяти (Туз пик -&gt; AS -&gt; 0x4153). Наконец, мы будем использовать <strong>uint32</strong> для хранения идентификатора последнего ожидающего запроса. Эта реализация предотвращает несколько одновременных запросов. Вы также можете разрешить несколько одновременных запросов.</p>
  <hr />
  <p id="quK0">Далее нам надо определить функции, с которыми могут взаимодействовать кошельки или контракты:</p>
  <figure id="7lSE" class="m_original">
    <img src="https://img1.teletype.in/files/83/1b/831bf46e-3350-4dbc-8b3b-88827118cba3.png" width="1283" />
  </figure>
  <p id="2CZD">Есть две <strong>public</strong> функции, называемые <strong>drawNCardsWithShuffle</strong> и <strong>drawNCardsWithoutShuffle</strong>. Первая функция вытягивает карту из перетасованной колоды и перетасовывает ее перед любым последующим розыгрышем карт. Вероятность вытянуть определенную карту останется 1/52. Последняя функция позволяет извлекать карты из одной и той же колоды без последующей повторной вставки карт. Обе функции начинаются с новой и перетасованной колоды при каждом запросе.</p>
  <p id="6V3v">Функции<strong> drawNCardsWithShuffle</strong> и <strong>drawNCardsWithoutShuffle </strong>создаю уровень абстракции. Пользователь никогда не вызовет функцию <strong>drawNCards</strong> напрямую. Вот почему они <strong>internal</strong>.</p>
  <p id="kMKh">Все функции используют <strong>uint8</strong> в качестве типа данных для параметра <strong>_nrOfCards</strong>, поскольку это наименьший тип данных, способный хранить число 52.</p>
  <hr />
  <p id="8dcn">Чтобы использовать функциональность между <strong>CardsClient</strong> и <strong>CardsOracle</strong>, мы создадим интерфейс, в котором есть структура <strong>Request</strong>:</p>
  <figure id="GqIe" class="m_original">
    <img src="https://img4.teletype.in/files/32/aa/32aac283-9d21-403b-9508-05c90837b7af.png" width="459" />
  </figure>
  <p id="lBGD">Эта структура действует как новый тип данных. Он группирует несколько атрибутов в один &quot;объект&quot;. С полями <strong>nrOfCards</strong> и <strong>shuffle</strong> мы уже знакомы. </p>
  <p id="LJ2D">В <strong>cbClient</strong> мы сохраним адрес контакта, который вызывает оракула. Вместе с <strong>cbSelector</strong> оракул может использовать функцию вызова низкого уровня для передачи результатов API обратно в наш контракт <strong>CardsClient</strong>. </p>
  <p id="Xdep">Чтобы предотвратить злонамеренную перезапись уже выполненных запросов, мы можем отслеживать состояние с помощью поля <strong>fulfilled</strong>.</p>
  <hr />
  <p id="PzJp"><strong>drawNCards</strong> отвечает за передачу эфира и <strong>Request </strong>оракулу.</p>
  <figure id="26xL" class="m_original">
    <img src="https://img2.teletype.in/files/dc/6c/dc6c64c6-e869-44d2-8c29-7edecfad5c00.png" width="1266" />
  </figure>
  <p id="bIQh">После того, как мы убедимся, что ожидающего запроса нет, мы можем создать запрос и инициализировать его. <strong>address(this)</strong> — это <strong>cbClient</strong>, а <strong>this.fulfill.selector</strong> — <strong>cbSelector</strong>. Если вы присмотритесь повнимательнее, я добавил сигнатуру функции <strong>fulfill</strong>. Это функция, которую вызовет оракул и передаст результаты запроса оффчейн API. <strong>this.fulfill.selector</strong> возвращает первые левые четыре байта хэша <strong>Keccak-256 (SHA-3) </strong>сигнатуре функции <strong>fulfill</strong>.</p>
  <p id="LERF">Теперь, когда запрос сформирован, пришло время передать его <strong>CardsOracle</strong>. Чтобы успешно выполнить транзакцию, функция <strong>call</strong> пересылает все, что контракт получил либо через <strong>drawNCardsWithShuffle</strong>, либо через <strong>drawNCardsWithoutShuffle</strong>. </p>
  <p id="UvwR">Получателем средств и структуры от оракула является функция <strong>receiveRequest</strong> . В первом аргументе функции <strong>abi.encodeWithSignature</strong> обязательно использовать две открывающие и закрывающие круглые скобки, поскольку мы используем структуру. В противном случае аргумент не будет закодирован правильно.</p>
  <p id="CFra">Транзакция <strong>receiveRequest</strong> контракта <strong>CardsOracle</strong> вызовет событие, которое сообщит бекенду, что она должна запросить API. Следовательно, переменная <strong>data</strong> не будет включать результат вызова API. Только <strong>fallback</strong>-функция (в нашем случае <strong>fulfill</strong>) получает доступ к результатам. Она будет вызвана из оракула транзакцией, которая инициируется бекендом.</p>
  <p id="bZqC">Если <strong>call</strong> не успешен, транзакция отменяется. Если успешен, идентификатор запроса извлекается из <strong>data</strong>, сохраняется в <strong>pendingRequest</strong> и затем возвращается.</p>
  <hr />
  <p id="u2WG"><strong>fulfill </strong>— это <strong>callback</strong>-функция, которая получает результат из оффчейн-сервиса:</p>
  <figure id="hBvG" class="m_original">
    <img src="https://img4.teletype.in/files/73/99/7399bf64-ef9f-4b12-9608-4dad1bdfb34e.png" width="1247" />
  </figure>
  <p id="uCEp">Когда вызывается функция <strong>fulfill</strong>, она сначала проверяет, соответствует ли вызывающий адрес адресу, который мы сохранили в переменной состояния <strong>oracle</strong>. Это очень важно. В противном случае любой кошелек или контракт могли бы вызвать эту функцию и злонамеренно предоставить неверную информацию. </p>
  <p id="6Wne">Наша функция также проверяет, что переданный <strong>_requestID </strong>совпадает с <strong>pendingRequestId</strong>. Я использую оператор <strong>assert</strong>, поскольку это условие, которое всегда должно быть истинным.</p>
  <p id="DLL7">Мы также запускаем событие под названием <strong>ClientFulfillment</strong>. На самом деле это не обязательно, но полезно для отслеживания и тестирования нашего контракта.</p>
  <p id="WWfy">В нашем случае функция просто сохраняет результат в переменной <strong>cards</strong>. Но это также может вызвать внутреннюю логику в зависимости от <strong>requestId</strong>. Все это зависит от варианта использования.</p>
  <hr />
  <h3 id="S10w"><strong>CardsOracle</strong></h3>
  <p id="RNBt">Как и в случае с <strong>CardsClient</strong>, я показываю вам переменные состояния и конструктор.</p>
  <figure id="yhk3" class="m_original">
    <img src="https://img1.teletype.in/files/46/85/46859422-04b2-4f65-b265-1325846b25a6.png" width="909" />
  </figure>
  <p id="U9b8"><strong>Owner&#x27;</strong>ом является разработчик контракта. <strong>fee</strong> — это сумма, которую запрашивающий должен заплатить оракулу за использование его сервиса. Это должно, по крайней мере, покрывать плату за газ для вторичной транзакции. </p>
  <p id="OY8b">Каждый запрос имеет свой собственный идентификатор. <strong>requestId</strong> увеличивается на единицу при каждом запросе. В экстренном случае, когда API будет взломан, нам нужно запретить пользователям отправлять запросы. С помощью <strong>stopped</strong> мы можем временно приостановить действие контракта. </p>
  <p id="F61M">Меппинг <strong>idToRequest </strong>кэширует запросы. На это есть две причины. Во-первых, нам нужно сохранить адрес и сигнатуру <strong>callback</strong>-функции, во-вторых, должен быть механизм, позволяющий не выполнять запрос несколько раз. Для этого мы используем поле <strong>fulfilled</strong>.</p>
  <section style="background-color:hsl(hsl(323, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="HtWR">Сначала мы развертываем контракт <strong>CardsOracle</strong>, чтобы мы могли напрямую передать его адрес конструктору <strong>CardsClient</strong>.</p>
  </section>
  <hr />
  <p id="xU6I">Функция <strong>receiveRequest</strong> получает запросы вместе с <strong>fee</strong> и запускает событие.</p>
  <figure id="ySHV" class="m_original">
    <img src="https://img3.teletype.in/files/23/1a/231af0fc-1f7d-4dca-96c1-5e5584289dae.png" width="1208" />
  </figure>
  <p id="y4qx"><strong>receiveRequest</strong> получает <strong>Request</strong> в качестве параметра. Функция проверяет, не остановлен ли контракт и правильно ли заполнены все поля. Если нет, то все возвращается. Также количество эфира, отправляемое пользователем, должно быть больше или равно указанной нами комиссии. Возврат средств не производится, если пользователь платит слишком много. Все запросы хранятся в нашем меппинге <strong>idToRequest</strong>. К запросам можно получить доступ по <strong>requestId</strong>.</p>
  <p id="DBar">Далее мы генерируем событие OracleRequest. Наш бекенд слышит это событие и запрашивает данные из API. Важно, чтобы событие было отправлено из контракта <strong>CardsOracle</strong>. Если бы мы разрешили какому-либо контракту отправлять событие, оракул был бы уязвим для спама, поскольку мы не можем гарантировать, что пользователь заплатил за услугу.</p>
  <p id="DcYC">Функция заканчивается оператором <strong>return</strong>, который возвращает текущий <strong>requestId </strong>и увеличивает ID. Это полезно, поскольку <strong>CardsClient</strong> может захотеть связать входящие вызовы по их идентификатору.</p>
  <hr />
  <p id="2LvN">Следующая функция может быть вызвана только беком. Его задача заключается в передаче извлеченных данных <strong>callback</strong>-функции контракта <strong>CardsClient</strong>.</p>
  <figure id="CFqh" class="m_original">
    <img src="https://img3.teletype.in/files/68/e3/68e3e939-45d8-4669-9763-073d900a55d0.png" width="1235" />
  </figure>
  <p id="yHin">С помощью <strong>_requestId</strong>, который мы получаем в качестве параметра, мы можем получить доступ к кэшированным запросам. Параметр <strong>_cards</strong> сохраняет разыгранные карты. Для того, чтобы успешно передать <strong>_cards</strong> клиенту, необходимо выполнить несколько условий:</p>
  <ul id="bJ9p">
    <li id="GRiw">Контракт не остановлен</li>
    <li id="EdTI">Запрос должен быть в <strong>idToRequest</strong></li>
    <li id="axWL">Запрос должен быть выполнен</li>
    <li id="TzKc">Количество запрошенных карт должно быть равно количеству кард, которые функция получила</li>
  </ul>
  <p id="apVA">После этого мы можем снова использовать функцию <strong>call</strong>. В этом случае мы передаем закодированные данные и <strong>_requestId</strong>.</p>
  <hr />
  <h2 id="h0br">Тестирование</h2>
  <p id="ynkW">Я написал несколько скриптов на Hardhat чтобы запустить проект:</p>
  <ul id="HZhv">
    <li id="7dls"><a href="https://github.com/noahliechti/minimal-viable-oracle/blob/main/scripts/1_deploy-contracts.js" target="_blank">1_deploy-contracts.js</a></li>
    <li id="0XlO"><a href="https://github.com/noahliechti/minimal-viable-oracle/blob/main/scripts/2_listen-to-client-fulfillments.js" target="_blank">2_listen-to-client-fulfillments.js</a></li>
    <li id="hbLk"><a href="https://github.com/noahliechti/minimal-viable-oracle/blob/main/scripts/2_listen-to-oracle-requests.js" target="_blank">2_listen-to-oracle-requests.js</a></li>
    <li id="nfH0"><a href="https://github.com/noahliechti/minimal-viable-oracle/blob/main/scripts/3_draw-cards.js" target="_blank">3_draw-cards.js</a></li>
    <li id="UuQD"><a href="https://github.com/noahliechti/minimal-viable-oracle/blob/main/scripts/helper-functions.js" target="_blank">helper-functions.js</a></li>
  </ul>
  <p id="WAgb">Хотя все части важны для работы проекта, только <strong>2_listen-to-oracle-requests.js </strong>необходим для прослушивания события <strong>OracleRequest</strong> и извлечения данных.</p>
  <figure id="x3Xl" class="m_original">
    <img src="https://img2.teletype.in/files/12/3e/123e2e0c-9d0a-469d-8ee8-71b8090cb5ac.png" width="1255" />
  </figure>
  <p id="CNbz"><strong>cardsOracle</strong> ссылается на контракт <strong>CardsOracle</strong>, который развернут в блокчейне. Метод <strong>on</strong> контракта позволяет нам определить событие, которое мы хотим прослушать, и действие, которое должно быть выполнено при запуске события. </p>
  <p id="oeAp">Мы используем событие <strong>OracleRequest</strong>, поскольку это событие мы определили в функции <strong>receiveRequest</strong> контракта <strong>CardsOracle</strong>. Когда происходит событие, скрипт получает доступ ко всем параметрам, которые были определены в контракте. </p>
  <p id="L1io">Чтобы извлечь соответствующие данные, нам нужно передать <strong>nrOfCards</strong> и <strong>shuffle</strong> в функцию <strong>getCards</strong>. Эта функция выполняет вызов API deckofcardsapi.com  и в результате возвращает массив карт в шестнадцатеричном формате. Этот массив (<strong>allCardsCoresHex</strong>) затем передается вместе с <strong>requestId</strong> обратно в контракт. Затем данные будут перенаправлены в fullback-функцию клиентского контракта.</p>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@jetix37/zk-snark-for-kids</guid><link>https://teletype.in/@jetix37/zk-snark-for-kids?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=jetix37</link><comments>https://teletype.in/@jetix37/zk-snark-for-kids?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=jetix37#comments</comments><dc:creator>jetix37</dc:creator><title>zk-SNARK для самых маленьких</title><pubDate>Mon, 06 Mar 2023 17:28:50 GMT</pubDate><media:content medium="image" url="https://img3.teletype.in/files/af/4c/af4c3d75-524c-49bc-b2b4-21f3d0262043.png"></media:content><category>Zero Knowledge</category><description><![CDATA[<img src="https://wallbox.ru/wallpapers/main2/201727/149959995959621457d78f28.08937496.jpg"></img>Оригинал — https://sjkelleyjr.medium.com/zk-snark-concepts-explained-like-youre-15-54755f87c6d1]]></description><content:encoded><![CDATA[
  <section style="background-color:hsl(hsl(323, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="E04J">Оригинал — <a href="https://sjkelleyjr.medium.com/zk-snark-concepts-explained-like-youre-15-54755f87c6d1" target="_blank">https://sjkelleyjr.medium.com/zk-snark-concepts-explained-like-youre-15-54755f87c6d1</a></p>
    <p id="4uE4">Канал — <a href="https://t.me/jetix37eth" target="_blank">https://t.me/jetix37eth</a></p>
  </section>
  <figure id="38xp" class="m_original">
    <img src="https://wallbox.ru/wallpapers/main2/201727/149959995959621457d78f28.08937496.jpg" width="1920" />
  </figure>
  <p id="9YPh">Последние несколько месяцев я изучал материалы по zk-SNARK и понимал, насколько тяжело слышать многие термины, используемые исследователями zk-SNARK, прежде чем я узнал, что они означают.</p>
  <p id="CEhe">Я решил написать гайд по жаргону zk-SNARK для самых маленьких, поскольку во время учебы мне еще не приходилось сталкиваться с чем-то подобным. </p>
  <p id="tYOu">Обратите внимание, что целевая аудитория этого поста — это те, кто не знаком с zk-SNARK&#x27;ами, поэтому многие определения будут математически неточными (см., например, определение <strong>конечного поля</strong> ниже).</p>
  <hr />
  <section style="background-color:hsl(hsl(323, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <h2 id="4sOH">Интерактивные доказательства</h2>
    <p id="6xuK"><strong>Интерактивные доказательства</strong> — это протоколы, в которых <strong>верифицируемый </strong>и <strong>верификатор </strong>взаимодействуют друг с другом таким образом, что <strong>верифицируемый </strong>может доказать <strong>верификатору</strong>, что они правильно выполнили некоторые вычисления. </p>
    <p id="ev7o">Они обычно преподаются как предшественники SNARK&#x27;ов, поскольку одна и та же ментальная модель может быть перенесена между ними, а интерактивное доказательство может быть превращено в SNARK с помощью преобразования Фиата-Шамира.</p>
  </section>
  <hr />
  <section style="background-color:hsl(hsl(170, 33%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <h2 id="0fDF">Протокол Фиата-Шамира</h2>
    <p id="d8pI">Это способ сделать интерактивную систему доказательства неинтерактивной (буква N в SNARK). В общем случае вы заменяете сетевые вызовы туда и обратно между <strong>верифицируемым</strong> и <strong>верификатором </strong>хэш-функцией, с помощью которой <strong>верификатор </strong>может проверить, что проверка выполнена правильно, чтобы обеспечить ее случайность.</p>
  </section>
  <hr />
  <section style="background-color:hsl(hsl(323, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <h2 id="5VoQ">Случайный оракул</h2>
    <p id="Q1bz">Это связано с преобразованием <strong>Фиата-Шамира</strong>. Когда кто-то говорит о модели случайного оракула, он говорит: “мы предполагаем, что существует некоторая <strong>идеализированная случайная хэш-функция</strong>”. Вы могли бы думать о модели случайного оракула как о ментальной модели, которая была использована для разработки техники преобразования <strong>Фиата-Шамира</strong>, чтобы убрать интерактивность из интерактивных доказательств.</p>
  </section>
  <hr />
  <section style="background-color:hsl(hsl(170, 33%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <h2 id="fYN5">Интерактивное доказательство оракула</h2>
    <p id="OtZO">Теперь, когда мы определили большинство из этих терминов, определить этот будет проще, поскольку мы можем использовать эти термины в этом определении.</p>
    <p id="Jw0F"><strong>Интерактивное доказательство оракула </strong>(называемое IOP) — это <strong>интерактивное доказательство</strong>, в котором используется <strong>оракул</strong> для повышения эффективности проверки.</p>
  </section>
  <hr />
  <section style="background-color:hsl(hsl(323, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <h2 id="zz07">Лемма Шварца-Зиппеля</h2>
    <p id="fqWI">Лемма Шварца-Зиппеля гласит, что если вы случайным образом вычисляете <strong>ненулевой многочлен</strong>, вероятность того, что он равен <strong>0</strong>, равна <strong>самой большой степени многочлена</strong> (назовем его <strong>d</strong>), деленной на <strong>количество возможных входных данных</strong> для многочлена (назовем его <strong>p</strong>).</p>
    <p id="qEss">Это звучит сложно, но интуитивно это означает, что вероятность того, что Вы выберете корень многочлена <strong>(d)</strong> из всех возможных значений для выбора <strong>(p)</strong>, равна <strong>d / p</strong>.</p>
    <p id="QCIr">Что интересно в этой лемме, так это то, что она также применима к многомерным многочленам.</p>
  </section>
  <hr />
  <section style="background-color:hsl(hsl(170, 33%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <h2 id="wNTl">Протокол проверки суммы</h2>
    <p id="8ScI">Этот интерактивный протокол использует <strong>лемму Шварца-Зиппеля</strong> для вероятностной проверки того, равны ли два многочлена друг другу, без непосредственного вычисления обоих многочленов.</p>
    <p id="Kd8w">Видео подробнее:</p>
    <figure id="EtFF" class="m_column">
      <iframe src="https://www.youtube.com/embed/XV62OB022tU?autoplay=0&loop=0&mute=0"></iframe>
    </figure>
  </section>
  <hr />
  <section style="background-color:hsl(hsl(323, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <h2 id="xI2N">Булев гиперкуб</h2>
    <p id="CQ9A">Вы часто будете видеть, что это записывается как <strong>{0,1} ^ n</strong>. Математическая нотация выдает это как <strong>набор битовых строк длины n</strong>. Например: <strong>{0,1}^2</strong> было бы <strong>{00, 01, 10, 11}</strong>.</p>
  </section>
  <hr />
  <section style="background-color:hsl(hsl(170, 33%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <h2 id="Hy9A">Протокол ГКР</h2>
    <p id="hno9"><strong>ГКР</strong> обозначает авторов протокола: <strong>Голдвассера</strong>, <strong>Калаи </strong>и <strong>Ротблюма</strong>. Протокол ГКР был одним из первых протоколов (опубликованных в 2015 году) для вероятностной проверки оценки арифметической схемы общего назначения с использованием протокола проверки суммы.</p>
  </section>
  <hr />
  <section style="background-color:hsl(hsl(323, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <h2 id="yyRf">Арифметическая схема</h2>
    <p id="f1bX">Если вы знакомы с традиционными схемами, которые оперируют булевыми значениями и операторами, это та же концепция, но применяемая к (как вы уже догадались) <strong>арифметике</strong>. </p>
    <p id="ADKI">Одна интересная вещь, которую следует отметить о вентилях этих схем, заключается в том, что в контексте zk-SNARK&#x27;ов на момент написания этой статьи они работают только с операторами <strong>сложения и умножения</strong>.</p>
  </section>
  <hr />
  <section style="background-color:hsl(hsl(170, 33%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <h1 id="a692">R1CS</h1>
    <p id="mPJH"><strong>R1CS</strong> расшифровывается как <strong>“Система ограничений ранга 1”</strong>. Ранг 1 взят из линейной алгебры и подразумевает, что система ограничений использует <strong>матрицы</strong>. Это было распространенное промежуточное представление для арифметических схем на всем пути от вычислений до zk-SNARK, пока в 2019 году не появился <strong>PLONK</strong>.</p>
    <p id="okQP">У Виталика есть <a href="https://medium.com/@VitalikButerin/quadratic-arithmetic-programs-from-zero-to-hero-f6d558cea649" target="_blank">пост в блоге</a>, объясняющий, как перейти от вычислений к zk-SNARK, который содержит изображение, которое я счел полезным, когда думал об этом пути.</p>
    <figure id="chpd" class="m_original">
      <img src="https://miro.medium.com/v2/resize:fit:299/1*98CQaZJ19McbwQlwXM_i8A.png" width="239" />
    </figure>
    <p id="G2oM">В посте также содержится базовое пошаговое объяснение того, как построить представление схемы R1CS, которое было построено на основе очень простых вычислений, так что за ним легко следить.</p>
  </section>
  <hr />
  <section style="background-color:hsl(hsl(323, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <h2 id="irFc">Схемы полиномиальных обязательств</h2>
    <p id="vEAv">Схема полиномиальных обязательств — это способ для <strong>верифицируемого</strong> привязаться к данному многочлену, не раскрывая его коэффициенты <strong>верификатору</strong>.</p>
    <p id="tJDe">На момент написания этой статьи двумя распространенными схемами полиномиальных обязательств являются <strong>KZG </strong>и <strong>FRI</strong>, но есть и другие версии, с которыми люди экспериментируют, такие как <strong>Dory</strong>, <strong>Ligero </strong>и <strong>Brakedown</strong>.</p>
    <p id="5C1l">Схемы полиномиальных обязательств обычно комбинируются с полиномиальными интерактивными доказательствами оракула и преобразованием Фиата-Шамира для получения SNARK&#x27;ов.</p>
  </section>
  <hr />
  <section style="background-color:hsl(hsl(170, 33%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <h2 id="v646">KZG</h2>
    <p id="UJK8">KZG расшифровывается как <strong>Кейт</strong>, <strong>Заверуча</strong> и <strong>Голберг</strong>, которые являются авторами этой схемы полиномиальных обязательств.</p>
    <p id="Quo7">Он использует <strong>“степени Тау”</strong> для генерации глобальных параметров, которые затем используются для привязки к полиномам. Это надежная настройка, поэтому она требует, чтобы <strong>степени Тау</strong> были удалены после того, как они будут использованы для генерации глобальных параметров. Поскольку их необходимо удалить, вы часто будете слышать, как <strong>степени Тау</strong> называют <strong>“токсичными отходами”</strong>.</p>
    <p id="NAaE">Вы также услышите о <strong>“церемониях KZG”</strong>, которые представляют собой способы для всех участников церемонии внести случайный вклад в <strong>степени Тау</strong> таким образом, что только одному участнику церемонии нужно удалить свой вклад в случайность, чтобы токсичные отходы были выброшены.</p>
  </section>
  <hr />
  <section style="background-color:hsl(hsl(323, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <h2 id="6WFR">Степени Тау</h2>
    <p id="sACL">Итак, что же это за <strong>степени Тау</strong>? <strong>Тау </strong>— это случайный <strong>элемент поля</strong>, который многократно <strong>умножается </strong>на себя и элементы Вашей группы во время схемы полиномиального обязательства <strong>KZG</strong>.</p>
  </section>
  <hr />
  <section style="background-color:hsl(hsl(170, 33%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <h2 id="qQj1">Конечное поле</h2>
    <p id="JZ9P">Говоря об <strong>“элементах поля”</strong>, вы можете часто слышать, что исследователи упоминают <strong>“конечное поле”</strong>, что это такое?</p>
    <p id="ZOWK">На момент написания этой статьи SNARK&#x27;и не могут работать со всеми целыми числами, поэтому конечное поле — это подмножество целых чисел, в котором определен ваш SNARK для работы. Элементы поля, которые я упомянул в определении <strong>степеней Тау</strong>, являются элементами этого определенного конечного поля.</p>
    <p id="fJOW">Конечные поля используют модульную арифметику для ограничения поля до требуемого размера.</p>
  </section>
  <hr />
  <section style="background-color:hsl(hsl(323, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <h2 id="Gjrs">Быстрые доказательства близости интерактивного оракула Рида-Соломона (FRI)</h2>
    <p id="XvD6"><strong>FRI </strong>— популярная альтернативная схема предоставления полиномиальных обязательств <strong>KZG</strong>, которая не требует доверенной настройки (называется “прозрачной”, что, следовательно, является <strong>T</strong> в <strong>STARK</strong>). Он использует кодировку Рида-Соломона и деревья Меркла для вероятностной проверки достоверности полинома степени <strong>D</strong> с использованием запросов менее <strong>D</strong> (отсюда и <strong>“Быстрый”</strong>).</p>
  </section>
  <hr />
  <section style="background-color:hsl(hsl(170, 33%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <h2 id="Wode">Интерполяция Лагранжа</h2>
    <p id="t6lF"><strong>Интерполяция Лагранжа</strong> — это способ построения многочлена, проходящего через <strong>заданный набор точек</strong>. Он используется как полиномиальный аналог <strong>кодирования Рида-Соломона</strong>, при котором, если два полинома отличаются даже на небольшую величину, их оценки интерполяции Лагранжа будут резко отличаться.</p>
  </section>
  <hr />
  <section style="background-color:hsl(hsl(323, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <h2 id="4FtR">Многолинейные расширения</h2>
    <p id="djgk"><strong>Многолинейные расширения</strong> (часто сокращаемые до <strong>MLEs</strong>) представляют собой <strong>многолинейные многочлены</strong> (например, <strong>x_1*x_2 + 4*x_1 + 3*x_2</strong>), построенные из <strong>многомерных многочленов </strong>(например, <strong>3x2 + 2xy + y3 + 5</strong>) с помощью <strong>интерполяции Лагранжа</strong>. Эти многолинейные многочлены полезны в контексте zk-SNARK, потому что их обычно быстрее проверять.</p>
  </section>
  <hr />
  <section style="background-color:hsl(hsl(170, 33%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <h2 id="Ktxy">Свидетель</h2>
    <p id="Y4Et"><strong>Свидетель </strong>— это входные данные для некоторого вычисления, которые используются для доказательства правильности утверждения без предоставления самого утверждения напрямую.</p>
    <p id="s9Il">В контексте арифметических схем <strong>свидетель </strong>— это способ для <strong>верифицируемого</strong> построить вычислительную “трассировку” своего выполнения арифметической схемы, которая позволяет <strong>верификатору</strong> вероятностно проверять правильность выполнения <strong>верифицируемого</strong> за сублинейное время, не выдавая саму схему.</p>
    <p id="4ymY">Обратите внимание, что свидетельство <strong>верифицируемого</strong> является секретным, что означает, что оно не передается непосредственно <strong>верификатору</strong>, но вместо этого вместо него дается краткое доказательство (отсюда <strong>S</strong> в <strong>SNARK</strong>).</p>
  </section>
  <hr />
  <section style="background-color:hsl(hsl(323, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <h2 id="NzCQ"><strong>Перестановки над базисами Лагранжа для Вселенских Неинтерактивных аргументов Знания (PLONK)</strong></h2>
    <p id="wDeF"><strong>PLONK </strong>— это популярный тип SNARK, который включает в себя доказательство 4 вещей об арифметической схеме:</p>
    <ul id="d7Px">
      <li id="g8bP">вентили схемы установлены правильно</li>
      <li id="erqE">входные сигналы в схему указаны правильно</li>
      <li id="m6xa">соединение между вентилями правильное</li>
      <li id="YHzs">выходной сигнал схемы правильный</li>
    </ul>
    <p id="JUqp">Термин <strong>“перестановка”</strong> происходит от способа доказательства подключения, поскольку <strong>PLONK </strong>использует перестановку многочлена для доказательства правильности подключения.</p>
  </section>
  <hr />

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@jetix37/smart-contract-vulnerabilities-p2</guid><link>https://teletype.in/@jetix37/smart-contract-vulnerabilities-p2?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=jetix37</link><comments>https://teletype.in/@jetix37/smart-contract-vulnerabilities-p2?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=jetix37#comments</comments><dc:creator>jetix37</dc:creator><title>Уязвимости в смарт-контрактах: Часть 2</title><pubDate>Sun, 05 Mar 2023 18:37:54 GMT</pubDate><media:content medium="image" url="https://img3.teletype.in/files/68/bc/68bce919-c471-41f6-8e97-8960c8d81c37.png"></media:content><category>Ethereum</category><description><![CDATA[<img src="https://media.kg-portal.ru/anime/r/renaiflops/trailers/49973t_2x.jpg"></img>Оригинал — https://github.com/kadenzipfel/smart-contract-vulnerabilities]]></description><content:encoded><![CDATA[
  <section style="background-color:hsl(hsl(323, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="MnV2">Оригинал — <a href="https://github.com/kadenzipfel/smart-contract-vulnerabilities" target="_blank">https://github.com/kadenzipfel/smart-contract-vulnerabilities</a></p>
    <p id="xyex">Канал — <a href="https://t.me/jetix37eth" target="_blank">https://t.me/jetix37eth</a></p>
  </section>
  <figure id="hauW" class="m_original">
    <img src="https://media.kg-portal.ru/anime/r/renaiflops/trailers/49973t_2x.jpg" width="1480" />
  </figure>
  <ol id="9i9e">
    <li id="ZXOq"><a href="#Ew9S">Неправильное имя конструктора</a></li>
    <li id="i2J6"><a href="#vPgY">Переопределение переменных</a></li>
    <li id="llgJ"><a href="#mvr0">Слабые источники случайности</a></li>
    <li id="rQBv"><a href="#1kOB">Отсутствие защиты от атак с повторным воспроизведением подписей</a></li>
    <li id="qzZT"><a href="#QoGX">Нарушение требований</a></li>
    <li id="J5He"><a href="#rbN5">Запись в произвольное место хранения</a></li>
    <li id="Qu08"><a href="#MfVA">Неправильный порядок наследования</a></li>
    <li id="7BBa"><a href="#GNZv">Произвольный переход функциональной переменной</a></li>
    <li id="iirR"><a href="#ua8z">Наличие неиспользуемых переменных</a></li>
    <li id="brcG"><a href="#bMyd">Неожиданный баланс эфира</a></li>
    <li id="jqBt"><a href="#mEu9">Незашифрованные секреты</a></li>
    <li id="tCl0"><a href="#9S6c">Обнаружение неисправного контракта</a></li>
    <li id="CxuS"><a href="#4RsY">Очищенная зависимость от блокчейна</a></li>
    <li id="jbCJ"><a href="#R22p">Несоответствие стандартам</a></li>
    <li id="IUEB"><a href="#ldyK">Незащищенный Callback</a></li>
    <li id="uaE1"><a href="#7Xl2">Определение типа адреса на основе размера кода</a></li>
    <li id="iq9L"><a href="#1rwV">Зависимость от очереди транзакций</a></li>
    <li id="I7Bc"><a href="#OX3e">DoS с ограничением расхода газа в блоке</a></li>
    <li id="0Pz6"><a href="#CtUr">DoS с (неожиданным) возвратом</a></li>
  </ol>
  <section style="background-color:hsl(hsl(323, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <h2 id="Ew9S">Неправильное имя конструктора</h2>
    <p id="4gj6">До Solidity <strong>0.4.22</strong> единственным способом определить конструктор было создание функции с именем контракта. В некоторых случаях это было проблематично. Например, если смарт-контракт повторно используется с другим именем, но функция конструктора также не изменяется, он просто становится обычной вызываемой функцией.</p>
    <p id="sgqq">Теперь, с современными версиями Solidity, вы можете определить конструктор с помощью ключевого слова <strong>constructor</strong>, устраняя эту уязвимость. Таким образом, решение этой проблемы заключается просто в использовании современных версий компилятора Solidity.</p>
  </section>
  <section style="background-color:hsl(hsl(170, 33%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <h2 id="vPgY">Переопределение переменных</h2>
    <p id="5NId">Можно дважды использовать одну и ту же переменную в Solidity, но это может привести к непреднамеренным побочным эффектам. Это особенно сложно при работе с несколькими контрактами. Например:</p>
    <figure id="qN1n" class="m_original">
      <img src="https://img3.teletype.in/files/2a/b9/2ab9ad1a-5ecc-405d-a78c-2ede790010f2.png" width="662" />
    </figure>
    <p id="ENMb">Здесь мы можем видеть, что <strong>SubContract</strong> наследует <strong>SuperContract</strong>, а переменная <strong>a</strong> определяется дважды с разными значениями. Теперь предположим, что мы используем <strong>a</strong> для выполнения некоторой функции в <strong>SubContract</strong>, функциональность, унаследованная от <strong>SuperContract</strong>, больше не будет работать, поскольку значение <strong>a</strong> было изменено.</p>
    <p id="iuYv">Чтобы избежать этой уязвимости, важно, чтобы мы проверили всю систему смарт-контрактов на наличие двусмысленностей. Также важно проверить наличие предупреждений компилятора, поскольку они могут отмечать эти двусмысленности, пока они есть в смарт-контракте.</p>
  </section>
  <section style="background-color:hsl(hsl(323, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <h2 id="mvr0">Слабые источники случайности</h2>
    <p id="J0yT">В Ethereum существуют определенные приложения, которые полагаются на генерацию случайных чисел для обеспечения справедливости. Однако генерация случайных чисел в Ethereum очень сложна, и есть несколько подводных камней, которые стоит рассмотреть.</p>
    <p id="Jbk6">Использование атрибутов цепочки, таких как: <strong>block.timestamp</strong>, <strong>block.hash</strong> и <strong>block.difficulty</strong>, может показаться хорошей идеей, поскольку они часто выдают псевдослучайные значения. Проблема, однако, заключается в способности майнера изменять эти значения. </p>
    <p id="Sw1q">Например, в приложении для азартных игр с джекпотом в несколько миллионов долларов у майнера есть достаточный стимул генерировать множество альтернативных блоков, выбирая только тот блок, который принесет майнеру джекпот. Конечно, подобный контроль над блокчейном сопряжен со значительными затратами, но если ставки достаточно высоки, это, безусловно, можно сделать.</p>
    <p id="JtjR">Чтобы избежать манипуляций майнера при генерации случайных чисел, существует несколько решений:</p>
    <ul id="IDtD">
      <li id="xdFF">Схема обязательств, такая как <strong>RANDAO</strong>, <strong>DAO</strong>, где случайное число генерируется всеми участниками <strong>DAO</strong>.</li>
      <li id="gCs9">Внешние источники через оракулы, например <strong>Oraclize</strong>.</li>
      <li id="ev1S">Использовать хэши блоков биткойна, поскольку сеть более децентрализована, а майнинг блоков обходится дороже.</li>
    </ul>
  </section>
  <section style="background-color:hsl(hsl(170, 33%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <h2 id="1kOB">Отсутствие защиты от атак с повторным воспроизведением подписей</h2>
    <p id="xrDW">Иногда в смарт-контрактах необходимо выполнить проверку подписи, чтобы улучшить удобство использования и снизить стоимость газа. Однако при внедрении проверки подписи необходимо проверять хеши. Для защиты от атак с повторным использованием сигнатур контракт должен разрешать обработку только новых хэшей. Это предотвращает многократную замену подписи другого пользователя злоумышленниками.</p>
    <p id="G5Un">Чтобы быть в большей безопасности при проверке подписи, следуйте этим рекомендациям:</p>
    <ul id="UCFE">
      <li id="ZDnf">Сохраните хэш каждого сообщения, обработанного контрактом, затем сверьте хэши сообщений с существующими перед выполнением функции.</li>
      <li id="MWWP">Включите адрес контракта в хэш, чтобы гарантировать, что сообщение используется только в одном контракте.</li>
      <li id="GmnS">Никогда не генерируйте хэш сообщения, включая подпись.</li>
    </ul>
  </section>
  <section style="background-color:hsl(hsl(323, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <h2 id="QoGX">Нарушение требований</h2>
    <p id="AMwT">Метод <strong>require() </strong>предназначен для проверки условий, таких как входные данные или переменные состояния контракта, или для проверки возвращаемых значений из внешних вызовов контракта. </p>
    <p id="pmkL">Для проверки внешних вызовов входные данные могут предоставляться вызывающими абонентами или они могут быть возвращены вызываемым. В случае, если возвращаемое значение вызываемого абонента нарушило ввод, скорее всего, одна из двух вещей пошла не так:</p>
    <ul id="VygX">
      <li id="Aiip">Существует ошибка в контракте, который предоставил входные данные.</li>
      <li id="Ezdo">Условие требования слишком строгое.</li>
    </ul>
    <p id="8mzf">Чтобы решить эту проблему, сначала подумайте, не является ли условие требования слишком строгим. При необходимости ослабьте его, чтобы разрешить любой допустимый внешний ввод. </p>
    <p id="ROwu">Если проблема не в обязательном условии, то в контракте, предоставляющем внешний ввод, должна быть ошибка. Убедитесь, что этот контракт не предоставляет недопустимые входные данные.</p>
  </section>
  <section style="background-color:hsl(hsl(170, 33%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <h2 id="rbN5">Запись в произвольное место хранения</h2>
    <p id="3QJc">Только авторизованные адреса должны иметь доступ для записи в важные хранилища. Если на протяжении всего контракта не проводится надлежащая проверка авторизации, злоумышленник может перезаписать важные данные. </p>
    <p id="bnUL">Однако, даже если существуют проверки авторизации для записи важных данных данных, злоумышленник все равно может перезаписать их с помощью нечувствительных данных. Это может дать злоумышленнику доступ к перезаписи важных переменных, таких как владелец контракта.</p>
    <p id="ObNv">Чтобы предотвратить это, надо не только защитить хранилища конфиденциальных данных с помощью требований авторизации, но и гарантировать, что записи в одну структуру данных не могут непреднамеренно перезаписывать записи в другой структуре данных.</p>
  </section>
  <section style="background-color:hsl(hsl(323, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <h2 id="MfVA">Неправильный порядок наследования</h2>
    <p id="YTRE">В Solidity возможно наследование из нескольких источников, что при неправильном понимании может привести к двусмысленности. Эта двусмысленность известна как проблема бриллианта, в которой, если два базовых контракта выполняют одну и ту же функцию, какому из них следует отдать приоритет? К счастью, Solidity изящно справляется с этой проблемой, то есть до тех пор, пока разработчик понимает решение.</p>
    <p id="Up0h">Решение, которое Solidity обеспечивает для проблемы алмаза, заключается в использовании обратной C3-линеаризации. Это означает, что он будет линеаризовать наследование справа налево, поэтому порядок наследования имеет значение. Предлагается начинать с более общих контрактов и заканчивать более конкретными контрактами, чтобы избежать проблем.</p>
  </section>
  <section style="background-color:hsl(hsl(170, 33%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <h2 id="GNZv">Произвольный переход функциональной переменной </h2>
    <p id="bwi5">Функциональные типы поддерживаются в Solidity. Это означает, что переменная типа function может быть назначена функции с соответствующей сигнатурой. Затем функция может быть вызвана из переменной точно так же, как и любая другая функция. Пользователи не должны иметь возможности изменять функциональную переменную, но в некоторых случаях это возможно.</p>
    <p id="bR5w">Если смарт-контракт использует определенные инструкции по сборке, например, <strong>mstore</strong>, злоумышленник может указать функциональную переменную на любую другую функцию. Это может дать злоумышленнику возможность нарушить функциональность контракта и, возможно, даже истощить средства контракта.</p>
  </section>
  <section style="background-color:hsl(hsl(323, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <h2 id="ua8z">Наличие неиспользуемых переменных</h2>
    <p id="hjdA">Хоть это и разрешено, лучше всего избегать неиспользуемых переменных. Неиспользуемые переменные могут привести к нескольким различным проблемам:</p>
    <ul id="4XIu">
      <li id="2Zyt">Увеличение объема вычислений (ненужное потребление газа).</li>
      <li id="2nJI">Указание на ошибки или искаженные структуры данных</li>
      <li id="93t8">Сниженная читаемость кода</li>
    </ul>
    <p id="FqgO">Настоятельно рекомендуется удалить все неиспользуемые переменные из кода.</p>
  </section>
  <section style="background-color:hsl(hsl(170, 33%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <h2 id="bMyd">Неожиданный баланс эфира</h2>
    <p id="PirN">Если контракт для работы предполагает определенный баланс, он уязвим для атаки.</p>
    <p id="VEyj">Допустим, у нас есть контракт, который предотвращает выполнение всех функций, если в контракте хранится какой-либо эфир. Если злоумышленник решит воспользоваться путем принудительной отправки эфира, он вызовет DoS, что сделает контракт непригодным для использования. </p>
    <p id="itpO">По этой причине важно никогда не использовать строгие проверки равенства для баланса эфира в контракте.</p>
  </section>
  <section style="background-color:hsl(hsl(323, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <h2 id="mEu9">Незашифрованные секреты</h2>
    <p id="9IuL">Код смарт-контракта Ethereum всегда можно прочитать. Даже если ваш код не проверен на Etherscan, злоумышленники все равно могут декомпилировать или даже просто проверять транзакции к нему и из него, чтобы проанализировать его.</p>
    <p id="ISlX">Одним из примеров проблемы здесь может быть &quot;игра в угадайку&quot;, в которой пользователь должен угадать сохраненную закрытую переменную, чтобы выиграть эфир в контракте. Это, конечно, чрезвычайно тривиально для использования (до такой степени, что вам не стоит пробовать это, потому что это почти наверняка контракт <strong>honeypot</strong>, который намного сложнее).</p>
    <p id="nXGg">Другой распространенной проблемой здесь является использование незашифрованных секретов вне цепочки, таких как ключи API и вызовы оракулов. Если ваш ключ API может быть определен, злоумышленники могут либо просто использовать его для себя, либо воспользоваться другими преимуществами, такими как исчерпание разрешенных вами вызовов API и принудительное возвращение оракулом страницы с ошибкой, которая может привести или не привести к проблемам в зависимости от структуры контракта.</p>
  </section>
  <section style="background-color:hsl(hsl(170, 33%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <h2 id="9S6c">Обнаружение неисправного контракта</h2>
    <p id="gtx8">Некоторые контракты не хотят, чтобы другие контракты взаимодействовали с ними (очень распространено в контрактах азартных игр, использующих низкокачественный рандом).</p>
    <p id="pWKS">Распространенный способ предотвратить это - проверить, хранится ли в вызывающей учетной записи какой-либо код. Однако учетные записи контрактов, инициирующие вызовы во время их создания, еще не будут показывать, что они хранят код, эффективно обходя обнаружение контракта.</p>
  </section>
  <section style="background-color:hsl(hsl(323, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <h2 id="4RsY">Очищенная зависимость от блокчейна</h2>
    <p id="zT7x">Многие контракты полагаются на вызовы, происходящие в течение определенного периода времени, но Ethereum можно заспамить транзакциями с очень высоким <strong>Gwei</strong> в течение приличного периода времени относительно дешево.</p>
    <p id="sKL9">Например, <strong>FOMO3D </strong>(игра с обратным отсчетом, в которой последний инвестор выигрывает джекпот, но каждая инвестиция добавляет время к обратному отсчету) выиграл пользователь, который полностью заблокировал блокчейн на небольшой промежуток времени, запретив другим инвестировать, пока не истечет таймер и он не выиграет.</p>
    <p id="iBsZ">В настоящее время существует множество контрактов на азартные игры с участием &quot;крупье&quot;, которые полагаются на хэши прошлых блоков для обеспечения рандома. По большей части это не самый плохой источник рандома, и они даже учитывают сокращение хэшей, которое происходит после 256 блоков, но в этот момент многие из них просто обнуляют ставку. </p>
    <p id="YA2I">Это позволило бы кому-то делать ставки на многие из этих аналогично функционирующих контрактов с определенным результатом в качестве победителя по всем из них, проверять подачу крупье, пока она еще находится на рассмотрении, и, если она неблагоприятна, просто блокировать блокчейн до тех пор, пока не произойдет обрезка, и вы сможете вернуть свои ставки.</p>
  </section>
  <section style="background-color:hsl(hsl(170, 33%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <h2 id="R22p">Несоответствие стандартам</h2>
    <p id="Jv1L">С точки зрения разработки смарт-контрактов важно следовать стандартам. Стандарты установлены для предотвращения уязвимостей, и игнорирование их может привести к неожиданным последствиям.</p>
    <p id="o4zT">Возьмем, к примеру, оригинальный токен BNB от Binance. Он продавался как токен ERC20, но позже было указано, что на самом деле он не соответствует стандарту ERC20 по нескольким причинам:</p>
    <ul id="CMeT">
      <li id="V9CJ">Он блокирует отправку на <strong>0x0</strong></li>
      <li id="N6Md">Он блокирует пустые транзакции</li>
      <li id="0knc">Он не возвращает <strong>true</strong> или <strong>false </strong>после транзакции</li>
    </ul>
    <p id="YsuH">Основная причина для беспокойства по поводу этой неправильной реализации заключается в том, что если он используется со смарт-контрактом, который ожидает токен ERC-20, она будет вести себя неожиданным образом. Он может даже застрять в контракте навсегда.</p>
    <p id="0OCh">Хотя стандарты не всегда идеальны и могут когда-нибудь устареть, они способствуют созданию наиболее безопасных смарт-контрактов.</p>
  </section>
  <section style="background-color:hsl(hsl(323, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <h2 id="ldyK">Незащищенный Callback</h2>
    <p id="jT7w">При написании или взаимодействии с <strong>callback</strong>-функциями в Solidity важно убедиться, что они не могут быть использованы для выполнения неожиданных эффектов.</p>
    <p id="3LjR">Давайте посмотрим на функцию ERC721._safeMint от OpenZeppelin:</p>
    <figure id="61np" class="m_original">
      <img src="https://img3.teletype.in/files/6d/b0/6db0abcc-0696-4f62-8314-980d7e35ff12.png" width="1056" />
    </figure>
    <p id="KLiL">Функция называется<strong> _safeMint,</strong> потому что она предотвращает непреднамеренный минт токенов к контракту, сначала проверяя, реализован ли в этом контракте <strong>ERC721Receiver</strong>, т.е. помечая себя как добровольного получателя NFT.</p>
    <p id="uE4o">Все это кажется прекрасным, но поскольку <strong>_checkOnERC721Received</strong> является <strong>callback</strong>-функцией, контракт получателя может определять любую произвольную логику для выполнения, включая повторный вход в исходную функцию <strong>mint</strong>, тем самым обходя ограничения, определенные в коде контракта. Например, в этой функции:</p>
    <figure id="hHhN" class="m_original">
      <img src="https://img1.teletype.in/files/01/1d/011dd3fd-34cb-46ba-9664-154c8d493987.png" width="1275" />
    </figure>
    <p id="0G9O">Поскольку у нас есть доступ к незащищенному <strong>callback</strong>, мы можем повторно войти в <strong>mint</strong> после того, как будет отчеканен только один токен, и отчеканить дополнительные <strong>MAX_PER_USER - 1</strong> токенов следующим образом:</p>
    <figure id="NfJG" class="m_original">
      <img src="https://img1.teletype.in/files/8e/84/8e843631-09fb-4962-8cde-293ca3658d2c.png" width="724" />
    </figure>
    <p id="kkNW">Чтобы устранить уязвимость, мы можем использовать функцию <strong>ERC721._mint</strong> или защиту от повторного входа.</p>
  </section>
  <section style="background-color:hsl(hsl(170, 33%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <h2 id="7Xl2">Определение типа адреса на основе размера кода</h2>
    <p id="psMx">Распространенным методом определения того, является ли отправитель контрактом или кошельком, была проверка размера кода отправителя. Эта проверка утверждает, что если отправитель имеет размер кода <strong>&gt; 0</strong>, то это должен быть контракт, а если нет, то это должен быть кошелек. Например:</p>
    <figure id="zifc" class="m_original">
      <img src="https://img4.teletype.in/files/f2/d2/f2d297e8-77f5-49a7-9b82-abeafd56b237.png" width="889" />
    </figure>
    <p id="VuRZ">Однако недавно было обнаружено, что это утверждение неверно и может быть игнорировано. По этой причине важно, чтобы логика вашего смарт-контракта не утверждала, что адрес отправителя является кошельком просто потому, что размер кода равен 0.</p>
  </section>
  <section style="background-color:hsl(hsl(323, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <h2 id="1rwV">Зависимость от очереди транзакций</h2>
    <p id="ceM4">Транзакции в Ethereum сгруппированы в блоки, которые обрабатываются с полурегулярным интервалом ~15 секунд. Прежде чем транзакции будут размещены в блоках, они передаются в мемпул, где создатели блоков затем могут приступить к их размещению в соответствии с экономически оптимальным вариантом. Что здесь важно понимать, так это то, что мемпул является общедоступным, и, следовательно, любой может видеть транзакции до их выполнения, что дает ему возможность запускать их заранее, размещая свою собственную транзакцию, выполняющую то же самое или аналогичное действие с более высокой ценой на газ. </p>
    <p id="XVVC">Фронтраннинг стал настолько распространенным в результате того, что все более распространенными становятся боты-фронтраннеры, которые работают, наблюдая за мемпулом в поисках прибыльных, воспроизводимых транзакций, которые они могут заменить для собственной выгоды.</p>
    <p id="3Zzj">Одним из решений зависимости от порядка транзакций является использование схемы <strong>commit-reveal</strong> в случае передачи информации в блокчейне. Это работает за счет того, что отправитель отправляет хэш информации, сохраняя ее в цепочке вместе с адресом пользователя, чтобы позже они могли раскрыть ответ вместе с солью, чтобы доказать, что они действительно были правильными. Другое решение - просто использовать частный мемпул.</p>
  </section>
  <section style="background-color:hsl(hsl(170, 33%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <h2 id="OX3e">DoS с ограничением расхода газа в блоке</h2>
    <p id="ObW3">Одним из основных преимуществ лимита газа в блоке является то, что оно не позволяет злоумышленникам создавать бесконечный цикл транзакций. Если использование газа в транзакции превысит этот предел, транзакция завершится неудачей. Однако наряду с этим преимуществом возникает побочный эффект, который важно понимать.</p>
    <h3 id="QRnG">Неограниченные операции</h3>
    <p id="CIhd">Примером, в котором лимит газа в блоке может быть проблемой, является выполнение логики в неограниченном цикле. Даже без какого-либо злого умысла это может легко пойти не так. Например, наличие слишком большого числа пользователей для отправки средств может превысить лимит газа и помешать успешной транзакции, потенциально навсегда заблокировав средства.</p>
    <p id="mVTu">Эта ситуация также может привести к атаке. Допустим, плохой человек решает создать значительное количество адресов, при этом каждому адресу выплачивается небольшая сумма средств из смарт-контракта. Если все сделано эффективно, транзакция может быть заблокирована на неопределенный срок, возможно, даже предотвратив проведение дальнейших транзакций.</p>
    <p id="nDhj">Эффективным решением этой проблемы было бы использование <strong>pull</strong>-платежной системы поверх вышеупомянутой <strong>push</strong>-платежной системы. Чтобы сделать это, выделите каждый платеж в отдельную транзакцию и попросите получателя вызвать функцию.</p>
    <p id="niqn">Если по какой-то причине вам действительно нужно перебирать массив неопределенной длины, по крайней мере, ожидайте, что он потенциально займет несколько блоков, и разрешите выполнять его в нескольких транзакциях - как показано в этом примере:</p>
    <figure id="QMmA" class="m_original">
      <img src="https://img3.teletype.in/files/a7/02/a7021c34-9985-43ad-9108-cc475d95c6f5.png" width="830" />
    </figure>
    <h3 id="0JDR">Набивание блока</h3>
    <p id="HErc">В некоторых ситуациях ваш контракт может быть атакован с помощью лимита газа в блоке, даже если вы не выполняете цикл по массиву неопределенной длины. Злоумышленник может заполнить несколько блоков, прежде чем транзакция сможет быть обработана, используя достаточно высокую цену на газ.</p>
    <p id="8spx">Эта атака осуществляется путем проведения нескольких транзакций по очень высокой цене на газ. Если цена на газ достаточно высока, а транзакции потребляют много газа, они могут заполнить целые блоки и помешать обработке других транзакций.</p>
    <p id="09RP">Транзакции Ethereum требуют, чтобы отправитель платил газ, чтобы предотвратить спам-атаки, но в некоторых ситуациях может быть достаточно стимулов для проведения такой атаки. Например, атака с набиванием блоков была использована в приложении для азартных игр <strong>Fomo3D</strong>. </p>
    <p id="83Rh">В приложении был таймер обратного отсчета, и пользователи могли выиграть джекпот, купив ключ последними, за исключением того, что каждый раз, когда пользователь покупал ключ, таймер продлевался. Злоумышленник купил ключ, а затем набил следующие 13 блоков подряд, чтобы выиграть джекпот.</p>
    <p id="S9TO">Чтобы предотвратить возникновение таких атак, важно тщательно продумать, безопасно ли включать действия, основанные на времени, в ваше приложение.</p>
  </section>
  <section style="background-color:hsl(hsl(323, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <h2 id="CtUr">DoS с (неожиданным) возвратом</h2>
    <p id="pLKl">DoS-атаки (отказ в обслуживании) могут возникать в функциях, когда вы пытаетесь отправить средства пользователю, и функциональность зависит от того, что перевод средств будет успешным.</p>
    <p id="5OJj">Это может быть проблематично в случае, если средства отправляются на смарт-контракт, созданный злоумышленником, поскольку они могут просто создать <strong>fallback</strong>-функцию, которая отменяет все платежи.</p>
    <p id="UaOx">Например:</p>
    <figure id="P1Et" class="m_original">
      <img src="https://img1.teletype.in/files/86/42/864201c4-4958-4976-afd5-d3628b79c7de.png" width="745" />
    </figure>
    <p id="vC4W">Как вы можете видеть в этом примере, если злоумышленник делает ставки по смарт-контракту с <strong>fallback</strong>-функцией, возвращающей все платежи, они никогда не могут быть возвращены, и, следовательно, никто никогда не сможет сделать более высокую ставку.</p>
    <p id="cJ6q">Это также может быть проблематично без присутствия злоумышленника. Например, вы можете захотеть оплатить массив пользователей путем итерации по массиву, и, конечно, вы хотели бы убедиться, что каждому пользователю должным образом оплачено. Проблема здесь в том, что если один платеж не выполняется, функция отменяется и никому не выплачивается.</p>
    <figure id="TtRl" class="m_original">
      <img src="https://img3.teletype.in/files/6f/54/6f543a25-3de0-4e72-ba46-821d7c21b338.png" width="1043" />
    </figure>
    <p id="KsIC">Эффективным решением этой проблемы было бы использование <strong>pull</strong>-платежной системы поверх вышеупомянутой <strong>push</strong>-платежной системы. Чтобы сделать это, выделите каждый платеж в отдельную транзакцию и попросите получателя вызвать функцию.</p>
    <figure id="7SLk" class="m_original">
      <img src="https://img4.teletype.in/files/32/f9/32f961d0-aeae-43fb-885d-6540e6aed8f7.png" width="1147" />
    </figure>
  </section>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@jetix37/smart-contract-vulnerabilities-p1</guid><link>https://teletype.in/@jetix37/smart-contract-vulnerabilities-p1?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=jetix37</link><comments>https://teletype.in/@jetix37/smart-contract-vulnerabilities-p1?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=jetix37#comments</comments><dc:creator>jetix37</dc:creator><title>Уязвимости в смарт-контрактах: Часть 1</title><pubDate>Sat, 04 Mar 2023 17:00:13 GMT</pubDate><media:content medium="image" url="https://img2.teletype.in/files/95/c1/95c1037b-443e-4547-94f2-5db5261c2150.png"></media:content><category>Ethereum</category><description><![CDATA[<img src="https://img3.teletype.in/files/2a/a6/2aa6e935-5ee5-445a-a9b3-5449f643f2a1.jpeg"></img>Оригинал — https://github.com/kadenzipfel/smart-contract-vulnerabilities]]></description><content:encoded><![CDATA[
  <section style="background-color:hsl(hsl(323, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="MnV2">Оригинал — <a href="https://github.com/kadenzipfel/smart-contract-vulnerabilities" target="_blank">https://github.com/kadenzipfel/smart-contract-vulnerabilities</a></p>
    <p id="xyex">Канал — <a href="https://t.me/jetix37eth" target="_blank">https://t.me/jetix37eth</a></p>
  </section>
  <figure id="IEUV" class="m_original">
    <img src="https://img3.teletype.in/files/2a/a6/2aa6e935-5ee5-445a-a9b3-5449f643f2a1.jpeg" width="1000" />
  </figure>
  <ol id="LX93">
    <li id="hohn"><a href="#gHyc">Принудительная отправка ETH в контракт</a></li>
    <li id="Iu4o"><a href="#UdG7">Недостаточное выделение газа</a></li>
    <li id="DUPl"><a href="#fqyC">Reentrancy</a></li>
    <li id="SxFF"><a href="#50OU">Переполнение и недополнение целых чисел</a></li>
    <li id="z8e2"><a href="http://Зависимость от временной метки" target="_blank">Зависимость от временной метки</a></li>
    <li id="qw6w"><a href="#HRvT">Авторизация через tx.origin</a></li>
    <li id="aHR6"><a href="#1If8">Плавающая pragma</a></li>
    <li id="wPQN"><a href="#RJga">Видимость функции по умолчанию</a></li>
    <li id="8ySZ"><a href="#nh0o">Устаревшая версия компилятора</a></li>
    <li id="9NUT"><a href="#cpHG">Непроверенное возвращаемое значение вызова</a></li>
    <li id="ejgX"><a href="#dV0T">Незащищенный вывод эфира</a></li>
    <li id="NUQk"><a href="#NjSD">Незащищенная инструкция Selfdestruct</a></li>
    <li id="cUOj"><a href="#rY0A">Видимость переменной состояния по умолчанию</a></li>
    <li id="GSaV"><a href="#j5E4">Неинициализированный указатель хранилища</a></li>
    <li id="jmPK"><a href="#9OGw">Нарушение assert</a></li>
    <li id="29Z5"><a href="#adfi">Использование устаревших функций</a></li>
    <li id="T6Cm"><a href="#iW3c">delegateCall ненадежному вызываемому абоненту</a></li>
    <li id="t8BQ"><a href="#PaPn">Податливость подписи</a></li>
  </ol>
  <section style="background-color:hsl(hsl(323, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <h2 id="gHyc">Принудительная отправка ETH в контракт</h2>
    <p id="ljTj">Иногда пользователям нежелательно иметь возможность отправлять эфир в смарт-контракт. К сожалению, есть возможность обойти функцию <strong>fallback()</strong> контракта и принудительно отправить эфир.</p>
    <figure id="t7J1" class="m_original">
      <img src="https://img3.teletype.in/files/e6/0e/e60e2943-55c2-4596-bcbe-5bcf49dd209b.png" width="556" />
    </figure>
    <p id="4wnX">Хотя кажется, что любая транзакция с уязвимым контрактом должна быть отменена, на самом деле существует пара способов принудительной отправки эфира.</p>
    <p id="9fVB">Первый метод заключается в вызове метода <strong>selfdestruct</strong> для контракта с адресом уязвимого контракта, установленным в качестве получателя. Это работает, потому что <strong>selfdestruct</strong> не вызовет функцию <strong>fallback()</strong>.</p>
    <p id="eXFh">Другой метод заключается в предварительном вычислении адреса контракта и отправке эфира на этот адрес еще до того, как контракт будет развернут. Как ни удивительно, это возможно.</p>
  </section>
  <section style="background-color:hsl(hsl(170, 33%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <h2 id="UdG7">Недостаточное выделение газа</h2>
    <p id="viyL"><strong>Грифинг</strong> — это тип атаки, часто применяемый в видеоиграх, когда злоумышленник играет в игру непреднамеренным образом, чтобы мучить других игроков, также известный как троллинг. Этот тип атаки также используется для предотвращения выполнения транзакций по назначению.</p>
    <p id="krwk">Эта атака может быть осуществлена на контракты, которые принимают данные и используют их во вложенном вызове другого контракта. Этот метод часто используется в кошельках с несколькими подписями, а также в ретрансляторах транзакций. Если вспомогательный вызов завершается неудачей, либо вся транзакция отменяется, либо выполнение продолжается.</p>
    <p id="a9pC">Давайте рассмотрим простой контракт ретранслятора в качестве примера. Как показано ниже, контракт ретранслятора позволяет кому-либо совершать и подписывать транзакцию без необходимости ее выполнения. Часто это используется, когда пользователь не может оплатить газ, связанный с транзакцией.</p>
    <figure id="alsU" class="m_original">
      <img src="https://img1.teletype.in/files/c4/6e/c46efc86-88d1-424c-a94a-c95fd11a04be.png" width="1087" />
    </figure>
    <p id="s8aQ">Пользователь, который выполняет транзакцию, &quot;пересыльщик&quot;, может эффективно подвергать транзакции цензуре, используя ровно столько газа, чтобы транзакция выполнялась, но недостаточно газа для успешного выполнения подзапроса.</p>
    <p id="1IFX">Есть два способа, которыми это можно было бы предотвратить. Первым решением было бы разрешить ретрансляцию транзакций только доверенным пользователям. Другое решение состоит в том, чтобы потребовать, чтобы пересыльщик подавал достаточное количество газа, как показано ниже.</p>
    <figure id="eOTH" class="m_original">
      <img src="https://img4.teletype.in/files/75/35/7535ec38-80d5-468f-aa9b-51bd4d8b534b.png" width="841" />
    </figure>
  </section>
  <section style="background-color:hsl(hsl(323, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <h2 id="fqyC">Reentrancy</h2>
    <p id="dP9M"><strong>Reentrancy</strong> (повторный вход) - это атака, которая может произойти, когда ошибка в функции может позволить выполнять взаимодействие с функцией несколько раз, когда в противном случае это должно быть запрещено. Это может быть использовано для вывода средств из смарт-контракта. Фактически, повторный вход был вектором атаки, использованным при взломе DAO.</p>
    <h3 id="oZAt">Внутри функции</h3>
    <p id="owEz">Атака повторного входа происходит, когда уязвимая функция является той же самой функцией, которую злоумышленник пытается рекурсивно вызвать.</p>
    <figure id="2cWy" class="m_original">
      <img src="https://img3.teletype.in/files/ec/3c/ec3c99d9-247a-4f8b-aa4f-b7ee13905c1b.png" width="754" />
    </figure>
    <p id="f93j">Здесь мы можем видеть, что баланс изменяется только после того, как средства были переведены. Это может позволить хакеру вызывать функцию много раз, прежде чем баланс станет нулевым, эффективно истощая смарт-контракт.</p>
    <h3 id="KUu3">Между функциями</h3>
    <p id="sgTk">Более сложная версия того же процесса. Повторный вход между функциями происходит, когда уязвимая функция разделяет состояние с функцией, которую злоумышленник может использовать.</p>
    <figure id="BsJv" class="m_original">
      <img src="https://img1.teletype.in/files/c9/a0/c9a00113-ce19-4ff4-94ba-0e85beec3003.png" width="817" />
    </figure>
    <p id="Z9Nr">В этом примере хакер может воспользоваться этим контрактом, вызвав <strong>fallback</strong>-функцию <strong>transfer()</strong> для перевода потраченных средств до того, как баланс будет установлен в 0 в функции <strong>withdraw()</strong>.</p>
    <h3 id="dBuP">Предотвращение reentrancy</h3>
    <p id="MQ4I">При переводе средств в смарт-контракте используйте <strong>send</strong> или <strong>transfer</strong> вместо <strong>call</strong>. Проблема с использованием <strong>call</strong> заключается в том, что, в отличие от других функций, у нее нет ограничения по расходу газа в <strong>2300</strong>. Это означает, что <strong>call</strong> может использоваться во внешних вызовах функций, которые могут быть использованы для выполнения атак повторного входа.</p>
    <p id="9Q2o">Другим надежным методом предотвращения является пометка ненадежных функций.</p>
    <figure id="SkLj" class="m_original">
      <img src="https://img3.teletype.in/files/af/61/af61c2d4-2ca8-4dad-a070-8baa45b7880e.png" width="703" />
    </figure>
    <p id="qM0i">Кроме того, для оптимальной безопасности используйте шаблон <strong>checks-effects-interactions</strong>. Это простое эмпирическое правило для создания функций смарт-контракта.</p>
    <p id="Lcpg">Функция должна начинаться с проверок, например, операторов <strong>require</strong> и <strong>assert</strong>.</p>
    <p id="LFhS">Далее должны быть выполнены последствия контракта, т.е. внесены изменения в состояние.</p>
    <p id="b8R4">Наконец, мы можем выполнять взаимодействия с другими смарт-контрактами, например, вызовы внешних функций.</p>
    <p id="1uc6">Эта структура эффективна против повторного входа, поскольку измененное состояние контракта предотвратит выполнение злоумышленниками вредоносных взаимодействий.</p>
    <figure id="rPmx" class="m_original">
      <img src="https://img2.teletype.in/files/94/30/94307bd9-87fa-4b10-8e49-da2a72c68a77.png" width="686" />
    </figure>
    <p id="MBhi">Поскольку баланс устанавливается равным 0 перед выполнением каких-либо взаимодействий, если контракт вызывается рекурсивно, после первой транзакции отправлять нечего.</p>
  </section>
  <section style="background-color:hsl(hsl(170, 33%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <h2 id="50OU"><strong>Переполнение и недополнение целых чисел</strong></h2>
    <p id="SP8R">В Solidity целочисленные типы имеют максимальные значения. Например:</p>
    <p id="yey6"><strong>uint8</strong> =&gt;  <strong>255</strong> </p>
    <p id="kRAK"><strong>uint16</strong> =&gt; <strong>65535</strong> </p>
    <p id="fJQb"><strong>uint24</strong> =&gt; <strong>16777215 </strong></p>
    <p id="Upw5"><strong>uint256</strong> =&gt; <strong>(2^256) - 1</strong></p>
    <p id="GOnq">Ошибки переполнения и недополнения могут возникать, когда вы превышаете максимальное значение или когда вы опускаетесь ниже минимального значения. Когда вы превышаете максимальное значение, вы возвращаетесь к нулю, а когда вы опускаетесь ниже минимального значения, это возвращает вас к максимальному значению.</p>
    <p id="EYbQ">Поскольку меньшие целочисленные типы, такие как: <strong>uint8</strong>, <strong>uint16</strong> и т.д., имеют меньшие максимальные значения, переполнение может возникнуть очень быстро, поэтому их следует использовать с большей осторожностью.</p>
    <p id="x77d">В старых контрактах часто использовалась библиотека <strong>SafeMath</strong>, чтобы избежать переполнения / недополнения, но в solidity <strong>&gt;=v0.8.0</strong> по умолчанию встроена безопасная математическая логика.</p>
  </section>
  <section style="background-color:hsl(hsl(323, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <h2 id="bDLW">Зависимость от временной метки</h2>
    <p id="Sqm7">Временная метка блока, доступ к которому осуществляется через <strong>now</strong> или <strong>block.timestamp</strong> может манипулироваться майнером. Есть три соображения, которые вы должны принять во внимание при использовании временной метки для выполнения функции.</p>
    <h3 id="7ntc">Манипулирование временной меткой</h3>
    <p id="1Uj5">Если временная метка используется в попытке сгенерировать случайность, майнер может опубликовать временную метку в течение 15 секунд после проверки блока, что дает им возможность установить временную метку в качестве значения, которое увеличило бы их шансы извлечь выгоду из функции.</p>
    <p id="GlJj">Например, лотерейное приложение может использовать временную метку блока для выбора случайного участника торгов в группе. Майнер может принять участие в лотерее, а затем изменить временную метку на значение, которое дает ему лучшие шансы на выигрыш в лотерее.</p>
    <p id="SBew">Таким образом, временные метки не должны использоваться для создания случайности.</p>
    <h3 id="mzo7">Правило 15 секунд</h3>
    <p id="5qR5">Справочная спецификация Ethereum, <strong>Yellow Paper</strong>, не устанавливает ограничения на то, сколько блоков может измениться во времени, оно просто должно быть больше, чем временная метка его родительского элемента. При этом популярные реализации протокола отклоняют блоки с временными метками, превышающими 15 секунд, поэтому, пока ваше зависящее от времени событие может безопасно изменяться на 15 секунд, будет безопасным использовать временную метку блока.</p>
    <h3 id="ljoU">Не используйте block.number в качестве временной метки</h3>
    <p id="WvB9">Вы можете оценить разницу во времени между событиями, используя <strong>block.number</strong> и среднее время блокировки, но время блока может измениться и нарушить функциональность, поэтому лучше избегать этого использования.</p>
  </section>
  <section style="background-color:hsl(hsl(170, 33%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <h2 id="HRvT">Авторизация через tx.origin</h2>
    <p id="7VOQ"><strong>tx.origin</strong> — это глобальная переменная в Solidity, которая возвращает адрес, с которого была отправлена транзакция. Важно, чтобы вы никогда не использовали <strong>tx.origin </strong>для авторизации, поскольку другой контракт может использовать <strong>fallback</strong> функцию для вызова вашего контракта и получения авторизации, поскольку авторизованный адрес хранится в <strong>tx.origin</strong>. </p>
    <p id="CNPj">Рассмотрим этот пример:</p>
    <figure id="LXI9" class="m_original">
      <img src="https://img1.teletype.in/files/49/6d/496d77c4-4023-4351-9d4b-4a4855299284.png" width="1038" />
    </figure>
    <p id="Vcrp">Здесь мы можем видеть, что контракт <strong>TxUserWallet</strong> проводит функцию <strong>transferTo()</strong> с помощью <strong>tx.origin</strong>.</p>
    <figure id="ojig" class="m_original">
      <img src="https://img3.teletype.in/files/ee/72/ee72ce10-648b-44df-8d14-d56332c78519.png" width="1118" />
    </figure>
    <p id="03i7">Теперь, если кто-то обманом заставит вас отправить эфир на адрес контракта <strong>TxAttackWallet</strong>, они могут украсть ваши средства, проверив <strong>tx.origin</strong>, чтобы найти адрес, с которого была отправлена транзакция.</p>
    <p id="zD6F">Чтобы предотвратить такого рода атаки, используйте <strong>msg.sender</strong> для авторизации.</p>
  </section>
  <section style="background-color:hsl(hsl(323, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <h2 id="1If8">Плавающая pragma</h2>
    <p id="zbOf">Считается наилучшей практикой выбрать одну версию компилятора и придерживаться ее. С плавающей <strong>прагмой</strong> контракты могут случайно быть развернуты с использованием устаревшей или проблемной версии компилятора, что может привести к ошибкам, поставив под угрозу безопасность вашего смарт-контракта. Для проектов с открытым исходным кодом <strong>pragma</strong> также сообщает разработчикам, какую версию использовать, если они развернут ваш контракт. Выбранная версия компилятора должна быть тщательно протестирована и рассмотрена на предмет известных ошибок.</p>
    <p id="E7nQ">Исключение, в котором допустимо использовать плавающую <strong>прагму</strong>, относится к библиотекам и пакетам. В противном случае разработчикам пришлось бы вручную обновлять <strong>прагму</strong> для локальной компиляции.</p>
  </section>
  <section style="background-color:hsl(hsl(170, 33%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <h2 id="RJga">Видимость функции по умолчанию</h2>
    <p id="Ir3g">Видимость функции может быть задана как <strong>public</strong>, <strong>private</strong>, <strong>internal</strong> или <strong>external</strong>. Важно учитывать, какая видимость лучше всего подходит для функциональности вашего смарт-контракта.</p>
    <p id="vZlR">Многие атаки на смарт-контракты вызваны тем, что разработчик забывает или отказывается использовать модификатор видимости. Затем функция устанавливается как <strong>public</strong> по умолчанию, что может привести к непреднамеренным изменениям состояния.</p>
  </section>
  <section style="background-color:hsl(hsl(323, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <h2 id="nh0o">Устаревшая версия компилятора</h2>
    <p id="2Oow">Разработчики часто находят ошибки и уязвимости в существующем программном обеспечении и вносят исправления. По этой причине важно использовать самую последнюю из возможных версий компилятора. </p>
  </section>
  <section style="background-color:hsl(hsl(170, 33%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <h2 id="cpHG">Непроверенное возвращаемое значение вызова</h2>
    <p id="KRtX">Если возвращаемое значение вызова низкого уровня не проверено, выполнение может возобновиться, даже если вызов функции выдает ошибку. Это может привести к неожиданному поведению и нарушить логику программы. Неудачный вызов может быть даже вызван злоумышленником, который может в дальнейшем использовать приложение.</p>
    <p id="b5u8">В <strong>Solidity</strong> вы можете использовать вызовы низкого уровня, такие как: <strong>address.call()</strong>, <strong>address.callcode()</strong>, <strong>address.delegatecall()</strong> и <strong>address.send()</strong>; или вы можете использовать контрактные вызовы, такие как: <strong>ExternalContract.doSomething()</strong>. Вызовы низкого уровня никогда не будут выдавать исключение, вместо этого они вернут <strong>false</strong>, если столкнутся с исключением, в то время как вызовы контракта будут выбрасывать исключения автоматически.</p>
    <p id="HnDg">В случае, если вы используете вызовы низкого уровня, обязательно проверьте возвращаемое значение для обработки возможных неудачных вызовов.</p>
  </section>
  <section style="background-color:hsl(hsl(323, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <h2 id="dV0T">Незащищенный вывод эфира</h2>
    <p id="6c9C">Без надлежащего контроля доступа злоумышленники могут изъять часть или весь эфир из контракта. Это может быть вызвано неправильным именованием функции в конструкторе, что дает любому доступ к повторной инициализации контракта. </p>
    <p id="CFt1">Чтобы избежать этой уязвимости, разрешайте вывод средств только тем, кто авторизован, или по назначению, и назовите свой конструктор соответствующим образом.</p>
  </section>
  <section style="background-color:hsl(hsl(170, 33%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <h2 id="NjSD">Незащищенная инструкция Selfdestruct</h2>
    <p id="YxHm">В контрактах, которые имеют метод <strong>Selfdestruct</strong>, при отсутствии или недостаточном контроле доступа злоумышленники могут самоуничтожить контракт. Важно рассмотреть, является ли функция самоуничтожения абсолютно необходимой. Если это необходимо, рассмотрите возможность использования авторизации с несколькими подписями для предотвращения атаки.</p>
    <p id="17YF">Эта атака была использована в <a href="https://www.parity.io/blog/a-postmortem-on-the-parity-multi-sig-library-self-destruct/" target="_blank">атаке на Parity</a>. Анонимный пользователь обнаружил и воспользовался уязвимостью в смарт-контракте <strong>library</strong>, сделав себя владельцем контракта. Затем злоумышленник приступил к самоуничтожению контракта. Это привело к блокировке средств на 587 уникальных кошельках, на которых в общей сложности было 513 774,16 эфира.</p>
  </section>
  <section style="background-color:hsl(hsl(323, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <h2 id="rY0A">Видимость переменной состояния по умолчанию</h2>
    <p id="NF4l">Разработчикам свойственно явно объявлять видимость функции, но не так часто объявлять видимость переменной. Переменные состояния могут иметь один из трех идентификаторов видимости: <strong>public</strong>, <strong>internal</strong> или <strong>private</strong>. К счастью, видимость переменных по умолчанию <strong>internal</strong>, а не<strong> public</strong>, но даже если вы намереваетесь объявить переменную как <strong>internal</strong>, важно, чтобы не было неправильных предположений относительно того, кто может получить доступ к переменной.</p>
  </section>
  <section style="background-color:hsl(hsl(170, 33%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <h2 id="j5E4">Неинициализированный указатель хранилища</h2>
    <p id="KqLs">Данные хранятся в EVM в виде <strong>storage</strong>, <strong>memory </strong>или<strong> calldata</strong>. Важно, чтобы эти параметры были хорошо поняты и правильно инициализированы. Неправильная инициализация указателей на хранилище данных или простое оставление их неинициализированными может привести к уязвимостям контрактов.</p>
    <p id="0a0b">Начиная с Solidity <strong>0.5.0</strong>, неинициализированные указатели хранилища больше не являются проблемой, поскольку контракты с неинициализированными указателями хранилища больше не будут компилироваться. При этом по-прежнему важно понимать, какие указатели хранилища вам следует использовать в определенных ситуациях.</p>
  </section>
  <section style="background-color:hsl(hsl(323, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <h2 id="9OGw"><strong>Нарушение assert</strong></h2>
    <p id="itDD">В Solidity <strong>0.4.10</strong> появились следующие функции: <strong>assert()</strong>, <strong>require()</strong> и <strong>revert()</strong>. Мы обсудим функцию <strong>assert</strong> и то, как ее использовать.</p>
    <p id="aG9L">Формально сказано, что функция <strong>assert() </strong>предназначена для утверждения инвариантов; неофициально сказано, что <strong>assert()</strong> - это чрезмерно напористый телохранитель, который защищает ваш контракт, но при этом крадет ваш газ. Должным образом функционирующие контракты никогда не должны приводить к сбою утверждения. Если вы достигли неудачного утверждения <strong>assert</strong>, вы либо неправильно использовали <strong>assert()</strong>, либо в вашем контракте есть ошибка, которая переводит его в недопустимое состояние.</p>
    <p id="VDXV">Если условие, проверенное в <strong>assert()</strong>, на самом деле не является инвариантом, рекомендуется заменить его инструкцией<strong> require()</strong>.</p>
  </section>
  <section style="background-color:hsl(hsl(170, 33%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <h2 id="adfi">Использование устаревших функций</h2>
    <p id="8f24">С течением времени функции в Solidity устаревают и часто заменяются более совершенными функциями. Важно не использовать устаревшие функции, так как это может привести к неожиданным эффектам и ошибкам компиляции.</p>
    <p id="eHcI">Вот список устаревших функций и альтернатив. Многие альтернативы являются просто псевдонимами и не нарушат текущее поведение, если будут использоваться в качестве замены своего устаревшего аналога.</p>
    <figure id="OgJW" class="m_original">
      <img src="https://img2.teletype.in/files/d4/86/d48663ab-7ac7-42a0-8017-4b733dfbee1e.png" width="505" />
    </figure>
  </section>
  <section style="background-color:hsl(hsl(323, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <h2 id="iW3c">delegateCall ненадежному вызываемому абоненту</h2>
    <p id="Hmap"><strong>delegateCall</strong> — это особый вариант вызова с сообщением. Это почти идентично обычному вызову сообщения, за исключением того, что целевой адрес выполняется в контексте контракта вызова, а <strong>msg.sender</strong> и <strong>msg.value </strong>остаются неизменными. По сути, <strong>delegateCall</strong> делегирует другие контракты для изменения хранилища вызывающего контракта.</p>
    <p id="tYfG">Поскольку <strong>delegateCall</strong> дает такой большой контроль над контрактом, очень важно использовать это только с доверенными контрактами, такими как ваш собственный. Если целевой адрес получен из пользовательского ввода, обязательно убедитесь, что это доверенный контракт.</p>
  </section>
  <section style="background-color:hsl(hsl(170, 33%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <h2 id="PaPn">Податливость подписи</h2>
    <p id="Hruw">Часто люди предполагают, что использование системы криптографических подписей в смарт-контрактах подтверждает уникальность подписей, однако это не так. Подписи в Ethereum могут быть изменены без использования закрытого ключа и оставаться действительными. </p>
    <p id="XCWR">Например, криптография с эллиптическим ключом состоит из трех переменных: <strong>v</strong>, <strong>r</strong> и<strong> s</strong>, и если эти значения изменены правильным образом, вы можете получить действительную подпись с неправильным закрытым ключом.</p>
    <p id="qeMQ">Чтобы избежать проблемы податливости подписи, никогда не используйте подпись в хэше подписанного сообщения для проверки того, были ли ранее подписанные сообщения обработаны контрактом, поскольку злоумышленники могут найти вашу подпись и воссоздать ее.</p>
  </section>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@jetix37/understanding-byte-code-p4</guid><link>https://teletype.in/@jetix37/understanding-byte-code-p4?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=jetix37</link><comments>https://teletype.in/@jetix37/understanding-byte-code-p4?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=jetix37#comments</comments><dc:creator>jetix37</dc:creator><title>Понимаем байт-код EVM: Часть 4</title><pubDate>Thu, 02 Mar 2023 18:41:34 GMT</pubDate><media:content medium="image" url="https://img3.teletype.in/files/20/d3/20d3cb7f-fd5b-4711-9290-84f9308f62ad.png"></media:content><category>Ethereum</category><description><![CDATA[<img src="http://ignis.anime-sharing.com/vault/files/67047_hykw9/%5BFFF%5D%20Golden%20Time%20-%2001%20%5BBD%5D%5B1080p-FLAC%5D%5B0A94EDB8%5D.mkv_snapshot_17.06_%5B2014.03.06_07.58.48%5D.png"></img>Оригинал — https://blog.trustlook.com/understand-evm-bytecode-part-4/]]></description><content:encoded><![CDATA[
  <section style="background-color:hsl(hsl(323, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="bUKh">Оригинал — <a href="https://blog.trustlook.com/understand-evm-bytecode-part-4/" target="_blank">https://blog.trustlook.com/understand-evm-bytecode-part-4/</a></p>
    <p id="Gg8z">Канал — <a href="https://t.me/jetix37eth" target="_blank">https://t.me/jetix37eth</a></p>
  </section>
  <figure id="sTf3" class="m_original">
    <img src="http://ignis.anime-sharing.com/vault/files/67047_hykw9/%5BFFF%5D%20Golden%20Time%20-%2001%20%5BBD%5D%5B1080p-FLAC%5D%5B0A94EDB8%5D.mkv_snapshot_17.06_%5B2014.03.06_07.58.48%5D.png" width="1920" />
  </figure>
  <p id="pFcZ">В прошлых частях мы говорили о том, как различные типы данных Solidity реализуются в хранилище. Сегодня мы поговорим о памяти более подробно, о ее использовании во внешних вызовах.</p>
  <p id="PGl9">Мы узнали некоторые основы о памяти из предыдущих разделов. Мы знаем, что память предназначена для вычисления хэша или взаимодействия с внешними вызовами или возвратами. Структура памяти зарезервировала <strong>0x0 </strong>и <strong>0x20 </strong>для вычисления хэша. По адресу <strong>0x40 </strong>он сохранит указатель свободной памяти для использования в будущем. </p>
  <p id="ZRH6">Когда необходимо выделить некоторую память, указатель может быть соответствующим образом скорректирован, чтобы выделенная память больше не посещалась повторно. Кроме того, память доступна только во время выполнения контракта. Как только выполнение завершено, его содержимое сбрасывается. По сравнению с хранилищем, это больше похоже на оперативную память в компьютере.</p>
  <hr />
  <p id="R9SF">Давайте посмотрим на все коды операций, которые зависят от памяти, кроме <strong>MLOAD </strong>и <strong>MSTORE</strong>:</p>
  <figure id="Aci5" class="m_original">
    <img src="https://img4.teletype.in/files/f6/49/f6492834-2fd3-4795-bf9b-4351fb718e90.png" width="854" />
  </figure>
  <p id="KZVE">Мы можем видеть, что помимо того, что <strong>SHA3</strong> используется для вычисления хэша, большинство остальных кодов операций связаны с взаимодействиями с EVM. Это включает в себя загрузку данных из полезной нагрузки EVM, кода или упорядочивание данных для внешних вызовов и возвратов.</p>
  <p id="cFRx">Сначала давайте посмотрим на код операции <strong>CALLDATACOPY</strong>. В документации Solidity “<strong>calldatacopy(t, f, s)</strong>” определяется как “копировать <strong>s</strong> байт из <strong>calldata </strong>в позиции <strong>f</strong> в <strong>mem </strong>в позиции <strong>t</strong>”. Если у вас есть опыт анализа контрактов на уровне байт-кода, вы можете заметить, что другой аналогичный код операции <strong>CALLDATALOAD </strong>более популярен, чем этот. </p>
  <p id="OHmk">Но разница между этими 2 кодами операций заключается в том, что <strong>CALLDATALOAD </strong>загружает только 32-байтовые данные в стек вместо памяти. Если публичная функция контракта использует только целые числа в своих аргументах, то <strong>CALLDATALOAD </strong>достаточно хорош для вызовов. Формат полезной нагрузки данных будет следующим:</p>
  <figure id="5SVc" class="m_original">
    <img src="https://img1.teletype.in/files/c9/d3/c9d331cc-3515-48a8-a2fa-0bcd99c599d6.png" width="856" />
  </figure>
  <hr />
  <p id="QCTI">Однако есть и исключения. Функции могут поддерживать больше типов данных в Solidity, отличных от чистых целых чисел. Пример функции со структурой или массивами фиксированного размера:</p>
  <figure id="vZKL" class="m_original">
    <img src="https://img1.teletype.in/files/c0/d4/c0d4e0f0-bd19-4fdc-8636-e2d53cadccc0.png" width="977" />
  </figure>
  <p id="eEqN">Дешифрованный байт-код выглядит так:</p>
  <p id="AQ4O"><strong>temp0 = mload(0x40); </strong></p>
  <p id="fD3z"><strong>mstore(0x40,(0x40 + temp0)); </strong></p>
  <p id="ho1f"><strong>calldatacopy(temp0,0x4,0x40);</strong></p>
  <p id="R1mo">По-видимому, на этот раз <strong>CALLDATACOPY </strong>используется для копирования всего аргумента в память для дальнейшего использования. Это не просто фиксированный размер массивов. Для любого параметра, который имеет фиксированный размер, например struct, <strong>CALLDATACOPY</strong> будет тем, кто выполнит эту работу.</p>
  <hr />
  <p id="4iZ8">Однако все может быть еще сложнее, когда есть динамические массивы. Например:</p>
  <figure id="jObE" class="m_original">
    <img src="https://img1.teletype.in/files/c4/6f/c46fb20b-c4d4-4ebe-8153-006fae1a78cc.png" width="973" />
  </figure>
  <p id="w3hl">Мы можем видеть, что существует только одна функция <strong>test()</strong>, использующая массив адресов в качестве аргумента. Итак, как данные будут расположены внутри полезной нагрузки данных? </p>
  <p id="aTJu">Давайте все-таки заглянем в байт-код в поисках истины. Вот фрагмент кода перед вызовом функции <strong>test()</strong>:</p>
  <p id="5hlY"><strong>temp0 = mload(0x40); </strong></p>
  <p id="iayo"><strong>temp1 = msg.data(0x4);</strong></p>
  <p id="QjaA"><strong>mstore(0x40,(0x20 + (temp0 + (msg.data((0x4 + temp1)) * 0x20)))); </strong></p>
  <p id="sj4h"><strong>mstore(temp0,msg.data((0x4 + temp1))); </strong></p>
  <p id="sP8e"><strong>calldatacopy((temp0 + 0x20),(0x24 + temp1),(msg.data((0x4 + temp1)) * 0x20)); </strong></p>
  <p id="yU6b"><strong>var1 = test(temp0);</strong></p>
  <p id="nGtT">Возможно, не так просто понять логику структуры полезной нагрузки данных. Но не волнуйся. Давай пройдем через это вместе. </p>
  <p id="q2i7">Первая строка <strong>“temp0 = mload(0x40);”</strong> очень популярна. Она получает указатель свободной памяти из адреса памяти <strong>0x40 </strong>в переменную <strong>temp0</strong>. Затем <strong>temp1 </strong>будет присвоено значение, полученное из полезной нагрузки данных со смещением <strong>0x4</strong>, которое регулярно содержит первый параметр, когда тип является <strong>integer</strong>. Однако, по-видимому, в данном случае это еще не закончено. Это значение в <strong>temp1 </strong>будет использоваться в качестве смещения для определения местоположения данных, начиная с <strong>0x4</strong>. </p>
  <p id="d8BX">Это значение может быть показано как <strong>“msg.data((0x4 + temp1))”</strong>. Из приведенного выше фрагмента кода это значение представляет собой длину массива. Размер каждого элемента в массиве равен <strong>0x20</strong>. Таким образом, <strong>“mstore(0x40,(0x20 + (temp0 + (msg.data((0x4 + temp1)) * 0x20))));”</strong> изменит указатель свободной памяти, чтобы сохранить часть памяти для этого аргумента. Затем длина массива будет скопирована в старый указатель свободной памяти, и элементы массива также будут скопированы <strong>CALLDATACOPY</strong>. Наконец, указатель памяти будет передан функции <strong>test()</strong> для работы. </p>
  <p id="Kp7H">После того, как у есть некоторое базовое представление о том, как была организована полезная нагрузка данных EVM, мы сможем увидеть, как работают коды операций, связанные с <strong>CALL</strong>, и как задействована память. В документации Solidity указано: “<strong>call (g, a, v, in, insize, out, outsize)</strong> – вызов контракта по адресу <strong>a</strong> с входной памятью<strong>[in..(in+insize))</strong>, обеспечивающий подачу газа <strong>g</strong> и <strong>v</strong> wei, а также память выходной области<strong>[out..(out+outsize))</strong>, возвращающий <strong>0</strong> при ошибке (например. кончился газ) и <strong>1</strong> на успех”. </p>
  <hr />
  <p id="K9n5">Поэтому, когда вы используете этот код операции для вызова другого смарт-контракта, вам необходимо предоставить всю информацию. Давайте посмотрим на реальный пример ссылки на внешний контракт:</p>
  <figure id="hRXz" class="m_original">
    <img src="https://img2.teletype.in/files/d2/d9/d2d97e71-80c0-42fd-921c-ffc174816448.png" width="881" />
  </figure>
  <p id="VlFv">Здесь мы определили 2 смарт-контракта. Функция getA() в контракте <strong>Exisitng </strong>вызовет внешнюю функцию <strong>a()</strong> в <strong>Deployed</strong>. Вот скомпилированный байт-код:</p>
  <figure id="ETR1" class="m_original">
    <img src="https://img1.teletype.in/files/42/20/42201e8a-3141-491f-b1dd-1b3b637d7eff.png" width="856" />
  </figure>
  <p id="fr0u">Как всегда, указатель свободной памяти был загружен в переменную <strong>temp0</strong>. Затем хэш функции <strong>0xDBE671F </strong>сохраняется в свободной памяти. Тогда <strong>var11 </strong>будет содержать первую переменную хранилища, которая в данном случае является <strong>dc</strong>. Строка <strong>require </strong>проверит, содержит ли адрес в <strong>var11 </strong>адрес контракта. </p>
  <p id="Pc9X">Наконец, этот адрес будет использоваться для выполнения внешнего вызова <strong>“var11.gas(gasleft).value(0).call(temp0,4,temp0,0x20)”</strong>. Параметры <strong>(temp0 и 4)</strong> в этом вызове — это полезная нагрузка данных, которую он хочет отправить внешнему контракту. В этом случае он отправляет 4 байта с адреса <strong>temp0</strong>. Из раннего кода мы знаем, что 4 байта - это хэш-значение подписи функции для <strong>a()</strong>. Поскольку в функции нет других аргументов, для выполнения этого вызова требуется всего 4 байта хэша функции. Если функция, которую вы хотите вызвать, действительно имеет аргументы, то компилятор расположит память так, как мы обсуждали ранее для вызова. </p>
  <p id="56du">Параметры <strong>(temp0 и 0x20)</strong> будут содержать возвращаемые данные из внешнего вызова. EVM получит данные, возвращенные из внешнего вызова, и поместит их в указанный вызов кода операции адреса памяти.</p>
  <hr />
  <p id="Btb4">Есть одна вещь, которую стоит упомянуть о внешнем вызове — это то, что в Solidity есть некоторый жестко закодированный адрес для встроенной функции. Если вы посмотрите на какой-нибудь байт-код, вы всегда найдете некоторые внешние вызовы, использующие адреса 1,2,3 и 4. По-видимому, это не обычные адреса смарт-контрактов. Я поискал в Интернете, и не так много информации о них можно найти. Но я нахожу некоторую подсказку в документации Solidity:</p>
  <p id="7JJZ"><em>Выражения, которые могут иметь побочный эффект на распределение памяти, разрешены, но те, которые могут иметь побочный эффект на другие объекты памяти, - нет. Разрешены встроенные функции <strong>keccak256</strong>, <strong>sha256</strong>, <strong>ripemd160</strong>, <strong>ecrecover</strong>, <strong>addmod </strong>и <strong>mulmode </strong>(даже если они вызывают внешние контракты).</em></p>
  <p id="e1uH">Посмотрите, что написано в скобках. Они вызывают внешние контракты для этих встроенных функций. Я просто написал смарт-контракт со всеми этими функциями внутри, затем я нашел сопоставление для жестко закодированных адресов:</p>
  <p id="BX3D"><strong>1 - ecrecover </strong></p>
  <p id="uSSE"><strong>2 - sha256 </strong></p>
  <p id="hPRu"><strong>3 - ripemd160 </strong></p>
  <p id="7Pym"><strong>4 - sha3</strong></p>
  <p id="5KkM">Для последней версии компилятора Solidity <strong>SHA3 </strong>имеет свой собственный код операции, поэтому для этого вычисления не требуется внешнего вызова. Но вы все еще можете видеть, что некоторые смарт-контракты используют <strong>0x4 </strong>для отправки внешнего вызова для этой функции.</p>
  <p id="XB5x">До сих пор мы обсуждали, какую важную роль играет память EVM, особенно при выполнении внешних вызовов к другим смарт-контрактам. Это будет последняя статья в этой серии. Мы рассмотрели большинство вещей, с которыми вы можете столкнуться, когда захотите проанализировать байт-код виртуальной машины. Надеюсь, это поможет вам немного понять, как это работает.</p>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@jetix37/understanding-byte-code-p3</guid><link>https://teletype.in/@jetix37/understanding-byte-code-p3?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=jetix37</link><comments>https://teletype.in/@jetix37/understanding-byte-code-p3?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=jetix37#comments</comments><dc:creator>jetix37</dc:creator><title>Понимаем байт-код EVM: Часть 3</title><pubDate>Wed, 01 Mar 2023 18:15:11 GMT</pubDate><media:content medium="image" url="https://img2.teletype.in/files/53/1c/531ca9da-5bde-4750-9697-513e283d484a.png"></media:content><category>Ethereum</category><description><![CDATA[<img src="https://img2.teletype.in/files/db/ff/dbff77c7-7741-4491-b091-704d2342b3aa.jpeg"></img>Оригинал — https://blog.trustlook.com/understand-evm-bytecode-part-3/]]></description><content:encoded><![CDATA[
  <section style="background-color:hsl(hsl(323, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="6RZ6">Оригинал — <a href="https://blog.trustlook.com/understand-evm-bytecode-part-3/" target="_blank">https://blog.trustlook.com/understand-evm-bytecode-part-3/</a></p>
    <p id="2Cj3">Канал — <a href="https://t.me/jetix37eth" target="_blank">https://t.me/jetix37eth</a></p>
  </section>
  <figure id="8228" class="m_original">
    <img src="https://img2.teletype.in/files/db/ff/dbff77c7-7741-4491-b091-704d2342b3aa.jpeg" width="1920" />
  </figure>
  <p id="yAQ5">В предыдущих частях мы говорили о байт-коде виртуальной машины для создания и выполнения. Мы видели, что переменные стека обычно используются для предоставления операндов кодам операций или передачи целочисленных аргументов между вызовами внутренних функций. Все переменные, связанные с состоянием, должны быть сохранены в хранилище. </p>
  <p id="vxWG">Однако хранилище спроектировано в виде словаря или хэш-таблицы. Оно будет содержать все данные по парам ключ-значение. Для каждого адресного ключа он может хранить 32-байтовое целое число. Итак, как он может реализовать сложные структуры данных, <strong>struct</strong>, <strong>mapping</strong>, массивы переменной длины и т.д.? Давайте подробнее разберемся в их реализации.</p>
  <hr />
  <p id="ZNpy">Давайте начнем с относительно простого: типы данных фиксированного размера. Мы уже знаем, что EVM поддерживает целые числа разной длины, от 1 байта до 32 байт. Если вы определили только несколько 32-байтовых целочисленных переменных в своих смарт-контрактах, компилятор просто назначит переменным последовательность адресов, начинающихся с <strong>0x0</strong>. Например:</p>
  <figure id="cjG4" class="m_original">
    <img src="https://img4.teletype.in/files/7d/d6/7dd6f89f-52e6-4d85-b2dd-da433c5ca66f.png" width="451" />
  </figure>
  <p id="n5Dt">Если вы скомпилировали приведенный выше код и проверили байт-код EVM, вы обнаружите, что весь код, считывающий или записывающий 3 переменные <strong>balance1</strong>, <strong>balance2 </strong>и <strong>balance3</strong>, будет сопоставляться операциям <strong>SLOAD </strong>или <strong>SSTORE </strong>по адресам <strong>0x0</strong>, <strong>0x1 </strong>и <strong>0x2 </strong>соответственно. </p>
  <p id="Hy6V">Однако все мы знаем, что использование хранилища обходится дорого. Если вы понятия не имеете, что такое газ в EVM, я рекомендую вам провести небольшое исследование по этому вопросу. Есть тонны статей, в которых говорится об этом. По сути, это стоимость выполнения вашего кода. </p>
  <p id="fhUl">Таким образом, чтобы оптимизировать затраты на газ при использовании хранилища, когда у вас есть несколько целых чисел небольшой длины, они будут оптимизированы для использования 1 слота хранения. Например:</p>
  <figure id="32vL" class="m_original">
    <img src="https://img3.teletype.in/files/a1/2b/a12ba6de-1075-4011-9721-70540f015845.png" width="468" />
  </figure>
  <p id="e3Zu">Когда у вас есть две переменные, определенные как <strong>uint128</strong>, они могут быть помещены в один слот памяти по адресу <strong>0x0</strong>, а третьей переменной <strong>balance3 </strong>будет присвоен собственный адрес <strong>0x1</strong>. Когда вы ссылаетесь на переменную <strong>balance1</strong>, первое 16-байтовое значение будет извлечено после <strong>SLOAD </strong>или <strong>SSTORE</strong>. Давайте посмотрим на какой-нибудь реальный пример этого:</p>
  <figure id="Ixou" class="m_original">
    <img src="https://img2.teletype.in/files/9e/26/9e26defa-e5dd-48cb-8c69-a77e90a69f1b.png" width="654" />
  </figure>
  <p id="bglQ">Байт-код:</p>
  <p id="oDpr">6080604052600436106100615763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166340441eec81146100665780634f2be91f146100a0578063c45c4f58146100c7578063f24a0faa146100dc575b600080fd5b34801561007257600080fd5b5061007b6100f1565b604080516fffffffffffffffffffffffffffffffff9092168252519081900360200190f35b3480156100ac57600080fd5b506100b561011d565b60408051918252519081900360200190f35b3480156100d357600080fd5b5061007b610158565b3480156100e857600080fd5b506100b5610170565b60005470010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff1681565b6000546fffffffffffffffffffffffffffffffff80821670010000000000000000000000000000000090920481169190910116600181905590565b6000546fffffffffffffffffffffffffffffffff1681565b600154815600a165627a7a72305820fa0e623e455a9cc0439ea393dff5b50cc571150034eb57658dd9718e3982b1590029</p>
  <hr />
  <p id="Wkl7">По соображениям экономии времени мы не будем рассматривать их все, давайте просто посмотрим на строку вычисления <strong>add </strong>внутри функции <strong>add()</strong>:</p>
  <figure id="qKA0" class="m_original">
    <img src="https://img2.teletype.in/files/1f/a8/1fa86e27-dc5f-47f0-9b76-5da59aadffb6.png" width="849" />
  </figure>
  <p id="TGP3">Эквивалентный код:</p>
  <p id="owCY"><strong>sstore(0x1,uint128((uint128((sload(0x0) / 0x100000000000000000000000000000000)) + uint128(sload(0x0)))));</strong></p>
  <p id="EqcH">Логика использования слотов хранения <strong>0x0 </strong>и <strong>0x1 </strong>четко показана в приведенной выше строке, которая является строкой <strong>balance3</strong> = <strong>balance1 </strong>+ <strong>balance2</strong>;</p>
  <p id="xesq">Мы можем видеть, что одна строка кода Solidity может привести к множеству инструкций В будущем мы обсудим еще более сложные структуры данных, это будет ошеломляюще, если мы посмотрим на все коды операций. </p>
  <p id="tDGh">Поэтому, чтобы сделать наш контент более читабельным, я просто покажу эквивалентный код на ассемблере, чтобы доказать логику. Тем не менее, вы всегда можете потратить больше времени на изучение байт-кода один за другим, чтобы попрактиковаться.</p>
  <hr />
  <p id="vulY">Мы говорили о самом базовом типе данных, <strong>Integer</strong>, в EVM. Теперь давайте посмотрим на <strong>struct </strong>в Solidity:</p>
  <figure id="OdWt" class="m_original">
    <img src="https://img3.teletype.in/files/a6/64/a6642d97-da61-47e9-add4-948d1faf0e03.png" width="1099" />
  </figure>
  <p id="JOzB">Эквивалентный код ассемблера для функции <strong>deposit()</strong> выглядит так:</p>
  <p id="ccjo"><strong>sstore(0x0,uint160(arg0)); </strong></p>
  <p id="bm11"><strong>sstore(0x1,arg1);</strong></p>
  <p id="pFfg">Мы можем видеть, что даже если мы определяем <strong>struct Funder</strong> в нашем контракте, компилятор все равно генерирует код как просто 2 обычные переменные хранилища. Кроме того, если несколько элементов-членов внутри структуры могут поместиться в один слот для хранения, оптимизация также произойдет.</p>
  <hr />
  <p id="bNe5">Теперь давайте поговорим об очень популярном типе данных — <strong>mapping</strong>.</p>
  <figure id="ckFS" class="m_original">
    <img src="https://img4.teletype.in/files/7f/a4/7fa4b4c4-abb2-4b12-8480-0bd0d5a4139b.png" width="1192" />
  </figure>
  <p id="Rhr3">Если вы знакомы с разработкой смарт-контрактов, вы, вероятно, увидите, что многие приложения аналогично используют отображение для записи баланса учетной записи. Давайте скомпилируем код и проверим ассемблерный код переменной <strong>balanceOf</strong>:</p>
  <p id="6WMU"><strong>mstore(0x20,0x0); </strong></p>
  <p id="HGjv"><strong>mstore(0x0,msg.data(0x04) &amp; 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); </strong></p>
  <p id="Tr4C"><strong>temp0 = keccak256(0x0,0x40); </strong></p>
  <p id="bKPk"><strong>return(sload(temp0));</strong></p>
  <p id="S4X3">Мы можем увидеть <strong>msg.data(0х04) &amp; 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFffffffffff </strong>получит параметр из данных полезной нагрузки в качестве значения адреса. Затем этот аргумент будет помещен в адрес памяти <strong>0x0</strong>. Кроме того, для адреса <strong>0x20</strong> в памяти задано значение <strong>0x0</strong>. Этот <strong>0x0</strong> на самом деле является индексом объявления переменных хранилища. Это только потому, что <strong>balanceOf</strong> является первой объявленной переменной в смарт-контакте. </p>
  <p id="BEbZ">После настройки памяти вызывается код операции <strong>SHA3 </strong>для вычисления хэш-значения входных данных, и результаты будут использоваться в качестве ключа хранения для <strong>mapping</strong>-переменной. Из приведенного выше кода мы можем видеть, что когда вы объявляете <strong>mapping</strong>-переменную одного уровня, компилятор фактически принимает ее как функцию с одним аргументом и возвращает одно значение. По соображениям удобочитаемости давайте использовать <strong>arg0 </strong>для значения, установленного в адресе <strong>0x0 </strong>в памяти вычисления сопоставления. </p>
  <hr />
  <p id="ENWT">Давайте взглянем на двухуровневый <strong>mapping</strong>:</p>
  <p id="WXbl"><strong>mapping (address =&gt; mapping (address =&gt; uint256)) public tokens;</strong></p>
  <p id="LfEj">Возможно, вы видели некоторые похожие <strong>mapping</strong>-переменные, подобные приведенным выше. Когда вы скомпилируете код и сгенерируете байт-код, его ассемблерный код будет выглядеть следующим образом:</p>
  <p id="C2US"><strong>mstore(0x20,0x0); </strong></p>
  <p id="cVab"><strong>mstore(0x0,arg0); </strong></p>
  <p id="ZlBG"><strong>temp0 = keccak256(0x0,0x40); </strong></p>
  <p id="PuKd"><strong>mstore(0x20,temp0); </strong></p>
  <p id="kTst"><strong>mstore(0x0,arg1); </strong></p>
  <p id="0sXO"><strong>temp1 = keccak256(0x0,0x40); </strong></p>
  <p id="tX2Y"><strong>return(sload(temp1));</strong></p>
  <p id="znGH">По-видимому, для двухуровневой <strong>mapping</strong>-переменной это фактически функция с двумя аргументами и возвращающая одно значение. Для этой <strong>tokens</strong> он сначала настроил память с <strong>0x0</strong> по адресу <strong>0x20</strong> (мы знаем, что <strong>0x0 </strong>- это индекс объявления переменной), <strong>arg0 </strong>по адресу <strong>0x0</strong>. Затем первое <strong>HASH</strong>-значение было вычислено с использованием кода операции <strong>SHA3</strong>. </p>
  <p id="7M5x">Однако это вычисленное значение снова будет помещено в адрес памяти <strong>0x20</strong>, а второй аргумент <strong>arg1 </strong>на этот раз будет помещен по адресу <strong>0x0 </strong>для другого вычисления <strong>SHA3</strong>. Затем результат будет использоваться в качестве ключа хранения для переменной. В той же логике вы можете продолжать добавлять уровни для ваших <strong>mapping</strong>-переменных, и ключом для хранилища всегда будет последний вычисленный результат <strong>SHA3</strong>.</p>
  <hr />
  <p id="oME7">До сих пор вы, возможно, лучше понимали, почему указатель на свободную память всегда сохраняется в <strong>0x40</strong>, а не в <strong>0x0 </strong>или <strong>0x20</strong>. Это происходит только потому, что эти адреса зарезервированы для вычисления хэша. </p>
  <p id="0ocr">Ранее мы изучили несколько структур данных в Solidity. Что, если мы соберем некоторые из них вместе? Например, что делать, если у нас есть <strong>mapping-переменная</strong>, которая возвращает значение struct:</p>
  <figure id="0IdY" class="m_original">
    <img src="https://img1.teletype.in/files/ce/e0/cee00903-59a4-4930-9262-9da25ff74bde.png" width="777" />
  </figure>
  <p id="rIiA">Давайте взглянем на декомпилированный код для <strong>balanceOf</strong>:</p>
  <p id="PUBZ"><strong>function balanceOf( address arg0) public return (var0,var1) { </strong></p>
  <p id="IFjA"><strong>mstore(0x20,0x0);</strong></p>
  <p id="RSVV"><strong>mstore(0x0,arg0); </strong></p>
  <p id="VAmn"><strong>temp0 = keccak256(0x0,0x40); </strong></p>
  <p id="XYRA"><strong>return(sload(temp0),sload((temp0 + 0x1))); </strong></p>
  <p id="SYUT"><strong>}</strong></p>
  <p id="cOAk">Мы можем видеть, что вычислительная часть <strong>SHA3</strong> такая же, как и обычная одноуровневая <strong>mapping</strong>-переменная. Единственное отличие заключается в том, что обычный тип данных, такой как <strong>integer</strong>, будет просто использовать результат хэша в качестве ключа хранения. Но элемент-член внутри структуры применит индекс объявления элемента к этому хэш-значению для ключа хранения. </p>
  <p id="uqx9">Таким образом, в этом случае два элемента-члена в <strong>struct Funder</strong> добавят <strong>0x0 </strong>и <strong>0x01 </strong>к хэш-значению <strong>temp0</strong> для ключа хранения.</p>
  <hr />
  <p id="wdik">Далее мы рассмотрим структуры данных с переменной длиной. Например, у нас есть вот такой смарт-контракт:</p>
  <figure id="LXSn" class="m_original">
    <img src="https://img1.teletype.in/files/86/e7/86e7f258-6016-4cf2-ad83-8d1b1e40e53f.png" width="548" />
  </figure>
  <p id="EzhY">Мы определили массив переменной длины <strong>senders</strong>. Давайте посмотрим, как EVM реализует эту переменную в хранилище:</p>
  <p id="i7VV"><strong>function senders( uint256 arg0) public return (var0) { </strong></p>
  <p id="exI9"><strong>assert((arg0 &lt; sload(0x0))); </strong></p>
  <p id="7f7J"><strong>mstore(0x0,0x0); </strong></p>
  <p id="UX80"><strong>temp0 = keccak256(0x0,0x20); </strong></p>
  <p id="9TAZ"><strong>return(uint160(sload((temp0 + arg0)))); </strong></p>
  <p id="oj6j"><strong>}</strong></p>
  <p id="am2l">Из приведенного выше ассемблерного кода мы можем заметить, что переменная реализована как функция с одним аргументом <strong>index</strong>, возвращающаяэлемент из массива. В Solidity мы определили только одну переменную массива <strong>senders</strong>. </p>
  <p id="7UgD">Как мы обсуждали ранее, хранилище назначит индекс адреса для каждой переменной, а поскольку существует только одна, ей присваивается <strong>0x0</strong>. Однако адреса <strong>0x0</strong> недостаточно для хранения всех данных массива. Вместо этого он будет содержать длину массива. </p>
  <p id="vCiw">Для данных массива хранилище использует значение <strong>SHA3 </strong>своего адресного индекса (в данном случае <strong>0x0</strong>) в качестве начала данных массива. Индекс массива будет добавлен к этому хэш-значению в качестве ключа хранения для соответствующего элемента. </p>
  <p id="Wna6">Итак, давайте вернемся к приведенному выше фрагменту кода. Когда внешние вызыватели хотят получить доступ к элементу в массиве по индексу, сначала проверяется, превышает ли индекс длину массива. Если нет, то будет вычислено значение хэша, и индекс будет добавлен к этому хэшу, чтобы получить элемент массива.</p>
  <hr />
  <p id="WRyi">До сих пор мы обсуждали <strong>struct</strong>, <strong>mapping </strong>и <strong>array</strong>. Есть еще один тип данных, который может вас заинтересовать — строки. <strong>String</strong> и <strong>bytes</strong> - это один и тот же тип данных для хранения байтов переменной длины. Давайте посмотрим, как это будет выглядеть, когда мы объявим строковую переменную в хранилище:</p>
  <figure id="Xw0j" class="m_original">
    <img src="https://img3.teletype.in/files/a5/da/a5da5e63-5c86-4f9b-82ba-e1e9df00d583.png" width="648" />
  </figure>
  <p id="GGM7">Этот смарт-контракт ничего не делает, кроме как просто возвращает строку вызывающему. Давайте посмотрим, как строка сохраняется в хранилище. Даже при наличии одной строки скомпилированный код на ассемблере немного сложен:</p>
  <figure id="7k8N" class="m_original">
    <img src="https://img3.teletype.in/files/a8/fc/a8fc35d9-a56a-43d8-98ba-736413f6e3f8.png" width="1059" />
  </figure>
  <p id="NvSa">Код на ассемблере немного сложен, давайте рассмотрим его логику. </p>
  <p id="zpvG">Сначала давайте разберемся с логикой <strong>“((((0x100 * ((0x1 &amp; sload(0x0)) == 0)) – 0x1) &amp; sload(0x0)) / 0x2)”</strong>. По-видимому, поскольку в контракте определена только одна переменная <strong>hello</strong>, для этой переменной зарезервирован адрес хранилища <strong>0x0</strong>. Но, похоже, дело не только в длине, поданной как массивы. </p>
  <p id="b9WG">Из этого сравнения <strong>“((0x1 &amp; sload(0x0)) == 0))”</strong> мы знаем, что последний бит этого поля является флагом. Если бит установлен в <strong>1</strong>, то это сравнение равно <strong>False (0x0)</strong>, поэтому вся строка будет <strong>“(uint256(sload(0x0)) / 0x2)”</strong>. Если бит установлен в <strong>0</strong>, то вся строка будет <strong>“(uint8(sload(0x0)) / 0x2)”</strong>. Это значение присвоено <strong>var7 </strong>в приведенном выше коде. </p>
  <p id="GOWp">Затем это значение сравнивается с <strong>0x1F</strong>. Если оно меньше <strong>0x1F</strong>, эта строка <strong>“mstore(var5,((sload(0x0) / 0x100) * 0x100));”</strong> скопирует первые байты <strong>0x1F </strong>из хранилища <strong>0x0 </strong>в память. Если оно больше <strong>0x1F</strong>, то результатом будет некоторая аналогичная операция, которую мы видели в массиве переменной длины.</p>
  <p id="dHA4">Основываясь на том, что мы видели из кода на ассемблере, мы можем понять основную логику строк. Максимально использовать хранилище, если строка меньше <strong>0x1F </strong>— значит она может быть сохранена в одном слоте хранилища.</p>
  <p id="dDPz"> Таким образом, последний бит поля установлен в <strong>0</strong>, а последний байт имеет длину строки, умноженную на 2. И строка сохраняется в первых 31 байте в слоте. Однако, если длина строки больше 31, то один слот для хранения не может вместить все данные. Таким образом, слот поля будет содержать только поле длины (последний бит установлен равным 1), а данные сохраняются в виде массива переменной длины.</p>
  <p id="dais">До сих пор мы обсуждали реализации хранения большинства типов данных в Solidity. В следующем разделе мы поговорим о памяти и ее взаимодействии с полезной нагрузкой данных.</p>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@jetix37/understanding-byte-code-p2</guid><link>https://teletype.in/@jetix37/understanding-byte-code-p2?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=jetix37</link><comments>https://teletype.in/@jetix37/understanding-byte-code-p2?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=jetix37#comments</comments><dc:creator>jetix37</dc:creator><title>Понимаем байт-код EVM: Часть 2</title><pubDate>Tue, 28 Feb 2023 11:41:09 GMT</pubDate><media:content medium="image" url="https://img2.teletype.in/files/11/6f/116f3ccc-b07e-4864-8b5c-06cbf26d272b.png"></media:content><category>Ethereum</category><description><![CDATA[<img src="https://img4.teletype.in/files/3d/4e/3d4eb75b-032c-461c-b3ff-4fb6b46606b4.jpeg"></img>Оригинал — https://blog.trustlook.com/understand-evm-bytecode-part-2/]]></description><content:encoded><![CDATA[
  <section style="background-color:hsl(hsl(323, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="lGUN">Оригинал — <a href="https://blog.trustlook.com/understand-evm-bytecode-part-2/" target="_blank">https://blog.trustlook.com/understand-evm-bytecode-part-2/</a></p>
    <p id="s7Fj">Канал — <a href="https://t.me/jetix37eth" target="_blank">https://t.me/jetix37eth</a></p>
  </section>
  <figure id="du2J" class="m_original">
    <img src="https://img4.teletype.in/files/3d/4e/3d4eb75b-032c-461c-b3ff-4fb6b46606b4.jpeg" width="1920" />
  </figure>
  <p id="CopP">В первой части мы изучили часть создания контракта в байт-коде виртуальной машины. В этом разделе мы проанализируем рантайм байт-кода виртуальной машины. Мы по-прежнему будем использовать пример кода из предыдущей статьи.</p>
  <figure id="pBn3" class="m_original">
    <img src="https://img1.teletype.in/files/84/c9/84c9a9bc-9bbb-4078-b708-8808b5028a45.png" width="831" />
  </figure>
  <p id="3KN7">Весь байткод выглядит так:</p>
  <p id="8Dcb">608060405234801561001057600080fd5b5060fd8061001f6000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680631003e2d214604e578063b69ef8a814608c575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060b4565b6040518082815260200191505060405180910390f35b348015609757600080fd5b50609e60cb565b6040518082815260200191505060405180910390f35b600081600054016000819055506000549050919050565b600054815600a165627a7a723058208c866a01e15ad0e4316f01fc7c7615f3c4b5f6a8845103d2c1ed587a07f9886b0029</p>
  <p id="B0Zp">Эта часть байт-кода кажется намного длиннее, чем часть создания. Но давайте разделим это на части и проанализируем их одну за другой.</p>
  <figure id="G0vT" class="m_original">
    <img src="https://img3.teletype.in/files/6a/b1/6ab1c79b-d843-41a3-94c8-7ab335151df1.png" width="865" />
  </figure>
  <p id="854s">Эти инструкции фактически сохраняют адрес <strong>0x80</strong> со смещением <strong>0x40</strong> в памяти в качестве указателя свободной памяти для использования в будущем.</p>
  <p id="aNNr">Существует новый код операции <strong>CALLDATASIZE</strong>, который мы раньше не встречали в <strong>0x07</strong>. Он получит размер полезной нагрузки данных из этой транзакции. <strong>LT</strong> — это код операции для сравнения двух элементов в стеке, он вернет значение <strong>TRUE</strong>, если сравнение выполнено. </p>
  <p id="DUre">Итак, собрав все части вместе, мы можем получить эквивалентный ассемблерный код Solidity следующим образом:</p>
  <p id="poRd"><strong>mstore(0x40,0x80); </strong></p>
  <p id="aaGa"><strong>if(msg.data.length &lt; 0x04) { revert(0,0); }</strong></p>
  <p id="2mgx">Мы можем видеть, что в основном эти инструкции инициализируют указатели памяти и проверяют, составляет ли размер полезной нагрузки данных не менее 4 байт. Причина этого заключается в том, что при обычном внешнем вызове смарт-контракта первые 4 байта в полезной нагрузке данных являются хэш-значением для подписи функции. </p>
  <p id="M1l3">Это 4-байтовое значение будет использоваться контрактом для выбора функции для доставки остальных данных, которые являются параметрами для этой функции. Например, если вы вызовете функцию <strong>withdraw(0xABCD)</strong> в смарт-контракте, полезная нагрузка данных для этого вызова будет выглядеть следующим образом:</p>
  <p id="gTuA"><strong>0x3823D66C000000000000000000000000000000000000000000000000000000000000ABCD</strong></p>
  <p id="kFae">В этом примере первое 4-байтовое значение равно <strong>0x3823D66C</strong>, что является хэш-значением SHA3 для “<strong>withdrawn(bytes32)</strong>”. Следующее 32-байтовое целое число является параметром вызова функции <strong>0xABCD</strong>. Это простой пример параметров целочисленной функции. При обработке параметров переменного размера все усложнится. Мы поговорим о них позже.</p>
  <p id="y83F">А пока давайте вернемся к инструкциям, которые мы обсуждали. Размер полезной нагрузки данных должен составлять не менее 4 байт. Если нет, то все вернется на круги своя. Но будет ли это справедливо для всех смарт-контрактов? Что, если мы просто отправим эфир в этот смарт-контракт без вызова какой-либо функции? </p>
  <p id="9oID">Возможно, вы уже помните некоторые функциональные возможности в программах <strong>Solidity</strong>. Да, именно там реализована функция <strong>fallback</strong>. Чтобы подтвердить это, вы можете получить образец с реализованной функцией <strong>fallback</strong>, а затем проверить ветвь инструкции после кода проверки <strong>msg.data.length</strong>.</p>
  <hr />
  <p id="gDlp">Продолжая разбор кода, мы видим следующие команды:</p>
  <figure id="bBFi" class="m_original">
    <img src="https://img4.teletype.in/files/f7/65/f7652798-76b4-45d0-82ff-254372497845.png" width="862" />
  </figure>
  <p id="Qc3T">Если вы посмотрите больше байт-кода виртуальной машины из разных смарт-контрактов, вы всегда найдете один и тот же фрагмент кода или функционально эквивалентный фрагмент кода. Этот фрагмент кода не из того кода, который вы поместили в свой исходный код, и он был введен компилятором. Давайте пройдемся по каждой строчке.</p>
  <p id="7SRP">Сначала код помещает <strong>0xFFFFFFFF</strong> в стек. Это значение будет использоваться в качестве одного из операндов кода операции <strong>AND</strong> в <strong>0x33</strong>. Затем код добавит еще одно огромное постоянное значение, которое будет использоваться в качестве разделителя <strong>DIV</strong> в <strong>0x32</strong>. Для инструкций в <strong>0x2F</strong>, <strong>0x31</strong> код получит первое 32-байтовое значение из полезной нагрузки данных, используя <strong>CALLDATALOAD(0x0)</strong>.</p>
  <p id="6IOz">В сочетании с набором команд более поздних <strong>DIV</strong> и <strong>AND</strong> мы можем видеть, что эти инструкции фактически получают первое 4-байтовое значение полезной нагрузки данных, которое является хэш-значением подписи функции. Вычисленный результат помещается в стек. Затем значение сравнивается с <strong>0x1003e2d2</strong>, используя код операции <strong>EQ</strong> в <strong>0x3A</strong>. Если это было правдой, выполнение будет переведено на адрес <strong>0x4D</strong> с помощью <strong>JUMPI</strong>. В противном случае код продолжит работу, и результат будет сравнен с другим хэш-значением <strong>0xb69ef8a8</strong>.</p>
  <p id="AUZr">Теперь логика этого фрагмента кода довольно ясна. Он получает первое 4-байтовое значение из полезной нагрузки данных и решает, какая функция будет выбрана для запуска. Для кодовых адресов <strong>0x4D</strong> и <strong>0x74</strong> они являются входом для каждой <strong>public</strong> функции, к которой вызывающий может получить доступ к смарт-контрактам. Если ни одно из хэш-значений в коде не удовлетворяется, то это приведет к срабатыванию <strong>fallback</strong> функции смарт-контрактов. Если он не был определен, он просто вернется.</p>
  <hr />
  <p id="0qNL">До сих пор мы знали, как <strong>public</strong> функции могут получить доступ к внешним функциям. Теперь давайте подробнее рассмотрим конкретные функции. Адрес <strong>0x4D</strong> является входом функции <strong>add()</strong>:</p>
  <figure id="hwtL" class="m_original">
    <img src="https://img4.teletype.in/files/7e/5e/7e5e922e-0192-4812-9c32-54a41dfbdb45.png" width="861" />
  </figure>
  <p id="2IeR">На входе в функцию <strong>add()</strong> находится код операции <strong>JUMPDEST</strong>. Это специальный код операции, который помечает только адрес, на который можно перейти. Похоже, что это не играет важной роли для реализации EVM. Однако вы увидите, что это действительно помогает определить график потока управления <strong>(CFG)</strong> для байт-кода. Мы обсудим это в следующем разделе.</p>
  <p id="ae0C">Инструкции, установленные в <strong>0x4F-0x57</strong>, были просмотрены в предыдущем разделе. Они были введены компилятором для функции, не принимающей средства. После этого кода проверки код операции <strong>PUSH1</strong> по адресу <strong>0x5A</strong> легко проигнорировать. Однако это <strong>PUSH1</strong> очень важно для того, чтобы код мог вернуться позже. </p>
  <p id="HjS7">Давайте просто запомним, что адрес <strong>0x62</strong> пока помещен в стек. Затем вызывается <strong>CALLDATALOAD(0x04)</strong> для загрузки параметра из полезной нагрузки данных, который расположен со смещением <strong>0x04</strong>. После получения параметра код перейдет к <strong>0x86</strong> для выполнения:</p>
  <figure id="ekJ5" class="m_original">
    <img src="https://img3.teletype.in/files/26/61/26617a9e-268f-478d-a843-40b6f3ea2d60.png" width="860" />
  </figure>
  <p id="Nn9S">В приведенном выше фрагменте кода значение <strong>0x0</strong> в хранилище загружается с помощью <strong>SLOAD(0x0)</strong>. Затем это значение будет добавлено к параметру, загруженному из полезной нагрузки данных, и сохранено обратно в то же место <strong>0x0</strong> в хранилище. Наконец, мы видим код, который мы поместили внутри функции <strong>add()</strong>:</p>
  <p id="6j5s"><strong>balance = balance + value;</strong></p>
  <hr />
  <p id="3mu4">Мы использовали только одну целочисленную переменную <strong>balance</strong> внутри смарт-контракта. Компилятор присваивает этой переменной смещение <strong>0x0</strong>. Таким образом, любая операция чтения или записи с этой переменной баланса будет помещена в смещение <strong>0x0</strong> в хранилище. </p>
  <p id="WjSF">В конце фрагмента кода используется <strong>JUMP</strong> для возврата к <strong>0x62</strong>. Если вы все еще помните, где это значение <strong>0x62</strong> было помещено в стек. Эта операция может напомнить вам что-то об архитектуре X86. Да, это на самом деле <strong>call</strong> и <strong>ret</strong> для вызовов функций. Поскольку EVM не поддерживает вызовы функций на уровне байт-кода, он может использовать только коды операций <strong>PUSH</strong> и <strong>JUMP</strong> для вызовов функций. Таким образом, возникнет много проблем с созданием <strong>CFG</strong> из байт-кода.</p>
  <hr />
  <p id="ysg5">Давайте вернемся к адресу 0x62, чтобы посмотреть, что произойдет дальше:</p>
  <figure id="Wkmw" class="m_original">
    <img src="https://img4.teletype.in/files/34/5b/345bc947-d85f-43a4-9094-108ff84c1350.png" width="862" />
  </figure>
  <p id="3dLa">Этот фрагмент кода содержит несколько настроек элементов в стеке с помощью кодов операций <strong>DUP</strong> и <strong>SWAP</strong>. Для эквивалентного кода на ассемблере приведенный выше код выглядит следующим образом:</p>
  <p id="gs19"><strong>mstore(mload(0x40), value); </strong></p>
  <p id="wrWE"><strong>return(mload(0x40), 0x20);</strong></p>
  <p id="ml37"><strong>Value</strong> внутри кода было вычисленным результатом предыдущей операции <strong>add()</strong>, которое является новым значением <strong>balance</strong>. Наконец, мы просмотрели весь байт-код внутри функции <strong>add()</strong>.</p>
  <hr />
  <p id="u4PW">Теперь давайте вернемся к фрагменту кода отправки функции для второй функции <strong>HASH 0xb69ef8a8</strong>. Вход для этой функции находится в <strong>0x74</strong>:</p>
  <figure id="5DLJ" class="m_original">
    <img src="https://img4.teletype.in/files/7b/60/7b60c9d3-d4c9-43b7-b1a5-7979e6c5c9ac.png" width="859" />
  </figure>
  <p id="MECv">Мы видим, что первая часть кода действительно похожа на предыдущую функцию, за исключением того, что параметр не был загружен. Затем функция вызовет фрагмент кода с <strong>0x95</strong>. Инструкции в <strong>0x95-0x98</strong> просто загружают значение в <strong>0x0</strong> в хранилище и возвращают. Мы отметили, что фрагмент кода в 0x62 был повторно использован для обеих функций. Это связано с тем, что обе функции вернут переменную <strong>balance</strong> обратно.</p>
  <p id="g59l">Вы можете задаться вопросом, почему внутри кода отправки функции присутствует функция <strong>HASH 0xb69ef8a8</strong>? Разве внутри смарт-контракта нет только одной функции <strong>add()</strong>? Если вы используете базу данных в 4 байта для проверки этого хэша, вы получите <strong>balance()</strong>. По-видимому, переменная хранилища распознается компилятором как <strong>public</strong> функция без параметров.</p>
  <p id="k8HK">Подводя итог этому разделу, мы обсудили всю структуру байт-кода, относящегося к среде выполнения. Как внешние вызывающие пользователи получают доступ к функциям, как передаются параметры. Но для этого демонстрационного примера мы ввели только некоторые целочисленные переменные хранилища. </p>
  <p id="6PDj">Как это будет выглядеть для сопоставлений или массивов переменной длины? Как будут представлены параметры в полезной нагрузке данных для строк? Мы поговорим обо всем этом в следующем разделе.</p>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@jetix37/understanding-byte-code-p1</guid><link>https://teletype.in/@jetix37/understanding-byte-code-p1?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=jetix37</link><comments>https://teletype.in/@jetix37/understanding-byte-code-p1?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=jetix37#comments</comments><dc:creator>jetix37</dc:creator><title>Понимаем байт-код EVM: Часть 1</title><pubDate>Mon, 27 Feb 2023 18:13:17 GMT</pubDate><media:content medium="image" url="https://img4.teletype.in/files/be/2f/be2f4ec2-2966-42b3-997e-70df12ecb44f.png"></media:content><category>Ethereum</category><description><![CDATA[<img src="https://img1.teletype.in/files/8d/0e/8d0ed1b0-380f-42bc-909d-7bfcd5877bc7.jpeg"></img>Оригинал — https://blog.trustlook.com/understand-evm-bytecode-part-1/]]></description><content:encoded><![CDATA[
  <section style="background-color:hsl(hsl(323, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="lC94">Оригинал — <a href="https://blog.trustlook.com/understand-evm-bytecode-part-1/" target="_blank">https://blog.trustlook.com/understand-evm-bytecode-part-1/</a></p>
    <p id="HGsd">Канал — <a href="https://t.me/jetix37eth" target="_blank">https://t.me/jetix37eth</a></p>
  </section>
  <figure id="9kCE" class="m_original">
    <img src="https://img1.teletype.in/files/8d/0e/8d0ed1b0-380f-42bc-909d-7bfcd5877bc7.jpeg" width="1920" />
  </figure>
  <p id="PEFH">Если Вы начали читать эту статью, я думаю, вы уже знаете, что означает EVM. Если Вам действительно нужны некоторые основы, пожалуйста, загуглите “Ethereum Virtual Machine”. Основная цель этой серии статей — помочь разобраться с байт-кодом EVM на случай, если вы будете вовлечены в какую-либо работу по аудиту контрактов на уровне байт-кода или разрабатывать декомпилятор байт-кода EVM.</p>
  <p id="0smF">Теперь давайте начнем с некоторых самых базовых элементов байт-кода EVM. EVM — это виртуальная машина на основе стека. Если у вас есть опыт работы с любой из подобных виртуальных машин (например, Java VM, DVM, .NET VM), вам не составит особого труда понять ее основную идею. </p>
  <p id="RWcE">По сути, байт-код — это машинный язык для виртуальной машины. Этот код, безусловно, не предназначен для чтения человеком так же, как обычный человеческий код. Байт-код может быть скомпилирован с помощью высокоуровневых языков EVM. </p>
  <p id="Qa6T">На данный момент самым популярным из них является Solidity. Чтобы лучше понять байт-код виртуальной машины, я буду использовать несколько простых примеров для демонстрации. Итак, давайте начнем с самого простого примера:</p>
  <figure id="rGYg" class="m_original">
    <img src="https://img2.teletype.in/files/98/24/9824405b-8bcf-45e5-bec8-c90edfc7c61e.png" width="901" />
  </figure>
  <p id="Vaox">Вы можете спросить, почему я не использовал HelloWorld в качестве стандартного примера. Это связано с тем, что обычно в примере HelloWorld используется строковая переменная, а для нашего байт-кода строковая переменная является динамической переменной длины, и позже мы поговорим об этом в другой статье. </p>
  <p id="JKzP">После компиляции кода мы получаем следующий байт-код:</p>
  <figure id="etNm" class="m_original">
    <img src="https://img1.teletype.in/files/4d/39/4d397372-3a62-48e1-bdd6-c15800dd8d38.png" width="1089" />
  </figure>
  <section style="background-color:hsl(hsl(170, 33%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="rtuL">Полная строка: 608060405234801561001057600080fd5b5060fd8061001f6000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680631003e2d214604e578063b69ef8a814608c575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060b4565b6040518082815260200191505060405180910390f35b348015609757600080fd5b50609e60cb565b6040518082815260200191505060405180910390f35b600081600054016000819055506000549050919050565b600054815600a165627a7a72305820006fbc72124720df0674199be8fdbc61b441b312b57ede5096a750c7264f8c330029</p>
  </section>
  <hr />
  <p id="8kHE">Мы подробно разберем, что означает эта строка.</p>
  <p id="zrkA">Во-первых, если вы внимательно посмотрите на строку, вы поймете, что это строка шестнадцатеричного формата для представления фрагмента двоичного файла.</p>
  <p id="Fete">Действительно, реальный байткод виртуальной машины на самом деле представляет собой двоичную строку, но для того, чтобы лучше показать его другим, он всегда должен быть представлен в шестнадцатеричном формате.</p>
  <p id="EFMb">Код операции — это инструкция EVM. Каждый код операции сам по себе представляет собой 8-битное беззнаковое целое число. Например, 0x00 означает STOP, 0x01 означает ADD. Чтобы понять все значения кодов операций, можно подглядеть в <a href="https://ethereum.github.io/yellowpaper/paper.pdf" target="_blank">желтую бумагу Ethereum.</a></p>
  <p id="ygaO">На данный момент мы не будем рассматривать все коды операций, чтобы объяснить их значения. Нам просто нужно знать их основы и объяснить новые коды операций, когда с ними столкнемся. Итак, давайте начнем с первой части байткода EVM: <strong>6080604052</strong>.</p>
  <p id="1ATH">Если мы заглянем в желтую бумагу, то обнаружим там следующие коды:</p>
  <figure id="ixcq" class="m_original">
    <img src="https://img1.teletype.in/files/04/fe/04fe3de2-7442-49fb-8d13-234d9c48600e.png" width="867" />
  </figure>
  <p id="mNGh">Из приведенного выше фрагмента кода мы можем видеть 2 кода операции, <strong>PUSH1 </strong>и <strong>MSTORE</strong>. <strong>PUSH1 </strong>означает поместить 1-байтовое целое число в стек для дальнейшего использования. <strong>PUSH</strong>ы могут быть вплоть до 32.</p>
  <p id="m5lw">В EVM все целые числа имеют длину от 1 до 32 байт. Коды операций семейства <strong>PUSH </strong>— единственные, которые поставляются с операндами в байткоде виртуальной машины, потому что для остальных кодов операций они будут использовать значения в стеке. </p>
  <p id="2sG5">В этом примере первые два <strong>PUSH1 </strong>поместят <strong>0x80 </strong>и <strong>0x40 </strong>в стек, затем <strong>MSTORE </strong>будет использовать 2 элемента в стеке для операции записи в память. Итак, приведенный выше фрагмент кода на самом деле является кодом EVM на ассемблере: <strong>mstore(0x40,0x80)</strong>.</p>
  <p id="IWYU">После того, как <strong>MSTORE</strong> использует 2 элемента в стеке, они будут извлечены. Обычно результат кода операции будет помещен в стек для последующего использования. Однако <strong>MSTORE </strong>не имеет возвращаемого значения, поэтому он ничего не поместит в стек.</p>
  <hr />
  <p id="Qic0">Таким образом, если вы продолжите просматривать весь байт-код, вы получите весь список кодов операций. Но прежде чем мы продолжим изучение дополнительных кодов операций, давайте поговорим еще о двух концепциях в среде EVM — <strong>memory </strong>и <strong>storage</strong>.</p>
  <p id="M9DI"><strong>Memory</strong> — это читаемая и записываемая структура, предназначенная для вычисления хэша и внешних вызовов или возвратов. Память сбрасывается как стек всякий раз, когда запускается EVM. Отличие от стека заключается в том, что доступ к памяти возможен по адресу. </p>
  <p id="CF6A">В предыдущем примере <strong>MSTORE </strong>сохранит указанное значение <strong>0x80 </strong>в соответствующий адрес <strong>0x40</strong>. Вы можете задаться вопросом о значении этого действия. На самом деле, адрес <strong>0x40 </strong>в памяти EVM зарезервирован для “указателя свободной памяти”, поэтому, когда коду EVM потребуется использовать некоторую память, он получит указатель свободной памяти из <strong>0x40</strong>. Кроме того, если вы не хотите, чтобы эта память была переполнена будущей операцией, вам необходимо обновить значение в <strong>0x40</strong>, чтобы будущая операция больше не использовала ту же память.</p>
  <p id="S4Gt">Помимо <strong>memory</strong> и стека, <strong>storage</strong>-переменные — это те, которые содержат состояния. Таким образом, переменные <strong>storage</strong> не будут сбрасываться каждый раз при перезапуске EVM. Вы можете использовать <strong>storage </strong>как словарь или хэш-таблицу. Все, что изменилось в <strong>storage</strong>, будет записано в блокчейн Ethereum. Коды операций, связанные с хранением, - это <strong>SLOAD </strong>и <strong>SSTORE</strong>. Мы подробнее поговорим о <strong>storage</strong>-переменных при анализе более сложных структур, таких как <strong>mapping</strong> или <strong>array</strong>.</p>
  <hr />
  <p id="apJL">Основываясь на этой информации, давайте продолжим работу со строкой байткода:</p>
  <figure id="kyQP" class="m_original">
    <img src="https://img2.teletype.in/files/96/f6/96f675bf-6db9-4232-8d4f-05b87606d6bc.png" width="853" />
  </figure>
  <p id="ua8O">Этот фрагмент кода немного длинный, но не беспокойтесь об этом. Давайте пройдемся по этому вопросу шаг за шагом. </p>
  <p id="l3Bu"><strong>CALLVALUE</strong> поместит <strong>msg.value</strong> в стек, затем <strong>DUP1 </strong>продублирует это значение в стеке и проверит, равно ли оно 0 или нет, используя <strong>ISZERO</strong>. Если значение <strong>ISZERO</strong>, полученное из стека, равно 0, этот код операции поместит значение <strong>TRUE</strong> в стек для следующих инструкций. </p>
  <p id="cUoN">Следующий <strong>PUSH2</strong> поместит кодовый адрес <strong>0x0010</strong> в стек для перехода. <strong>JUMP</strong> —  это инструкция условного перехода, которая использует 2 элемента из стека. Один предназначен для результата условия, а другой - для адреса перехода. Если условие (в данном случае это значение <strong>ISZERO(msg.value)</strong>) выполнено, выполнение перейдет к <strong>0x0010</strong>, в противном случае код завершится <strong>REVERT(0,0)</strong>. </p>
  <p id="LMcU">Таким образом, байт-код с адреса <strong>0x05</strong>-<strong>0x0F</strong> можно перевести как: <strong><em>if(msg.value != 0) revert();</em></strong></p>
  <p id="0dcl">Причина, по которой мы не увидели эту строку в нашем исходном коде, заключается в том, что эта проверка была введена компилятором для <strong>non-payable </strong>функции.</p>
  <hr />
  <p id="DOMx">Если вы упорядочите стек вручную, вы можете увидеть, что существует инструкция <strong>CODECOPY(0x0,0x001F,0xC7)</strong>. </p>
  <p id="U6eV">Это означает, что он скопирует код <strong>0xC7</strong> байт из смещения <strong>0x1F </strong>в память <strong>(0x0, 0xC7)</strong>. Затем код вызовет <strong>RETURN(0x0,0xC7)</strong>, чтобы передать скопированные данные обратно в EVM. До сих пор Вы, возможно, догадывались о логике этой операции и о том, какова функциональность этого фрагмента байт-кода.</p>
  <hr />
  <p id="Lsp6">Скорее всего, весь фрагмент байт-кода, сгенерированный компилятором Remix, состоит из нескольких частей. Набор из <strong>0-0x1E</strong> является частью контракта для создания. Этот код будет вызываться только во время создания смарт-контракта. Он вызовет конструктор контракта, а также скопирует часть кода во время выполнения в EVM для создания. </p>
  <p id="wr1t">После создания учетной записи контракта будет вызвана часть кода из<strong> 0x1F-(0x1F+0xC7)</strong> для будущих транзакций по этому контракту, и функция конструктора больше не будет вызываться. Кроме того, вы, возможно, обнаружили, что в части создания байт-кода нет никаких инструкций <strong>JUMP </strong>или <strong>JUMPI</strong>.</p>
  <p id="vD1n">Чтобы доказать правильность наших предположений, давайте создадим другой код Solidity с функцией конструктора:</p>
  <figure id="GdVc" class="m_original">
    <img src="https://img4.teletype.in/files/f7/63/f7631054-5d93-46b5-b0a1-1059af458a5c.png" width="666" />
  </figure>
  <section style="background-color:hsl(hsl(323, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="QOjI">После компиляции его с помощью Remix мы получаем такой код:</p>
    <p id="XMCH">608060405234801561001057600080fd5b5060405160208061014683398101806040528101908080519060200190929190505050806000819055505060fd806100496000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680631003e2d214604e578063b69ef8a814608c575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060b4565b6040518082815260200191505060405180910390f35b348015609757600080fd5b50609e60cb565b6040518082815260200191505060405180910390f35b600081600054016000819055506000549050919050565b600054815600a165627a7a723058209494c57a0075e1335b7b0adeabecca32387ff8db3d573d798daa361f0d6f7eb90029</p>
  </section>
  <p id="ufug">По-видимому, код длиннее предыдущего, так как мы определили там функцию конструктора. Итак, давайте разберем код операции на более читаемые коды:</p>
  <figure id="su8k" class="m_original">
    <img src="https://img3.teletype.in/files/ed/4b/ed4be544-3432-49e0-99e8-602580f11bd2.png" width="677" />
  </figure>
  <p id="Li8F">Мы можем видеть некоторый аналогичный код, установленный в начале и в конце. Но код, установленный между <strong>0x12 </strong>и <strong>0x25</strong>, является новым. Итак, давайте сосредоточимся на этой новой части. </p>
  <p id="hxcq">Сначала, в коде операции, установленном <strong>0x12 </strong>и <strong>0x14</strong>, была вызвана <strong>MLOAD(0x40)</strong> для получения значения из памяти по адресу <strong>0x40</strong>. Из предыдущего раздела мы уже знали, что адрес <strong>0x40 </strong>в памяти содержит указатель свободной памяти в EVM. В данном случае это <strong>0x80</strong>. </p>
  <p id="7obY">Затем, после упорядочивания стека с помощью <strong>PUSH </strong>и <strong>DUP</strong>, он будет иметь <strong>[... 0x20, 0x00FA, 0x80]</strong> в стеке перед вызовом <strong>CODECOPY</strong>. Таким образом, код вызовет <strong>CODECOPY(0x80, 0x00FA, 0x20)</strong>. </p>
  <p id="Ch4G">По-видимому, это действие не было показано в предыдущем демонстрационном байт-коде. Это как-то связано с новым кодом, который мы поместили внутри функции конструктора. Он копирует последние 32 байта данных из кода в адрес свободной памяти. Скорее всего, это значение параметра во время развертывания контракта. Давайте продолжим работу с более поздним байт-кодом.</p>
  <p id="AVTz">В наборе инструкций <strong>0x1D </strong>– <strong>0x21 </strong>код добавил <strong>0x20 </strong>к текущему указателю свободной памяти <strong>0x80 </strong>и сохранил его обратно по адресу <strong>0x40 </strong>с помощью <strong>MSTORE(0x40, 0x80+0x20)</strong>.</p>
  <p id="Sdrq">Затем команда в <strong>0x22 </strong>поместит значение, возвращаемое <strong>MLOAD(0x80)</strong>, в стек, который представляет собой 32-байтовое значение, скопированное из кода. Более поздний код в <strong>0x23</strong>, <strong>0x25 </strong>сохранит значение в хранилище со смещением <strong>0x0</strong>, используя <strong>SSTORE(0x0, LOAD(0x80))</strong>. Итак, вкратце, инструкции между <strong>0x12 </strong>и <strong>0x25 </strong>в основном выполняют некоторую операцию, подобную <em><strong>SSTORE(0x0,CODECOPY(0x80, 0x00FA, 0x20))</strong>.</em></p>
  <hr />
  <p id="I4w1">По-видимому, во время развертывания нового контракта инициализированные параметры указываются в конце байт-кода виртуальной машины в полезной нагрузке данных транзакции. Затем в процессе создания функция конструктора получит параметр с помощью <strong>CODECOPY</strong>.</p>
  <p id="thei">До сих пор мы говорили об основах байт-кода EVM, включая три типа структур данных в EVM: <strong>стек</strong>, <strong>память </strong>и <strong>хранилище</strong>, некоторые обычные коды операций, участвующие в создании смарт-контракта, способ передачи параметров конструктора и структуру скомпилированного байт-кода EVM.</p>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@jetix37/limit-break-contract-token</guid><link>https://teletype.in/@jetix37/limit-break-contract-token?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=jetix37</link><comments>https://teletype.in/@jetix37/limit-break-contract-token?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=jetix37#comments</comments><dc:creator>jetix37</dc:creator><title>Limit Break: Программируемые Обратно Совместимые Роялти</title><pubDate>Wed, 11 Jan 2023 06:26:32 GMT</pubDate><media:content medium="image" url="https://img3.teletype.in/files/e8/a1/e8a1c376-8123-4626-b4c4-6144f2bc5464.png"></media:content><category>limit break</category><description><![CDATA[<img src="https://img4.teletype.in/files/78/4c/784ce0b8-8ef5-4e13-8721-cbe761bc7d03.png"></img>Оригинал — https://twitter.com/gabrielleydon/status/1612899581003714561]]></description><content:encoded><![CDATA[
  <figure id="wDLN" class="m_original">
    <img src="https://img4.teletype.in/files/78/4c/784ce0b8-8ef5-4e13-8721-cbe761bc7d03.png" width="1496" />
  </figure>
  <section style="background-color:hsl(hsl(323, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="mDpn"><strong>Оригинал</strong> — <a href="https://twitter.com/gabrielleydon/status/1612899581003714561" target="_blank">https://twitter.com/gabrielleydon/status/1612899581003714561</a></p>
    <p id="WGef"><strong>Автор</strong> — <a href="https://t.me/jetix37eth" target="_blank">@jetix37eth</a></p>
  </section>
  <section style="background-color:hsl(hsl(236, 74%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="trFP">Будем честны, NFT в их нынешнем виде очень <strong>скучные</strong>.</p>
  </section>
  <section style="background-color:hsl(hsl(170, 33%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="jZMU">За несколькими заметными исключениями, почти все NFT ограничены только тремя функциями:</p>
    <ul id="oCCj">
      <li id="IouD">Держать на кошельке</li>
      <li id="3TiI">Торговать на площадках</li>
      <li id="CVvs">Хранить медиа вместе с некоторыми дополнительными атрибутами</li>
    </ul>
    <p id="6XeP"></p>
    <p id="mIhs">Это не очень хорошо!</p>
  </section>
  <section style="background-color:hsl(hsl(236, 74%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="C7V7">Способность технологии NFT трансформировать игровую индустрию может быть раскрыта только с помощью смелых технологических обновлений. Отсутствие контроля над экономическими границами внутриигровых предметов не позволяет разработчикам игр создать сбалансированную внутриигровую экономику и вывести Web3 на новый уровень.</p>
  </section>
  <section style="background-color:hsl(hsl(199, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="tkPr">Индустрия NFT столкнулась с угрозой смерти в конце 2022 года, когда площадки, жаждущие ликвидности, перешли к сделкам с нулевым или опциональным роялти в битве за ликвидность. Этот сдвиг в отрасли выявил два фундаментальных недостатка в текущей структуре индустрии NFT.</p>
  </section>
  <section style="background-color:hsl(hsl(236, 74%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="sUD2"><strong>Во-первых</strong>, несмотря на идеал децентрализации и недоверия Web3, федералы фундаментально настроены против многих создателей и в пользу горстки централизованных бирж.</p>
  </section>
  <section style="background-color:hsl(hsl(199, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="qXoc"><strong>Во-вторых</strong>, ныне умирающий отраслевой стандарт универсальных роялти существовал только как общественный договор, а не в блокчейне. На этом текущем пути многие создатели просто покинут пространство или вообще не войдут в него.</p>
  </section>
  <section style="background-color:hsl(hsl(236, 74%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="50hk">При разработке новой парадигмы, меняющей отрасль, Limit Break поставил в приоритет две ключевые цели, соответствующие духу Web3: <strong>обратная совместимость</strong> и <strong>свобода выбора</strong>.</p>
  </section>
  <section style="background-color:hsl(hsl(199, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="Xram">Архитектура <strong>Creator Token</strong> обеспечивает обратную совместимость с помощью элегантного, простого в использовании механизма стейкинга, предоставляя создателям всех ранее существовавших коллекций возможность применять новые стандарты и функции токенов.</p>
  </section>
  <section style="background-color:hsl(hsl(236, 74%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="7IzL">Самое главное, архитектура <strong>Creator Token</strong> продвигает ценности децентрализации, предоставляя держателям токенов свободу выбора с помощью системы согласия, где только они могут решать, делать ставки в систему или нет.</p>
  </section>
  <section style="background-color:hsl(hsl(199, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="ykAZ">Разработчикам требуется свобода действий для определения того, как используются их NFT. Белые списки передачи обеспечивают надежный механизм для обеспечения соблюдения правил, что, в свою очередь, делает возможной разработку сложных, забавных и новых функций.</p>
  </section>
  <section style="background-color:hsl(hsl(236, 74%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="ZMAy">Стейкинг предоставляет <strong>обратно совместимую систему согласия</strong>, которая позволяет разработчикам обновлять свои коллекции, не жертвуя неизменностью. Пользователи сами выбирают, соглашаются ли они на обновление. Разработчики должны согласовывать стимулы со своим сообществом, чтобы поощрять обновления.</p>
  </section>
  <section style="background-color:hsl(hsl(199, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="OGsu">Когда пользователи стейкают, токен переходит в обернутое состояние, в котором переводы могут выполняться доверенными операторами, занесенными в белый список. Во многих случаях пользователи также могут <strong>отказаться</strong> от этого состояния.</p>
  </section>
  <section style="background-color:hsl(hsl(236, 74%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="GEBo">Некоторые стимулы, которые можно использовать, включают, но не ограничиваются:</p>
    <ul id="MNrL">
      <li id="RsaN">Стейкинг для ревила</li>
      <li id="uP48">Стейкинг за награды</li>
      <li id="XBH6">Рентабельность</li>
      <li id="EPTi">Гарантированный флор</li>
      <li id="P0Bj">Программируемые роялти для коллекционеров и партнеров</li>
      <li id="6Gl1">Внутриигровое применение</li>
    </ul>
  </section>
  <section style="background-color:hsl(hsl(323, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="ng6z"><a href="https://medium.com/limit-break/introducing-opt-in-programmable-royalties-and-more-through-staking-by-limit-break-3a166e3749e3" target="_blank"><strong>Подробнее про новую архитектуру Limit Break</strong></a></p>
  </section>
  <section style="background-color:hsl(hsl(199, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="JZn4">Как правило, внесение в белый список одного или нескольких доверенных операторов передачи позволяет использовать <strong>новые функции</strong>, которые ранее были невозможны. Новая функциональность теперь может быть построена вокруг передачи NFT без необходимости встраивать пользовательские функции непосредственно в сам контракт NFT.</p>
  </section>
  <section style="background-color:hsl(hsl(236, 74%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="KJDw">В версии 1.1 мы представим концепцию <strong>минимального флора</strong>. Это позволит создателям впервые в истории определить минимальную цену продажи своих NFT в сети!</p>
  </section>
  <section style="background-color:hsl(hsl(199, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="PfJQ">Элитные художники могут избегать создания NFT из-за беспокойства о том, что их работы продаются ниже стоимости их традиционных произведений. Поскольку традиционное изобразительное искусство, как правило, неликвидно, снижение ликвидности вряд ли будет проблемой для этого типа пользователей.</p>
  </section>
  <section style="background-color:hsl(hsl(236, 74%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="Ovqg"><strong>Минимальный флор решает эту проблему</strong>! Теперь известный художник, который продает ценные произведения искусства, может устанавливать минимальную цену за свои NFT, и, таким образом, ему никогда не придется опасаться репутационного ущерба, связанного с тем, что их произведения продаются по дешевке.</p>
  </section>
  <section style="background-color:hsl(hsl(199, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="o13e">Минимальный флор может использоваться разработчиками игр разными способами, чтобы повлиять на баланс их внутриигровой экономики, что улучшит игровой процесс для игроков. Это может помочь изолировать внутриигровую экономику от внешних и макроэкономических факторов.</p>
  </section>
  <section style="background-color:hsl(hsl(236, 74%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="F14A">Например, если у разработчика игры есть суперультраредкое внутриигровое оружие в единственном экземпляре, одна продажа этого предмета ниже рыночной может негативно повлиять на всю внутриигровую экономику. Минимальный флор остановил бы это.</p>
  </section>
  <section style="background-color:hsl(hsl(199, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="hj6g">Минимальный флор также сделает программируемые роялти обязательными для исполнения на уровне контракта, поскольку они всегда должны были работать. Комиссии и роялти на рынке гарантированы, если контрольный поток и средства от сделок NFT должны пройти через новый стандарт контракта.</p>
  </section>
  <section style="background-color:hsl(hsl(236, 74%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="4CGf">Решение проблемы принудительного применения роялти позволяет использовать новые функции, включающие роялти аффилированных лиц и коллекционеров по их собственным индивидуальным NFT. Будет много справедливых, инновационных схем распределения роялти, разработанных командами, как только обязательные роялти будут нормализованы.</p>
  </section>
  <section style="background-color:hsl(hsl(199, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="WJU0">Одним из примеров роялти коллекционеров является то, что первоначальный пользователь, который <strong>создал</strong> NFT, получает лицензионные платежи за этот отдельный NFT <strong>навсегда</strong>. Аффилированные лица, с другой стороны, могут получать роялти за NFT, отчеканенные их рефералами.</p>
  </section>
  <section style="background-color:hsl(hsl(236, 74%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="bc6K">Включение коллекционеров в качестве заинтересованных сторон в отношении роялти — это мощный способ, с помощью которого создатели могут согласовать стимулы со своим сообществом держателей NFT. Это создает ценность для коллекционеров NFT, которая ранее была невозможна, и будет работать вечно благодаря смарт-контрактам.</p>
  </section>
  <section style="background-color:hsl(hsl(199, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="zaD2">Запуск контрактов <strong>Creator Token</strong> является важным шагом на пути к улучшению NFT. Нам не терпится увидеть, что авторы создадут с его помощью в 2023 году и далее!</p>
  </section>
  <section style="background-color:hsl(hsl(323, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="7mcN"><strong>Дисклеймер</strong>: Команды Limit Break и DigiDaigaku все еще планируют, как они могут реализовать эти решения, поэтому пока не делайте никаких предположений о том, как это будет работать для какой-либо команды или их продуктов.</p>
  </section>

]]></content:encoded></item></channel></rss>