<?xml version="1.0" encoding="utf-8" ?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:tt="http://teletype.in/" xmlns:opensearch="http://a9.com/-/spec/opensearch/1.1/"><title>параноик хуесос</title><author><name>параноик хуесос</name></author><id>https://teletype.in/atom/paranoikcodit</id><link rel="self" type="application/atom+xml" href="https://teletype.in/atom/paranoikcodit?offset=0"></link><link rel="alternate" type="text/html" href="https://teletype.in/@paranoikcodit?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=paranoikcodit"></link><link rel="next" type="application/rss+xml" href="https://teletype.in/atom/paranoikcodit?offset=10"></link><link rel="search" type="application/opensearchdescription+xml" title="Teletype" href="https://teletype.in/opensearch.xml"></link><updated>2026-04-23T06:07:38.933Z</updated><entry><id>paranoikcodit:gospodstvo-cambria</id><link rel="alternate" type="text/html" href="https://teletype.in/@paranoikcodit/gospodstvo-cambria?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=paranoikcodit"></link><title>Тотальное господство в Cambria'ии или как я получил бан за твинк</title><published>2025-04-28T15:30:00.375Z</published><updated>2025-04-28T15:30:00.375Z</updated><summary type="html">Все началось в начале апреля, когда я узнал о грядущем втором сезоне Камбрии. Тогда я лишь подумывал, стоит ли вообще залететь в эту тему.</summary><content type="html">
  &lt;p id=&quot;5CcO&quot;&gt;Все началось в начале апреля, когда я узнал о грядущем втором сезоне Камбрии. Тогда я лишь подумывал, стоит ли вообще залететь в эту тему.&lt;/p&gt;
  &lt;h2 id=&quot;gO7J&quot;&gt;Начало&lt;/h2&gt;
  &lt;section style=&quot;background-color:hsl(hsl(236, 74%, var(--autocolor-background-lightness, 95%)), 85%, 85%);&quot;&gt;
    &lt;p id=&quot;vqpr&quot;&gt;Время шло, сомнения постепенно сменялись надеждой. Я собрал восемь юнитов для отыгровки, и мы начали играть. За каждого пришлось отдать не меньше 300 $ в эквиваленте ETH — в итоге вышло около 1,2 ETH плюс небольшой резерв на непредвиденные расходы.&lt;/p&gt;
  &lt;/section&gt;
  &lt;h2 id=&quot;ojFE&quot;&gt;Сомнения&lt;/h2&gt;
  &lt;section style=&quot;background-color:hsl(hsl(24,  24%, var(--autocolor-background-lightness, 95%)), 85%, 85%);&quot;&gt;
    &lt;p id=&quot;irR4&quot;&gt;В течение всего сезона каждый игрок на сервере так или иначе чувствовал, что команда проекта не готова оперативно решать проблемы. Ботов было море, и условно их можно разделить на два типа:&lt;/p&gt;
    &lt;ul id=&quot;60yu&quot;&gt;
      &lt;li id=&quot;4cyw&quot;&gt;&lt;strong&gt;фармеры&lt;/strong&gt; — добывают мобов / руду / рыбу;&lt;/li&gt;
      &lt;li id=&quot;Chzq&quot;&gt;&lt;strong&gt;ПК-шники (Player Killer)&lt;/strong&gt; — боты, убивающие игроков.&lt;/li&gt;
    &lt;/ul&gt;
    &lt;p id=&quot;tl6B&quot;&gt;Из-за них нормальный геймплей страдал, прогресс тормозился.&lt;/p&gt;
  &lt;/section&gt;
  &lt;h2 id=&quot;Uol8&quot;&gt;Орешники&lt;/h2&gt;
  &lt;section style=&quot;background-color:hsl(hsl(170, 33%, var(--autocolor-background-lightness, 95%)), 85%, 85%);&quot;&gt;
    &lt;p id=&quot;k7Xx&quot;&gt;На седьмой день, когда мы уже кое-как развились, в голову пришла классная мысль: а что, если у нас будет собственная дивизия ботов, которые в любой момент смогут мгновенно появиться рядом с нами и прикрыть от противников?&lt;/p&gt;
    &lt;p id=&quot;BuYA&quot;&gt;Так родился проект с кодовым названием &lt;strong&gt;O.R.E.S.H.N.I.K&lt;/strong&gt; — сеть ботов, которые быстро пополняли бы наши ряды и служили боевой мощью гильдии.&lt;/p&gt;
    &lt;p id=&quot;bME6&quot;&gt;Я углубился в исходники и понял, насколько всё это криво: игра, по сути, собрана на Phaser.js и Svelte. 😋&lt;br /&gt; Тогда-то я ещё не подозревал, какой пиздец меня ждёт дальше.&lt;/p&gt;
  &lt;/section&gt;
  &lt;h2 id=&quot;M6ZP&quot;&gt;Провал&lt;/h2&gt;
  &lt;section style=&quot;background-color:hsl(hsl(55,  86%, var(--autocolor-background-lightness, 95%)), 85%, 85%);&quot;&gt;
    &lt;p id=&quot;xKwJ&quot;&gt;В день, когда кодовая база уже была готова, разработчики выкатила античит, который должен был поставить крест на гильдиях, использующих подобные уловки и автоматизированных ботов.&lt;/p&gt;
    &lt;p id=&quot;Kbpc&quot;&gt;Я не придал новости особого значения — и поплатился. После успешного теста лёг спать, а утром обнаружил бан на основном аккаунте и такой же бан на тестовом аккаунте бота.&lt;/p&gt;
  &lt;/section&gt;
  &lt;h2 id=&quot;KAvq&quot;&gt;Выводы&lt;/h2&gt;
  &lt;section style=&quot;background-color:hsl(hsl(199, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);&quot;&gt;
    &lt;p id=&quot;w8bC&quot;&gt;Тестить бота с одного IP-адреса — ебучая глупость. Больше таких банальных ошибок допускать не планирую.&lt;br /&gt; И вообще, писать ботов стоило ещё во время F2P-стейджа, до начала самого сезона.&lt;/p&gt;
  &lt;/section&gt;

</content></entry><entry><id>paranoikcodit:JITO-SOLANA</id><link rel="alternate" type="text/html" href="https://teletype.in/@paranoikcodit/JITO-SOLANA?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=paranoikcodit"></link><title>история о том, как солану чуть не централизовали</title><published>2024-03-17T22:03:45.408Z</published><updated>2024-03-18T17:42:19.925Z</updated><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://img4.teletype.in/files/f0/25/f025c14b-eac6-4aa8-b6d0-f55085af2d56.png"></media:thumbnail><summary type="html">&lt;img src=&quot;https://img1.teletype.in/files/87/82/8782b6e2-0bcb-4046-a416-1350582e03dc.png&quot;&gt;Эх, солана-солана, что же ты творишь...</summary><content type="html">
  &lt;figure id=&quot;6mkk&quot; class=&quot;m_column&quot;&gt;
    &lt;img src=&quot;https://img1.teletype.in/files/87/82/8782b6e2-0bcb-4046-a416-1350582e03dc.png&quot; width=&quot;2400&quot; /&gt;
  &lt;/figure&gt;
  &lt;blockquote id=&quot;aPDP&quot;&gt;Эх, солана-солана, что же ты творишь...&lt;/blockquote&gt;
  &lt;section style=&quot;background-color:hsl(hsl(236, 74%, var(--autocolor-background-lightness, 95%)), 85%, 85%);&quot;&gt;
    &lt;h2 id=&quot;jZNE&quot; data-align=&quot;center&quot;&gt;Солана и мемпул&lt;/h2&gt;
  &lt;/section&gt;
  &lt;section style=&quot;background-color:hsl(hsl(0,   0%,  var(--autocolor-background-lightness, 95%)), 85%, 85%);&quot;&gt;
    &lt;p id=&quot;8w1o&quot;&gt;Соланочка наша любимая архитектурно так устроена, что мемпула в ней нет и быть не может. &lt;/p&gt;
    &lt;p id=&quot;vQpu&quot;&gt;Оптимизации, которые были предприняты для решения вопроса мемпула, позволили решить соланочке не только вопрос с мемпулом, но и повысила пропускную способность.&lt;br /&gt;&lt;/p&gt;
    &lt;p id=&quot;j5d9&quot;&gt;&lt;strong&gt;Gulf Stream&lt;/strong&gt; - одна из оптимизаций, которая позволяет соланочке быть такой быстрой. Ее главная особенность - каждый раз, как только в валидатор попадает транзакция, валидатор заранее напрявляет транзакцию в лидера, который уже в свою очередь будет исполнять транзакцию. (данные из доки соланы)&lt;br /&gt;&lt;/p&gt;
    &lt;p id=&quot;RmSN&quot;&gt;&lt;strong&gt;Proof Of History &lt;/strong&gt;- очень сложная штука, которую в двух словах не объяснить. Если говорить ОЧЕНЬ поверхностно, то это последовательность вычислений, которая позволяет определять временной диапазон между транзакциями.&lt;/p&gt;
    &lt;p id=&quot;OsXD&quot;&gt;&lt;em&gt;ну еще в соланке, чем больше стейкинг в валидаторе, тем больше транзакций он может обрабатывать(запомните это на будущее)&lt;/em&gt;&lt;/p&gt;
  &lt;/section&gt;
  &lt;section style=&quot;background-color:hsl(hsl(0,   0%,  var(--autocolor-background-lightness, 95%)), 85%, 85%);&quot;&gt;
    &lt;p id=&quot;zYB8&quot;&gt;Из этой непонятной на превый взгляд хуйня, можно понять, что в соланочке то по сути и нет пендинговых транзакций, они сразу попадают в &amp;quot;исполнителя&amp;quot;, хранятся там до момента исполнения +- 32 блока, а потом если все очень плохо, то просто пропадают.&lt;/p&gt;
  &lt;/section&gt;
  &lt;section style=&quot;background-color:hsl(hsl(236, 74%, var(--autocolor-background-lightness, 95%)), 85%, 85%);&quot;&gt;
    &lt;h2 id=&quot;0rGM&quot; data-align=&quot;center&quot;&gt;&lt;strong&gt;JITO и их &amp;quot;мемпул&amp;quot;&lt;/strong&gt;&lt;/h2&gt;
  &lt;/section&gt;
  &lt;section style=&quot;background-color:hsl(hsl(34,  84%, var(--autocolor-background-lightness, 95%)), 85%, 85%);&quot;&gt;
    &lt;p id=&quot;NDts&quot;&gt;Житовцы, как истинные гои, хотели зарабатывать мильйоны денег. Насмотрелись на MEV на эфирах всяких и решили, а чо бы и на соланочке такое не сделать????&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Сели и думают:&lt;/strong&gt; &lt;em&gt;ТАКК, КАК БЫ НАМ ТАКОЕ РЕАЛИЗОВАТЬ??&lt;/em&gt;&lt;/p&gt;
  &lt;/section&gt;
  &lt;section style=&quot;background-color:hsl(hsl(0,   0%,  var(--autocolor-background-lightness, 95%)), 85%, 85%);&quot;&gt;
    &lt;p id=&quot;rPxB&quot;&gt;Жито переписывают валидатор соланы, его главные функции становятся:&lt;/p&gt;
    &lt;ul id=&quot;20SV&quot;&gt;
      &lt;li id=&quot;68cd&quot;&gt;Перенаправлять транзакции во временное хранилище&lt;/li&gt;
      &lt;li id=&quot;gJ9N&quot;&gt;Исполнять и симулировать бандлы&lt;/li&gt;
    &lt;/ul&gt;
    &lt;blockquote id=&quot;Xh6G&quot;&gt;&lt;strong&gt;БАНДЛ&lt;/strong&gt; - &lt;em&gt;ПОСЛЕДОВАТЕЛЬНОСТЬ ТРАНЗАКЦИЙ, КОТОРЫЕ ВАЛИДАТОР ИСПОЛНЯЕТ ПОСЛЕДОВАТЕЛЬНО, ТРАНЗАКЦИЮ ЗА ТРАНЗАКЦИЕЙ&lt;/em&gt;&lt;/blockquote&gt;
    &lt;p id=&quot;ONBq&quot;&gt;В качестве временного хранилища выступил - релеер&lt;br /&gt;&lt;strong&gt;Jito-Relayer&lt;/strong&gt; - штучка, которая принимает транзакции со всех валидаторов из сети жито, дедублицирует их, перенаправляет их непосредственно в распределительный пункт(block engine), удерживает их какое-то время, а дальше отправляет обратно в валидатора. &lt;/p&gt;
    &lt;p id=&quot;x1Jg&quot;&gt;&lt;strong&gt;Jito Block Engine &lt;/strong&gt;- хуйнюшка, которая принимает от релеера все транзакции и направляет их в ботов. Боты получают эти транзакции, обрабатывают и на выходе возвращают тот самый БАНДЛ, в котором хранится эта самая транзакция и нужные для мева другие транзакции. Блок енжин отправляет в релеер этот бандл, а он уже перенаправляет их в валидатор(если не было превышено 200 миллисекунд).  &lt;/p&gt;
  &lt;/section&gt;
  &lt;section style=&quot;background-color:hsl(hsl(0,   0%,  var(--autocolor-background-lightness, 95%)), 85%, 85%);&quot;&gt;
    &lt;p id=&quot;oNso&quot;&gt;Такая сложная связка нескольких инструментов позволила JITO добиться создания &amp;quot;мемпула&amp;quot;, но это было бы невозможно, если бы в их связке не было бы большого количества валидаторов с большим стейкингом. для этого они проводили всякие активности и тыр пыр мыр мыр уже есть валидаторы с большими объемами стейкинга. ВОТ ЗА ЧТО ВЫ ПОЛУЧИЛИ ДРОПЫ, ДОВОЛЬНЫ???&lt;/p&gt;
  &lt;/section&gt;
  &lt;section style=&quot;background-color:hsl(hsl(236, 74%, var(--autocolor-background-lightness, 95%)), 85%, 85%);&quot;&gt;
    &lt;h2 id=&quot;uSEp&quot; data-align=&quot;center&quot;&gt;Что же будет дальше?&lt;/h2&gt;
  &lt;/section&gt;
  &lt;section style=&quot;background-color:hsl(hsl(0,   0%,  var(--autocolor-background-lightness, 95%)), 85%, 85%);&quot;&gt;
    &lt;p id=&quot;QTFh&quot;&gt;Я думаю в ближайшее время люди начнут повторять успех жито, ведь для их успеха им всего лишь нужно:&lt;/p&gt;
    &lt;ol id=&quot;07xW&quot;&gt;
      &lt;li id=&quot;LVYe&quot;&gt;иметь дохуя денег, чтобы их застейкать в нескольких валидаторах&lt;/li&gt;
      &lt;li id=&quot;tQYZ&quot;&gt;переписать валидатор, чтобы удерживать транзакции и передавать их по какому-нибудь протоколу коммуникации с ботом и получать бандлы обратно&lt;/li&gt;
    &lt;/ol&gt;
    &lt;p id=&quot;SmYJ&quot;&gt;казалось бы, ТАКАЯ СЕТЬ, КАК ТАКОЕ МОЖЕТ БЫТЬ, КАК ЕЕ МОГУТ ТАК ЛЕГКО ПРИВЕСТИ К ЦЕНТРАЛИЗАЦИИ? а вот... вот такое вот случается...&lt;/p&gt;
  &lt;/section&gt;
  &lt;p id=&quot;ICBT&quot;&gt;ДЛЯ ВАС СТАРАЛСЯ - &lt;a href=&quot;https://t.me/dolbaebskicode&quot; target=&quot;_blank&quot;&gt;https://t.me/dolbaebskicode&lt;/a&gt;&lt;/p&gt;

</content></entry><entry><id>paranoikcodit:ethereum-act-one</id><link rel="alternate" type="text/html" href="https://teletype.in/@paranoikcodit/ethereum-act-one?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=paranoikcodit"></link><title>Блокчейн эфириум, контракт нфтишек и ебучий питон... или как написать свой BUYER для эфира. АКТ ПЕРВЫЙ</title><published>2022-11-02T13:35:31.694Z</published><updated>2022-11-04T10:33:24.630Z</updated><summary type="html">&lt;img src=&quot;https://img1.teletype.in/files/43/88/4388a26d-10ad-47bf-9c7a-31a8807b0289.png&quot;&gt;Что же такое эфириум минты? - Этим вопросом не задаются грамотные люди, ибо знают, что минтеры - пидорасы.</summary><content type="html">
  &lt;section style=&quot;background-color:hsl(hsl(170, 33%, var(--autocolor-background-lightness, 95%)), 85%, 85%);&quot;&gt;
    &lt;p id=&quot;DdSl&quot;&gt;Что же такое эфириум минты? - Этим вопросом не задаются грамотные люди, ибо знают, что минтеры - пидорасы.&lt;/p&gt;
  &lt;/section&gt;
  &lt;p id=&quot;qNbN&quot;&gt;Набрав в поисковике лучшей социальной сети ВКОНТАКТИ - &amp;quot;что такое эфириум&amp;quot;, получил довольно вразумляющий ответ:&lt;/p&gt;
  &lt;figure id=&quot;cgVt&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://img1.teletype.in/files/43/88/4388a26d-10ad-47bf-9c7a-31a8807b0289.png&quot; width=&quot;168&quot; /&gt;
  &lt;/figure&gt;
  &lt;blockquote id=&quot;lkVX&quot;&gt;&lt;strong&gt;Ethereum&lt;/strong&gt; - это управляемая сообществом технология, на основе которой работает криптовалюта эфир (ETH) и тысячи децентрализованных приложений. &lt;br /&gt;&lt;br /&gt;Изучите Ethereum&lt;br /&gt;&lt;br /&gt; Посмотреть другие варианты на DeepL.com&lt;/blockquote&gt;
  &lt;p id=&quot;QJZR&quot;&gt;Как же он работает эта тварь?&lt;/p&gt;
  &lt;h2 id=&quot;Dq4j&quot;&gt;Блоки и транзакции&lt;/h2&gt;
  &lt;section style=&quot;background-color:hsl(hsl(170, 33%, var(--autocolor-background-lightness, 95%)), 85%, 85%);&quot;&gt;
    &lt;p id=&quot;LXoE&quot;&gt;Блоки делятся на несколько видов: алмазный, золотой, железный, блок дерева кхм...,  - блоки это та часть блокчейна, в которых хранится его информация. Blockchain - цепочка блоков, которые идут последовательно и хранят в себе всю information о транзакциях.&lt;/p&gt;
    &lt;p id=&quot;Z6X2&quot;&gt;Транзакция - хуйня для отправки новых данных в блокчейн, эта залупа может быть, как переводом средств, так и вызовом функций из разных контрактов или сообщением ебучего ламера, который выебал мамку разработчиков.&lt;/p&gt;
  &lt;/section&gt;
  &lt;p id=&quot;UxXB&quot;&gt;В блоках хранятся транзакции, в транзакциях информация о всех действиях участников блокчейна.&lt;/p&gt;
  &lt;h3 id=&quot;tRao&quot;&gt;Транзакции подробно&lt;/h3&gt;
  &lt;hr /&gt;
  &lt;p id=&quot;IBot&quot;&gt;Для следующих тем нужно знать несколько понятий: &lt;/p&gt;
  &lt;ol id=&quot;SBdP&quot;&gt;
    &lt;li id=&quot;4SFm&quot;&gt;Ethereum - нативная монета, которую мы отдаём уёбкам(ныне называются валидаторами) чтобы они наши транзы подтвердили.&lt;/li&gt;
    &lt;li id=&quot;jxoL&quot;&gt;Gas limit и gas price - дань для ахуевших хуесосов(в простонародии - комиссия), которую мы должны заплатить, чтобы нашу транзу включили в сеть. Gas limit зависит от сложности транзакции(перевод стоит меньше, чем вызов функций контрактов, потому что количество переносимых байтов в транзе меньше, чем вес твоей мамаши), gas price зависит от количество поступаемых пенисов в жопу сети(&lt;a href=&quot;https://etherscan.io/gastracker&quot; target=&quot;_blank&quot;&gt;Чекать тут&lt;/a&gt;). &lt;/li&gt;
    &lt;li id=&quot;xLBD&quot;&gt;Data - данные, которые передаются в контракт. &lt;/li&gt;
  &lt;/ol&gt;
  &lt;p id=&quot;i1kq&quot;&gt;бля, а что нахуй такое эти ваши контракты?&lt;/p&gt;
  &lt;h2 id=&quot;abVh&quot;&gt;Смарт-контрактики&lt;/h2&gt;
  &lt;section style=&quot;background-color:hsl(hsl(170, 33%, var(--autocolor-background-lightness, 95%)), 85%, 85%);&quot;&gt;
    &lt;p id=&quot;NLQ6&quot;&gt;Смарт-контракт это аппка в блокчейне, у которой есть какой-то пул функций и стор. токены, nft, дексы - всё это работает через них.&lt;/p&gt;
  &lt;/section&gt;
  &lt;p id=&quot;E0CZ&quot;&gt;Вот, к примеру, метод перевода в контракте юэсдитишки, который мы каждый раз используем когда хотим перевести свои стейблы&lt;/p&gt;
  &lt;figure id=&quot;4bI0&quot; class=&quot;m_column&quot;&gt;
    &lt;img src=&quot;https://img2.teletype.in/files/19/7d/197d6478-736e-48ca-9d16-96263fb039b5.png&quot; width=&quot;1364&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;dVAb&quot;&gt;У нас есть 3 аргумента:&lt;strong&gt;&lt;code&gt;от кого&lt;/code&gt;&lt;/strong&gt;, &lt;code&gt;кому&lt;/code&gt; и &lt;code&gt;сколько&lt;/code&gt; - аргументы.&lt;/p&gt;
  &lt;p id=&quot;elhi&quot;&gt;Конечный результат вызова функции, так называемая INPUT DATA, штука, которая хранит в себе: нейм вызываемой функции и аргументы:&lt;/p&gt;
  &lt;figure id=&quot;MKA7&quot; class=&quot;m_column&quot;&gt;
    &lt;img src=&quot;https://img3.teletype.in/files/e8/4a/e84a965a-b02d-48d6-b346-7c46146a4f63.png&quot; width=&quot;1364&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;WyqN&quot;&gt;Если не заглядывать под капот - компьютеру куда проще читать такую хуйню, нежели читаемый текст. в будущем объясню, как эта хуйня воркает, а пока что держи в голове, что любой вызов функции выглядит так.&lt;/p&gt;
  &lt;p id=&quot;r0kO&quot;&gt;&lt;/p&gt;
  &lt;section style=&quot;background-color:hsl(hsl(170, 33%, var(--autocolor-background-lightness, 95%)), 85%, 85%);&quot;&gt;
    &lt;p id=&quot;jjhS&quot;&gt;Input data - поле транзакции, в котором передаётся информация о том, какую функцию мы хотим вызвать и какие аргументы в неё передать. В переводе USDT мы используем функцию transferFrom и передаём аргументы from, to, value(от кого, кому и сколько). Но в целом, туда можно написать что угодно при желании, например что-то о матери битка???&lt;/p&gt;
  &lt;/section&gt;
  &lt;h2 id=&quot;oWBw&quot;&gt;Вывод&lt;/h2&gt;
  &lt;section style=&quot;background-color:hsl(hsl(0,   0%,  var(--autocolor-background-lightness, 95%)), 85%, 85%);&quot;&gt;
    &lt;p id=&quot;Zi8r&quot;&gt;В целом, краткий ликбез в эфир закончен, вы успешно завершили первый акт познания эфириума.&lt;/p&gt;
  &lt;/section&gt;
  &lt;p id=&quot;m2wL&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;pKBE&quot;&gt;Авторы:&lt;/p&gt;
  &lt;p id=&quot;ItnN&quot;&gt;&lt;a href=&quot;https://t.me/leviathan_hell&quot; target=&quot;_blank&quot;&gt;Левиафан&lt;/a&gt; / &lt;a href=&quot;https://t.me/dolbaebskicode&quot; target=&quot;_blank&quot;&gt;Параноик&lt;/a&gt;&lt;/p&gt;

</content></entry><entry><id>paranoikcodit:ethereum-rust-transaction-part2</id><link rel="alternate" type="text/html" href="https://teletype.in/@paranoikcodit/ethereum-rust-transaction-part2?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=paranoikcodit"></link><title>Эфириум в Расте? Часть вторая</title><published>2022-08-12T18:33:38.938Z</published><updated>2022-08-13T14:51:50.510Z</updated><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://img4.teletype.in/files/fd/56/fd56c7d0-07b1-483a-a8b1-b61ca58a081f.png"></media:thumbnail><tt:hashtag>rust</tt:hashtag><tt:hashtag>ethereum</tt:hashtag><tt:hashtag>mempool</tt:hashtag><summary type="html">&lt;img src=&quot;https://img1.teletype.in/files/0b/f4/0bf4c4fb-459e-4b09-aa39-e45a55c36348.png&quot;&gt;В этом руководстве показано, как законнектиться к WebSocket ноде Эфира, генерировать кошельки, подписывать сообщения, чтение мемпула.</summary><content type="html">
  &lt;figure id=&quot;CCXf&quot; class=&quot;m_custom&quot;&gt;
    &lt;img src=&quot;https://img1.teletype.in/files/0b/f4/0bf4c4fb-459e-4b09-aa39-e45a55c36348.png&quot; width=&quot;1027.5275895450145&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;A5hk&quot;&gt;В этом руководстве показано, как законнектиться к WebSocket ноде Эфира, генерировать кошельки, подписывать сообщения, чтение мемпула.&lt;/p&gt;
  &lt;section style=&quot;background-color:hsl(hsl(0, 0%, var(--autocolor-background-lightness, 95%)), 85%, 85%);&quot;&gt;
    &lt;h2 id=&quot;FkTa&quot; data-align=&quot;center&quot;&gt;Окружение&lt;/h2&gt;
  &lt;/section&gt;
  &lt;p id=&quot;5ij9&quot;&gt;Про то как скачать и установить раст было в &lt;a href=&quot;https://teletype.in/@paranoikcodit/ethereum-rust-transaction#eCzt&quot; target=&quot;_blank&quot;&gt;прошлой части,&lt;/a&gt; так что останавливаться и ждать балласт не будем.&lt;/p&gt;
  &lt;section style=&quot;background-color:hsl(hsl(0, 0%, var(--autocolor-background-lightness, 95%)), 85%, 85%);&quot;&gt;
    &lt;h2 id=&quot;SwcG&quot; data-align=&quot;center&quot;&gt;Зависимости&lt;/h2&gt;
  &lt;/section&gt;
  &lt;p id=&quot;rLOI&quot;&gt;Теперь мы создадим проект Rust и добавим необходимые зависимости.&lt;/p&gt;
  &lt;p id=&quot;bwt1&quot;&gt;Создайте папку нашего проекта и инициализируйте его с помощью &lt;code&gt;cargo&lt;/code&gt;:&lt;/p&gt;
  &lt;pre data-lang=&quot;bash&quot; id=&quot;srPA&quot;&gt;mkdir rust-ethereum-tutorial-part2
cd rust-ethereum-tutorial-part2
cargo init&lt;/pre&gt;
  &lt;p id=&quot;GCfB&quot;&gt;Для удобства добавления зависимостей Rust в наш проект мы будем использовать &lt;code&gt;cargo-edit&lt;/code&gt;:&lt;/p&gt;
  &lt;pre id=&quot;erbw&quot;&gt;cargo install cargo-edit&lt;/pre&gt;
  &lt;p id=&quot;g4jN&quot;&gt;Добавим необходимые модули:&lt;/p&gt;
  &lt;pre id=&quot;917R&quot;&gt;cargo add tokio eyre ethers +legacy&lt;/pre&gt;
  &lt;section style=&quot;background-color:hsl(hsl(0, 0%, var(--autocolor-background-lightness, 95%)), 85%, 85%);&quot;&gt;
    &lt;h3 id=&quot;6zWP&quot; data-align=&quot;center&quot;&gt;Подключение к WebSocket Ноде эфира.&lt;/h3&gt;
  &lt;/section&gt;
  &lt;p id=&quot;PCjL&quot;&gt;В отличие от прошлого туториала, конструкция подключения будет немного иная:&lt;/p&gt;
  &lt;pre id=&quot;vLUA&quot; data-lang=&quot;rust&quot;&gt;use ethers::prelude::*;

#[tokio::main]
async fn main() -&amp;gt; eyre::Result&amp;lt;()&amp;gt; {
    // Подключаемся ноде, создаем новый экземпляр провайдера.
    let client = Provider::new(
        Ws::connect(&amp;quot;wss://eth-mainnet.g.alchemy.com/v2/rhHsBRIaD9TsoHdKpnymrJimBxPwv5Xp&amp;quot;).await?,
    );
    
    Ok(()) // Пустое возвращаемое значение
}&lt;/pre&gt;
  &lt;section style=&quot;background-color:hsl(hsl(0, 0%, var(--autocolor-background-lightness, 95%)), 85%, 85%);&quot;&gt;
    &lt;h3 id=&quot;HL8t&quot; data-align=&quot;center&quot;&gt;Создание кошелька&lt;/h3&gt;
  &lt;/section&gt;
  &lt;p id=&quot;P7M4&quot;&gt;Теперь когда клиент создан, можно заняться хуйней, то есть сгенерировать новый кошелек:&lt;/p&gt;
  &lt;pre id=&quot;D4lp&quot; data-lang=&quot;rust&quot;&gt;    let wallet = LocalWallet::new(&amp;amp;mut rand::thread_rng()); // Генерация рандомного кошелька
    let address = wallet.address(); // Получение адреса кошелька

    println!(&amp;quot;ебать привет {:?}&amp;quot;, address);&lt;/pre&gt;
  &lt;section style=&quot;background-color:hsl(hsl(0, 0%, var(--autocolor-background-lightness, 95%)), 85%, 85%);&quot;&gt;
    &lt;h3 id=&quot;1bcN&quot; data-align=&quot;center&quot;&gt;Подпись сообщения&lt;/h3&gt;
  &lt;/section&gt;
  &lt;p id=&quot;9xIC&quot;&gt;Так как мы не долбаебы, то лучше всего изучить какие-то доп. возможности объекта кошелька.&lt;/p&gt;
  &lt;p id=&quot;d7xG&quot;&gt;Одна из возможностей подпись сообщения:&lt;/p&gt;
  &lt;pre id=&quot;mKbI&quot; data-lang=&quot;rust&quot;&gt;    let signature = wallet.sign_message(&amp;quot;Я пидор&amp;quot;).await?; // Создание сигнатуры

    println!(&amp;quot;{}&amp;quot;, signature);&lt;/pre&gt;
  &lt;p id=&quot;0q9Q&quot;&gt;Сигнатура подписанного сообщения есть, но как теперь ее верефать?&lt;/p&gt;
  &lt;p id=&quot;kEe9&quot;&gt;Для такого есть метод &lt;code&gt;verify&lt;/code&gt;, который на вход получает возможное сообщение и адрес кошелька. &lt;/p&gt;
  &lt;p id=&quot;DOuz&quot;&gt;Попробуем наебать систему и вписать какую-то хуйню:&lt;/p&gt;
  &lt;pre id=&quot;I75H&quot; data-lang=&quot;rust&quot;&gt;    match signature.verify(&amp;quot;Я не пидор&amp;quot;, address) {
        Err(_) =&amp;gt; println!(&amp;quot;ХХИХХИХИ, ТЫ ЧО ПИДОР?))&amp;quot;),
        Ok(_) =&amp;gt; println!(&amp;quot;ЧОО ТЫ НЕ ПИДОР?&amp;quot;),
    };&lt;/pre&gt;
  &lt;p id=&quot;6wXK&quot;&gt;Запустив наше детище в ебло прилетит сообщение&lt;code&gt; ХХИХХИХИ, ТЫ ЧО ПИДОР?))&lt;/code&gt;&lt;/p&gt;
  &lt;p id=&quot;DKiJ&quot;&gt;Так, а теперь попробуем ввести правильное значение, которое соответствует подписанному сообщению:&lt;/p&gt;
  &lt;pre id=&quot;vinM&quot; data-lang=&quot;rust&quot;&gt;    match signature.verify(&amp;quot;Я пидор&amp;quot;, address) {
        Ok(_) =&amp;gt; println!(&amp;quot;ЧЧООО ПИДОР??&amp;quot;),
        Err(_) =&amp;gt; println!(&amp;quot;НЕЕЕЕЕЕЕ ПИДОР???&amp;quot;),
    };&lt;/pre&gt;
  &lt;p id=&quot;Fat4&quot;&gt;Что же получится при запуске? - Выведется &lt;code&gt;ЧЧООО ПИДОР??&lt;/code&gt;&lt;/p&gt;
  &lt;section style=&quot;background-color:hsl(hsl(0, 0%, var(--autocolor-background-lightness, 95%)), 85%, 85%);&quot;&gt;
    &lt;h3 id=&quot;XuC7&quot; data-align=&quot;center&quot;&gt;Чтение мемпула&lt;/h3&gt;
  &lt;/section&gt;
  &lt;p id=&quot;CGSf&quot;&gt;Чтобы полностью овладеть БЛОКЧЕЙНОМ ЭФИРОМ и стать настоящим Терном, нужно научиться читать мемпул.&lt;/p&gt;
  &lt;section style=&quot;background-color:hsl(hsl(170, 33%, var(--autocolor-background-lightness, 95%)), 85%, 85%);&quot;&gt;
    &lt;p id=&quot;VXP0&quot;&gt;&lt;strong&gt;Mempool&lt;/strong&gt; — очень важная часть сети Bitcoin (сеть компьютеров и устройств, подключенных к Интернет и работающих с программным обеспечением Bitcoin Core). Данные, хранящиеся в мемпуле — это неподтвержденные транзакции, которые в настоящее время застряли в сети. Каждая часть мемпула хранится на специальном электронном устройстве и за его содержимым можно наблюдать в режиме реального времени. Чтобы объяснить, как это работает, сначала нужно разобраться, что происходит во время транзакции?&lt;/p&gt;
    &lt;p id=&quot;NzEi&quot;&gt;(я спиздил определение хз нахуя но мне похуй)&lt;/p&gt;
  &lt;/section&gt;
  &lt;p id=&quot;eBTr&quot;&gt;ТАААК, чтение мемпула дело простое, но требует некоторых действий.&lt;/p&gt;
  &lt;p id=&quot;EUpG&quot;&gt;Для начала нужно создать фильтр мемпула, то есть какого типа транзакций будут поступать при чтении. &lt;/p&gt;
  &lt;p id=&quot;ZvV3&quot;&gt;В своем примере я решил хуй забить на фильтрацию и сделать его пустым, то есть любого вида транзакции будут поступать:&lt;/p&gt;
  &lt;pre id=&quot;G6YW&quot; data-lang=&quot;rust&quot;&gt;    let filter = Filter::new();&lt;/pre&gt;
  &lt;p id=&quot;btyj&quot;&gt;Теперь, чтобы начать читать мемпул, нам нужно передать этот своеобразный фильтр в метод &lt;code&gt;subscribe_logs&lt;/code&gt;, который создает новый ручей подписок&lt;/p&gt;
  &lt;figure id=&quot;s5uq&quot; class=&quot;m_custom&quot;&gt;
    &lt;img src=&quot;https://img1.teletype.in/files/86/ed/86ed8142-aef8-43e0-b7f9-6df43b970856.png&quot; width=&quot;616&quot; /&gt;
  &lt;/figure&gt;
  &lt;pre id=&quot;o7h0&quot; data-lang=&quot;rust&quot;&gt;    let mut stream = client.subscribe_logs(&amp;amp;filter).await?;&lt;/pre&gt;
  &lt;p id=&quot;ZW1G&quot;&gt;Ручей получили, а как в нем купаться? Для этого нам потребуется особенная конструкция:&lt;/p&gt;
  &lt;pre id=&quot;aR9G&quot; data-lang=&quot;rust&quot;&gt;    while let Some(log) = stream.next().await {}&lt;/pre&gt;
  &lt;p id=&quot;wujL&quot;&gt;Такая хуета позволяет постоянно получать данные из мемпула.&lt;/p&gt;
  &lt;p id=&quot;vT0O&quot;&gt;Чтобы понять, что находится внутри лога, достаточно ебануть под двум клавишам ctrl+space.&lt;/p&gt;
  &lt;p id=&quot;3L0o&quot;&gt;По аналогии я сделал что-то примерно такое:&lt;/p&gt;
  &lt;pre id=&quot;AuSr&quot; data-lang=&quot;rust&quot;&gt;    while let Some(log) = stream.next().await {
        println!(
            &amp;quot;транзакция {:?}, блок {:?}&amp;quot;,
            log.transaction_hash, log.block_number
        );
    }&lt;/pre&gt;
  &lt;hr /&gt;
  &lt;p id=&quot;xQLI&quot;&gt;Код готов, пришло время запускать:&lt;/p&gt;
  &lt;pre id=&quot;S94D&quot;&gt;[meqy@meqy-hplaptop15seq2xxx eth_rust]$ cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.38s
     Running &amp;#x60;target/debug/eth_rust&amp;#x60;
ебать привет 0xda85e6532cce4869c18025aac2b34386d5c5f8ce
18350d269ad85efee75ee83ed22c84c891dd6209c27868582c9e81a65cc6ad9965002af2fd1c0174ad44bfcacd314ff6af153f1cba02e6e368f25e2d9fc6172c1b
ХХИХХИХИ, ТЫ ЧО ПИДОР?))
ЧЧООО ПИДОР??
^C&lt;/pre&gt;
  &lt;p id=&quot;UuSL&quot;&gt;Дальше мне думать чото лень, так что ждите следующей статьи.&lt;/p&gt;
  &lt;hr /&gt;
  &lt;tt-tags id=&quot;nLEq&quot;&gt;
    &lt;tt-tag name=&quot;rust&quot;&gt;#rust&lt;/tt-tag&gt;
    &lt;tt-tag name=&quot;ethereum&quot;&gt;#ethereum&lt;/tt-tag&gt;
    &lt;tt-tag name=&quot;mempool&quot;&gt;#mempool&lt;/tt-tag&gt;
  &lt;/tt-tags&gt;

</content></entry><entry><id>paranoikcodit:ethereum-rust-transaction</id><link rel="alternate" type="text/html" href="https://teletype.in/@paranoikcodit/ethereum-rust-transaction?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=paranoikcodit"></link><title>Эфириум в Расте? Часть первая: Отправка простых транзакций.</title><published>2022-08-10T16:21:33.923Z</published><updated>2022-08-10T16:21:33.923Z</updated><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://img3.teletype.in/files/29/b6/29b64cdd-1d41-4a57-bb3e-43f9ef62b7e2.png"></media:thumbnail><tt:hashtag>rust</tt:hashtag><tt:hashtag>ethereum</tt:hashtag><tt:hashtag>transaction</tt:hashtag><tt:hashtag>block</tt:hashtag><summary type="html">&lt;img src=&quot;https://img4.teletype.in/files/78/17/781778f2-39c4-4c79-b111-bbc868d9d606.png&quot;&gt;В этом руководстве показано, как запустить локальную виртуальную машину Ethereum (EVM) в Rust, запросить баланс и совершить простые транзакции.</summary><content type="html">
  &lt;figure id=&quot;tqxg&quot; class=&quot;m_custom&quot;&gt;
    &lt;img src=&quot;https://img4.teletype.in/files/78/17/781778f2-39c4-4c79-b111-bbc868d9d606.png&quot; width=&quot;1202&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;2NIe&quot;&gt;В этом руководстве показано, как запустить локальную виртуальную машину &lt;code&gt;Ethereum (EVM)&lt;/code&gt; в Rust, запросить баланс и совершить простые транзакции.&lt;/p&gt;
  &lt;section style=&quot;background-color:hsl(hsl(0, 0%, var(--autocolor-background-lightness, 95%)), 85%, 85%);&quot;&gt;
    &lt;h2 id=&quot;eCzt&quot; data-align=&quot;center&quot;&gt;Настройка зависимостей&lt;/h2&gt;
  &lt;/section&gt;
  &lt;p id=&quot;lOIf&quot;&gt;Прежде чем писать код, мы должны убедиться, что у нас установлены некоторые необходимые инструменты, а именно &lt;code&gt;Rust&lt;/code&gt; и &lt;code&gt;Ganache&lt;/code&gt;.&lt;/p&gt;
  &lt;section style=&quot;background-color:hsl(hsl(0, 0%, var(--autocolor-background-lightness, 95%)), 85%, 85%);&quot;&gt;
    &lt;h3 id=&quot;9GeZ&quot; data-align=&quot;center&quot;&gt;Rust&lt;/h3&gt;
  &lt;/section&gt;
  &lt;p id=&quot;ggIk&quot;&gt;Установите Rust, следуя  &lt;a href=&quot;https://www.rust-lang.org/tools/install&quot; target=&quot;_blank&quot;&gt;&lt;code&gt;инструкциям&lt;/code&gt;&lt;/a&gt;. Или, если вы используете Arch Linux, вы можете следовать &lt;a href=&quot;https://wiki.archlinux.org/title/rust&quot; target=&quot;_blank&quot;&gt;&lt;code&gt;инструкциям здесь&lt;/code&gt;&lt;/a&gt;, чтобы установить Rust.&lt;/p&gt;
  &lt;section style=&quot;background-color:hsl(hsl(0, 0%, var(--autocolor-background-lightness, 95%)), 85%, 85%);&quot;&gt;
    &lt;h3 id=&quot;a9d2&quot; data-align=&quot;center&quot;&gt;ganache-cli&lt;/h3&gt;
  &lt;/section&gt;
  &lt;p id=&quot;5nNE&quot;&gt;&lt;code&gt;&lt;a href=&quot;https://trufflesuite.com/docs/ganache/&quot; target=&quot;_blank&quot;&gt;Ganache&lt;/a&gt;&lt;/code&gt; поможет вам быстро создать среду Ethereum для тестирования. Если вам не нужен весь пакет, вы можете просто установить &lt;code&gt;ganache-cli&lt;/code&gt; с помощью npm,&lt;/p&gt;
  &lt;pre id=&quot;Z2am&quot; data-lang=&quot;powershell&quot;&gt;npm install -g ganache&lt;/pre&gt;
  &lt;p id=&quot;scr8&quot;&gt;Запустите &lt;code&gt;ganache-cli&lt;/code&gt;, чтобы проверить, правильно ли вы его установили.&lt;/p&gt;
  &lt;section style=&quot;background-color:hsl(hsl(0, 0%, var(--autocolor-background-lightness, 95%)), 85%, 85%);&quot;&gt;
    &lt;h2 id=&quot;zzTP&quot; data-align=&quot;center&quot;&gt;Настройка проекта Rust&lt;/h2&gt;
  &lt;/section&gt;
  &lt;p id=&quot;rLOI&quot;&gt;Теперь мы создадим проект Rust и добавим необходимые зависимости. &lt;/p&gt;
  &lt;p id=&quot;bwt1&quot;&gt;Создайте папку нашего проекта и инициализируйте его с помощью &lt;code&gt;cargo&lt;/code&gt;:&lt;/p&gt;
  &lt;pre id=&quot;BaJd&quot; data-lang=&quot;bash&quot;&gt;mkdir rust-ethereum-tutorial
cd rust-ethereum-tutorial
cargo init&lt;/pre&gt;
  &lt;p id=&quot;GCfB&quot;&gt;Для удобства добавления зависимостей Rust в наш проект мы будем использовать &lt;code&gt;cargo-edit&lt;/code&gt;:&lt;/p&gt;
  &lt;pre id=&quot;epHJ&quot;&gt;cargo install cargo-edit&lt;/pre&gt;
  &lt;p id=&quot;g4jN&quot;&gt;Это добавит последнюю версию &lt;code&gt;ethers&lt;/code&gt; в качестве зависимости в &lt;code&gt;Cargo.toml&lt;/code&gt;:&lt;/p&gt;
  &lt;pre id=&quot;8RvU&quot; data-lang=&quot;bash&quot;&gt;cargo add --no-default-features ethers +legacy&lt;/pre&gt;
  &lt;p id=&quot;R2l2&quot;&gt;и еще несколько библиотек:&lt;/p&gt;
  &lt;pre id=&quot;bA2o&quot;&gt;cargo add tokio +full clap hex eyre&lt;/pre&gt;
  &lt;p id=&quot;EJM1&quot;&gt;Краткое описание этих библиотек: &lt;/p&gt;
  &lt;ul id=&quot;ho5i&quot;&gt;
    &lt;li id=&quot;8L7e&quot;&gt;&lt;a href=&quot;https://docs.rs/tokio/&quot; target=&quot;_blank&quot;&gt;&lt;code&gt;tokio&lt;/code&gt;&lt;/a&gt; - позволяет нам использовать асинхронные функции в Rust&lt;/li&gt;
    &lt;li id=&quot;ESxf&quot;&gt;&lt;a href=&quot;https://docs.rs/clap&quot; target=&quot;_blank&quot;&gt;&lt;code&gt;clap&lt;/code&gt;&lt;/a&gt; - для работы с аргументами командной строки&lt;/li&gt;
    &lt;li id=&quot;phTm&quot;&gt;&lt;a href=&quot;https://docs.rs/hex&quot; target=&quot;_blank&quot;&gt;&lt;code&gt;hex&lt;/code&gt;&lt;/a&gt; - для работы с шестнадцатеричными строками &lt;/li&gt;
    &lt;li id=&quot;nDqp&quot;&gt;&lt;a href=&quot;https://docs.rs/eyre&quot; target=&quot;_blank&quot;&gt;&lt;code&gt;eyre&lt;/code&gt;&lt;/a&gt; - это библиотека, которая помогает сократить количество кодового кода при обработке ошибок&lt;/li&gt;
  &lt;/ul&gt;
  &lt;section style=&quot;background-color:hsl(hsl(0, 0%, var(--autocolor-background-lightness, 95%)), 85%, 85%);&quot;&gt;
    &lt;h3 id=&quot;9IbT&quot; data-align=&quot;center&quot;&gt;Подключение к RPC Ethereum Web3&lt;/h3&gt;
  &lt;/section&gt;
  &lt;p id=&quot;4vBb&quot;&gt;Узел &lt;code&gt;Ethereum&lt;/code&gt; обычно предоставляет конечную точку &lt;em&gt;HTTP&lt;/em&gt;, &lt;em&gt;WebSocket&lt;/em&gt; или &lt;em&gt;IPC&lt;/em&gt; для доступа к сети &lt;code&gt;Ethereum&lt;/code&gt;. Для разработки в локальной среде мы можем подключиться к нашей конечной точке, предоставляемой локально запущенным ganache. Чтобы запустить &lt;code&gt;ganache&lt;/code&gt; в Rust, мы можем просто использовать &lt;code&gt;Ganache::new().spawn()&lt;/code&gt;:&lt;/p&gt;
  &lt;pre id=&quot;Szy9&quot; data-lang=&quot;rust&quot;&gt;use ethers::utils::Ganache;
use eyre::Result;#[tokio::main]
async fn main() -&amp;gt; Result&amp;lt;()&amp;gt; {
    // Спавн Ganache экземпляра
    let ganache = Ganache::new().spawn();
    println!(&amp;quot;HTTP Endpoint: {}&amp;quot;, ganache.endpoint());
    Ok(())
}&lt;/pre&gt;
  &lt;p id=&quot;vVrG&quot;&gt;Если мы выполним команду &lt;code&gt;cargo run&lt;/code&gt;, мы увидим &lt;code&gt;URL&lt;/code&gt; конечной точки, выведенный в консоли:&lt;/p&gt;
  &lt;pre id=&quot;lPSN&quot;&gt;...
HTTP Endpoint: http://localhost:46795&lt;/pre&gt;
  &lt;section style=&quot;background-color:hsl(hsl(0, 0%, var(--autocolor-background-lightness, 95%)), 85%, 85%);&quot;&gt;
    &lt;h3 id=&quot;7fN0&quot; data-align=&quot;center&quot;&gt;Доступ к тестовым кошелькам ganache&lt;/h3&gt;
  &lt;/section&gt;
  &lt;p id=&quot;Q5JR&quot;&gt;При каждом запуске Ganache будет использовать случайно генерируемые мнемоники.&lt;/p&gt;
  &lt;blockquote id=&quot;eFd4&quot;&gt;Чтобы сделать наш пример более детерминированным, мы можем настроить его на использование определенную мнемонику:&lt;/blockquote&gt;
  &lt;pre id=&quot;pWbU&quot; data-lang=&quot;rust&quot;&gt;let mnemonic = &amp;quot;gas monster ski craft below illegal discover limit dog bundle bus artefact&amp;quot;;
let ganache = Ganache::new().mnemonic(mnemonic).spawn();&lt;/pre&gt;
  &lt;p id=&quot;B0G6&quot;&gt;Чтобы получить доступ к кошельку, созданному ganache, мы сначала получим закрытые ключи, а затем преобразуем их в экземпляр &lt;code&gt;LocalWallet&lt;/code&gt;:&lt;/p&gt;
  &lt;pre id=&quot;AZwo&quot; data-lang=&quot;rust&quot;&gt;// Получите первый кошелек, управляемый ganache
let wallet: LocalWallet = ganache.keys()[0].clone().into();
let wallet_address: String = wallet.address().encode_hex();
println!(&amp;quot;Default wallet address: {}&amp;quot;, wallet_address);&lt;/pre&gt;
  &lt;section style=&quot;background-color:hsl(hsl(0, 0%, var(--autocolor-background-lightness, 95%)), 85%, 85%);&quot;&gt;
    &lt;h3 id=&quot;SWmu&quot; data-align=&quot;center&quot;&gt;Подключение через JSON RPC&lt;/h3&gt;
  &lt;/section&gt;
  &lt;p id=&quot;AAsQ&quot;&gt;Для взаимодействия с узлом или сетью Ethereum нам потребуется создать клиент, который подключается к конечной точке ganache:&lt;/p&gt;
  &lt;pre id=&quot;Y0tU&quot; data-lang=&quot;rust&quot;&gt;// Провайдер - это клиент Ethereum JsonRPC.
let provider = Provider::try_from(ganache.endpoint())?.interval(Duration::from_millis(10));&lt;/pre&gt;
  &lt;section style=&quot;background-color:hsl(hsl(0, 0%, var(--autocolor-background-lightness, 95%)), 85%, 85%);&quot;&gt;
    &lt;h3 id=&quot;fMSR&quot; data-align=&quot;center&quot;&gt;Запрос баланса по адресу&lt;/h3&gt;
  &lt;/section&gt;
  &lt;p id=&quot;MRar&quot;&gt;Теперь, когда наш клиент подключен к сети Ethereum, мы можем запросить баланс любого адреса в нашем кошельке:&lt;/p&gt;
  &lt;pre id=&quot;PHpC&quot; data-lang=&quot;rust&quot;&gt;// Запросить баланс нашего счета
let first_balance = provider.get_balance(first_address, None).await?;
println!(&amp;quot;Баланс первого адреса кошелька: {}&amp;quot;, first_balance);&lt;/pre&gt;
  &lt;p id=&quot;PrKJ&quot;&gt;Если мы снова запустим программу &lt;code&gt;cargo run&lt;/code&gt;, то узнаем, что по этому адресу у нас есть 1000ETH. Это добавляется программой Ganache автоматически.&lt;/p&gt;
  &lt;p id=&quot;JOii&quot;&gt;Чтобы запросить баланс, используя адрес в формате шестнадцатеричной строки, нам нужно сначала преобразовать строку в тип &lt;code&gt;Address:&lt;/code&gt;&lt;/p&gt;
  &lt;pre id=&quot;RXBw&quot; data-lang=&quot;rust&quot;&gt;// Запросить баланс случайного счета
let other_address_hex = &amp;quot;0xaf206dCE72A0ef76643dfeDa34DB764E2126E646&amp;quot;;
let other_address = &amp;quot;0xaf206dCE72A0ef76643dfeDa34DB764E2126E646&amp;quot;.parse::&amp;lt;Address&amp;gt;()?;
let other_balance = provider.get_balance(other_address, None).await?;
println!(
    &amp;quot;Баланс адреса {}: {}&amp;quot;,
    other_address_hex, other_balance
);&lt;/pre&gt;
  &lt;p id=&quot;cv5L&quot;&gt;Попробуйте &lt;code&gt;cargo run&lt;/code&gt;, и вы увидите, что баланс этого адреса равен нулю:&lt;/p&gt;
  &lt;pre id=&quot;Pzvq&quot;&gt;Баланс адреса 0xaf206dCE72A0ef76643dfeDa34DB764E2126E646: 0&lt;/pre&gt;
  &lt;section style=&quot;background-color:hsl(hsl(0, 0%, var(--autocolor-background-lightness, 95%)), 85%, 85%);&quot;&gt;
    &lt;h3 id=&quot;XEEg&quot; data-align=&quot;center&quot;&gt;Создать простую транзакцию&lt;/h3&gt;
  &lt;/section&gt;
  &lt;p id=&quot;xEzx&quot;&gt;Далее мы создадим простую транзакцию для перевода нескольких токенов Ethereum на другой адрес.&lt;/p&gt;
  &lt;pre id=&quot;zV1S&quot; data-lang=&quot;rust&quot;&gt;// Создайте транзакцию для перевода 1000 wei на &amp;#x60;other_address&amp;#x60;.
let tx = TransactionRequest::pay(other_address, U256::from(1000u64)).from(first_address);
// Отправляем транзакцию и ждем получения
let receipt = provider
    .send_transaction(tx, None)
    .await?
    .log_msg(&amp;quot;Ожидание трансфера&amp;quot;)
    .await?
    .context(&amp;quot;Нет получателя&amp;quot;)?;
    
println!(
    &amp;quot;Транзакция замайнена на блоке {}&amp;quot;,
    receipt.block_number.context(&amp;quot;Не получилось получить блок транзакции&amp;quot;)?
);
println!(
    &amp;quot;Баланс у {} {}&amp;quot;,
    other_address_hex,
    provider.get_balance(other_address, None).await?
);&lt;/pre&gt;
  &lt;p id=&quot;HKd0&quot;&gt;Выполните cargo run, вы получите следующие результаты:&lt;/p&gt;
  &lt;pre id=&quot;Ogc8&quot;&gt;Ожидание трансфера: 
0xff6153310304732bb28856ca3a90ab9c94c5c9e20cf51e8a2803f3483670cd6f
Транзакция замайнена на блоке 1
Баланс у 0xaf206dCE72A0ef76643dfeDa34DB764E2126E646 1000&lt;/pre&gt;
  &lt;hr /&gt;
  &lt;tt-tags id=&quot;X5Bs&quot;&gt;
    &lt;tt-tag name=&quot;rust&quot;&gt;#rust&lt;/tt-tag&gt;
    &lt;tt-tag name=&quot;ethereum&quot;&gt;#ethereum&lt;/tt-tag&gt;
    &lt;tt-tag name=&quot;transaction&quot;&gt;#transaction&lt;/tt-tag&gt;
    &lt;tt-tag name=&quot;block&quot;&gt;#block&lt;/tt-tag&gt;
  &lt;/tt-tags&gt;

</content></entry><entry><id>paranoikcodit:create-rust-web-api</id><link rel="alternate" type="text/html" href="https://teletype.in/@paranoikcodit/create-rust-web-api?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=paranoikcodit"></link><title>Реализация своего АПИ на Rust с помощью Tokio и Wrap</title><published>2022-08-10T12:30:19.199Z</published><updated>2022-08-10T14:55:25.796Z</updated><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://img2.teletype.in/files/5a/54/5a549979-5656-4e78-87ab-caf6389b4cac.png"></media:thumbnail><tt:hashtag>rust</tt:hashtag><tt:hashtag>wrap</tt:hashtag><tt:hashtag>mutex</tt:hashtag><tt:hashtag>arc</tt:hashtag><tt:hashtag>dyn</tt:hashtag><tt:hashtag>server</tt:hashtag><summary type="html">&lt;img src=&quot;https://img2.teletype.in/files/d6/af/d6af85a0-635c-4f28-8bb4-990b1fed9aca.png&quot;&gt;Сейчас вам поведаю о создании своего API сервиса на основе Warp и Tokio. 
Это является перевод данной статьи.  </summary><content type="html">
  &lt;figure id=&quot;0UEK&quot; class=&quot;m_custom&quot;&gt;
    &lt;img src=&quot;https://img2.teletype.in/files/d6/af/d6af85a0-635c-4f28-8bb4-990b1fed9aca.png&quot; width=&quot;1294&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;F9ip&quot;&gt;Сейчас вам поведаю о создании своего API сервиса на основе Warp и Tokio. &lt;br /&gt;Это является перевод &lt;a href=&quot;https://levelup.gitconnected.com/building-an-api-using-warp-and-tokio-26a52173860a&quot; target=&quot;_blank&quot;&gt;данной статьи.&lt;/a&gt;  &lt;/p&gt;
  &lt;hr /&gt;
  &lt;section style=&quot;background-color:hsl(hsl(0, 0%, var(--autocolor-background-lightness, 95%)), 85%, 85%);&quot;&gt;
    &lt;h2 id=&quot;1hQX&quot; data-align=&quot;center&quot;&gt;Структура&lt;/h2&gt;
  &lt;/section&gt;
  &lt;p id=&quot;4uN7&quot;&gt;Прежде чем приступить к написанию кода, нужно немного продумать структуру API. Это поможет определить нужные эндпоинты, контроллеры и способы хранения данных.&lt;/p&gt;
  &lt;h3 id=&quot;U33g&quot;&gt;Роуты&lt;/h3&gt;
  &lt;p id=&quot;mhPD&quot;&gt;Для своего апи я определил два роута&lt;/p&gt;
  &lt;pre id=&quot;ebR3&quot;&gt;/customers
     - GET -&amp;gt; получить список пользователей
     - POST -&amp;gt; создать нового пользователя и добавить инфомацию в хранилище
/customers/{guid}
     - GET -&amp;gt; получить информацию о пользователе
     - POST -&amp;gt; обновить информацию о пользователе
     - DELETE -&amp;gt; удалить пользователя из хранилища&lt;/pre&gt;
  &lt;h3 id=&quot;8vlx&quot;&gt;Обработчики&lt;/h3&gt;
  &lt;p id=&quot;a9pf&quot;&gt;На основе маршрутов мне потребовалось определить несколько обработчиков&lt;/p&gt;
  &lt;pre id=&quot;Fbhu&quot;&gt;list_customers -&amp;gt; вернуть список пользователей
create_customer -&amp;gt; создать нового пользователя и добавить его в базу данных
get_customer -&amp;gt; вернуть информацию о конкретном пользователе
update_customer -&amp;gt; обновить информацию о пользователе 
delete_customer -&amp;gt; удалить пользователя из базы данных&lt;/pre&gt;
  &lt;h3 id=&quot;znFP&quot;&gt;База данныхчто&lt;/h3&gt;
  &lt;p id=&quot;vxua&quot;&gt;Для примера я буду использовать in-memory хранилище.&lt;/p&gt;
  &lt;p id=&quot;4jku&quot;&gt;Чтобы сгенерировать необходимый набор данных я воспользовался &lt;a href=&quot;https://www.mockaroo&quot; target=&quot;_blank&quot;&gt;mockaroo&lt;/a&gt;.&lt;/p&gt;
  &lt;figure id=&quot;Itmt&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://img2.teletype.in/files/9c/ec/9cec6691-2905-4a79-87b3-82f00c3db261.png&quot; width=&quot;797&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;fO23&quot;&gt;Кроме того, модуль для работы с бд должен уметь инициализировать хранилище после запуска сервера.&lt;/p&gt;
  &lt;section style=&quot;background-color:hsl(hsl(0, 0%, var(--autocolor-background-lightness, 95%)), 85%, 85%);&quot;&gt;
    &lt;h2 id=&quot;KhPw&quot; data-align=&quot;center&quot;&gt;Зависимости&lt;/h2&gt;
  &lt;/section&gt;
  &lt;ul id=&quot;zkRb&quot;&gt;
    &lt;li id=&quot;ht1n&quot;&gt;&lt;a href=&quot;https://crates.io/crates/warp&quot; target=&quot;_blank&quot;&gt;Warp&lt;/a&gt; — Фреймворк для создания веб-сервера на Расте.&lt;/li&gt;
    &lt;li id=&quot;3OiW&quot;&gt;&lt;a href=&quot;https://crates.io/crates/tokio&quot; target=&quot;_blank&quot;&gt;Tokio&lt;/a&gt; — Асинхронная среда выполнения для Раста.&lt;/li&gt;
    &lt;li id=&quot;Nn7K&quot;&gt;&lt;a href=&quot;https://crates.io/crates/serde&quot; target=&quot;_blank&quot;&gt;Serde&lt;/a&gt; — Библиотека для де/сериализации данных в типизированные данные Раста.&lt;/li&gt;
  &lt;/ul&gt;
  &lt;section style=&quot;background-color:hsl(hsl(0, 0%, var(--autocolor-background-lightness, 95%)), 85%, 85%);&quot;&gt;
    &lt;h2 id=&quot;FZRF&quot; data-align=&quot;center&quot;&gt;Реализация&lt;/h2&gt;
  &lt;/section&gt;
  &lt;h3 id=&quot;adfb&quot;&gt;Модели&lt;/h3&gt;
  &lt;p id=&quot;ZKZu&quot;&gt;Первое, что я хочу сделать, это определить свою модель пользователя, а также добавить некоторую структуру в код. &lt;/p&gt;
  &lt;p id=&quot;Mvo2&quot;&gt;В &lt;strong&gt;&lt;em&gt;&lt;code&gt;main.rs&lt;/code&gt;&lt;/em&gt; &lt;/strong&gt;определите новый модуль с именем &lt;strong&gt;&lt;em&gt;&lt;code&gt;models&lt;/code&gt;&lt;/em&gt;&lt;/strong&gt; следующим образом:&lt;/p&gt;
  &lt;pre id=&quot;wE5f&quot; data-lang=&quot;rust&quot;&gt;mod models;
fn main() { /* Логика */ }&lt;/pre&gt;
  &lt;p id=&quot;VKfS&quot;&gt;Затем создать новый файл с именем &lt;strong&gt;&lt;em&gt;&lt;code&gt;models.rs&lt;/code&gt;&lt;/em&gt;&lt;/strong&gt; и добавьте следующее:&lt;/p&gt;
  &lt;pre id=&quot;NJiJ&quot; data-lang=&quot;rust&quot;&gt;pub struct Customer {
    pub guid: String,    
    pub first_name: String,
    pub last_name: String,    
    pub email: String,    
    pub address: String,
}&lt;/pre&gt;
  &lt;p id=&quot;v29t&quot;&gt;Так как я разрабатываю API, эта структура данных должна иметь возможность &lt;em&gt;сериализации&lt;/em&gt; и &lt;em&gt;десериализации&lt;/em&gt; JSON. Я также хочу иметь возможность копировать структуру в хранилище данных и из него, не беспокоясь о проверке &lt;em&gt;заимствования&lt;/em&gt;.&lt;/p&gt;
  &lt;p id=&quot;0Rax&quot;&gt;Для этого я добавлю оператор &lt;em&gt;&lt;strong&gt;drive&lt;/strong&gt; &lt;/em&gt;для структуры пользователя&lt;em&gt;, &lt;/em&gt;чтобы использовать пару макросов из библиотеки &lt;strong&gt;&lt;code&gt;Serde&lt;/code&gt;&lt;/strong&gt; и пару из Rust. Сейчас &lt;em&gt;&lt;strong&gt;models.rs&lt;/strong&gt;&lt;/em&gt; выглядит так:&lt;/p&gt;
  &lt;figure id=&quot;kdLz&quot; class=&quot;m_custom&quot;&gt;
    &lt;img src=&quot;https://miro.medium.com/max/565/0*sue-KWrU-R1S1vwg.png&quot; width=&quot;565&quot; /&gt;
  &lt;/figure&gt;
  &lt;h3 id=&quot;WivM&quot;&gt;База данных&lt;/h3&gt;
  &lt;p id=&quot;FzEX&quot;&gt;База данных для этого API будет являться in-memory(то есть находиться в оперативной памяти и после отключения сервера очистится), которая будет являться вектором структуры &lt;em&gt;&lt;code&gt;Customer&lt;/code&gt;. &lt;/em&gt;Но хранилище должно быть общим для всех маршрутов, поэтому я буду использовать &lt;em&gt;смарт поинтеры&lt;a href=&quot;https://doc.rust-lang.org/book/ch15-00-smart-pointers.html&quot; target=&quot;_blank&quot;&gt;[&lt;/a&gt;&lt;a href=&quot;https://doc.rust-lang.ru/book/ch15-00-smart-pointers.html&quot; target=&quot;_blank&quot;&gt;0&lt;/a&gt;&lt;a href=&quot;https://doc.rust-lang.org/book/ch15-00-smart-pointers.html&quot; target=&quot;_blank&quot;&gt;]&lt;/a&gt; Раста&lt;/em&gt; вместе с &lt;em&gt;Mutex&lt;a href=&quot;https://doc.rust-lang.ru/book/ch16-03-shared-state.html#%D0%9C%D1%8C%D1%8E%D1%82%D0%B5%D0%BA%D1%81%D1%8B-%D0%BF%D1%80%D0%B5%D0%B4%D0%BE%D1%81%D1%82%D0%B0%D0%B2%D0%BB%D1%8F%D1%8E%D1%82-%D0%B4%D0%BE%D1%81%D1%82%D1%83%D0%BF-%D0%BA-%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D0%BC-%D0%B8%D0%B7-%D0%BE%D0%B4%D0%BD%D0%BE%D0%B3%D0%BE-%D0%BF%D0%BE%D1%82%D0%BE%D0%BA%D0%B0-%D0%B7%D0%B0-%D1%80%D0%B0%D0%B7&quot; target=&quot;_blank&quot;&gt;[1]&lt;/a&gt;&lt;/em&gt;, чтобы обеспечить &lt;strong&gt;безопасность потоков&lt;/strong&gt;.&lt;/p&gt;
  &lt;p id=&quot;wtn8&quot;&gt;Во-первых, добавим в &lt;strong&gt;&lt;code&gt;main.rs&lt;/code&gt;&lt;/strong&gt; новый модуль &lt;em&gt;db&lt;/em&gt;:&lt;/p&gt;
  &lt;pre id=&quot;i1h1&quot; data-lang=&quot;rust&quot;&gt;mod db;
mod models;
fn main() { /* Логика */ }&lt;/pre&gt;
  &lt;p id=&quot;bxSu&quot;&gt;Теперь создадим новый файл &lt;strong&gt;&lt;code&gt;db.rs&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;
  &lt;p id=&quot;GgJL&quot;&gt;В этом файле нужно сделать несколько вещей, но первое, что нужно сделать, это определить, как будет выглядеть хранилище данных.&lt;/p&gt;
  &lt;p id=&quot;cnSJ&quot;&gt;Наше простое хранилище это лишь вектор структур &lt;em&gt;&lt;strong&gt;&lt;code&gt;Customer&lt;/code&gt;,&lt;/strong&gt;&lt;/em&gt; но его необходимо обернуть в &lt;strong&gt;&lt;em&gt;thread safe&lt;/em&gt;&lt;/strong&gt; ссылку, чтобы иметь возможность использовать несколько ссылок на хранилище данных в нескольких асинхронных обработчиках.&lt;/p&gt;
  &lt;p id=&quot;AGuy&quot;&gt;Добавим примерно такое в &lt;strong&gt;&lt;em&gt;&lt;code&gt;db.rs&lt;/code&gt;&lt;/em&gt;&lt;/strong&gt;:&lt;/p&gt;
  &lt;pre id=&quot;euEh&quot; data-lang=&quot;rust&quot;&gt;use std::sync::Arc;
use tokio::sync::Mutex;

use crate::models::Customer;

pub type Db = Arc&amp;lt;Mutex&amp;lt;Vec&amp;lt;Customer&amp;gt;&amp;gt;&amp;gt;;&lt;/pre&gt;
  &lt;p id=&quot;8Pl5&quot;&gt;Теперь, когда мы определили структуру хранилища данных, нам нужен способ его инициализации. Инициализация хранилища данных имеет два результата: либо пустое хранилище данных, либо хранилище данных, наполненное данными из файла.&lt;/p&gt;
  &lt;p id=&quot;3S5o&quot;&gt;Инициализация пустого хранилища довольно проста:&lt;/p&gt;
  &lt;pre id=&quot;wv3e&quot; data-lang=&quot;rust&quot;&gt;pub fn init_db() -&amp;gt; Db {
    Arc::new(Mutex::new(Vec::new()))
}&lt;/pre&gt;
  &lt;p id=&quot;jRSd&quot;&gt;Но чтобы получить данные из файла, нам придется добавить еще одну зависимость:&lt;/p&gt;
  &lt;ul id=&quot;IiJ6&quot;&gt;
    &lt;li id=&quot;3846&quot;&gt;&lt;a href=&quot;https://crates.io/crates/serde_json&quot; target=&quot;_blank&quot;&gt;serde_json&lt;/a&gt; — Для чтения необработанного JSON&lt;/li&gt;
  &lt;/ul&gt;
  &lt;p id=&quot;8Izx&quot;&gt;Добавим его в &lt;code&gt;Cargo.toml&lt;/code&gt;:&lt;/p&gt;
  &lt;pre id=&quot;SNqw&quot;&gt;serde_json = &amp;quot;1.0&amp;quot;&lt;/pre&gt;
  &lt;p id=&quot;vpdh&quot;&gt;Теперь мы можем изменить &lt;code&gt;db.rs&lt;/code&gt; следующим образом:&lt;/p&gt;
  &lt;pre id=&quot;9a27&quot; data-lang=&quot;rust&quot;&gt;use std::fs::File;
use serde_json::from_reader;

pub fn init_db() -&amp;gt; Db {
    let file = File::open(&amp;quot;./data/customers.json&amp;quot;);
    match file =&amp;gt; {
        Ok(json) =&amp;gt; {
            let customers = from_reader(json).unwrap();
            Arc::new(Mutex::new(customers))
        },
        Err(_) =&amp;gt; {
            Arc::new(Mutex::new(Vec::new()))
        }
    }
}&lt;/pre&gt;
  &lt;p id=&quot;nZOI&quot;&gt;Эта функция пытается прочитать файл &lt;code&gt;./data/customers.json&lt;/code&gt;. В случае успеха функция возвращает хранилище данных, загруженное данными клиента, в противном случае она возвращает пустой вектор.&lt;/p&gt;
  &lt;p id=&quot;FaDd&quot;&gt;&lt;code&gt;db.rs&lt;/code&gt; должен выглядеть примерно так:&lt;/p&gt;
  &lt;figure id=&quot;tSFm&quot; class=&quot;m_custom&quot;&gt;
    &lt;img src=&quot;https://miro.medium.com/max/628/0*VkFMRfyUv7oWLHhL.png&quot; width=&quot;628&quot; /&gt;
  &lt;/figure&gt;
  &lt;section style=&quot;background-color:hsl(hsl(0, 0%, var(--autocolor-background-lightness, 95%)), 85%, 85%);&quot;&gt;
    &lt;h2 id=&quot;360s&quot; data-align=&quot;center&quot;&gt;Обработчики&lt;/h2&gt;
  &lt;/section&gt;
  &lt;p id=&quot;e5pp&quot;&gt;На текущем этапе мы имеем модели и настройку базы данных. Теперь у нас нужда в общем связывающем инструменте. Тут в дело вступают обработчики.&lt;/p&gt;
  &lt;p id=&quot;WwZu&quot;&gt;Создадим файл &lt;code&gt;handlers.rs&lt;/code&gt; и определим его, как модуль в файле &lt;code&gt;main.rs:&lt;/code&gt;&lt;/p&gt;
  &lt;pre id=&quot;HY5s&quot; data-lang=&quot;rust&quot;&gt;mod handlers;&lt;/pre&gt;
  &lt;p id=&quot;Bcl7&quot;&gt;Добавим в handlers.rs несколько импортов:&lt;/p&gt;
  &lt;pre id=&quot;1H71&quot; data-lang=&quot;rust&quot;&gt;use std::convert::Infallible;
use warp;

use crate::models::Customer;
use crate::db::Db;&lt;/pre&gt;
  &lt;p id=&quot;K90o&quot;&gt;Этот кусок позволяет обращаться к примитивам &lt;code&gt;Customer&lt;/code&gt; и &lt;code&gt;Db&lt;/code&gt;, которые мы определили ранее, из модуля обработчиков. Также тут мы импортируем модуль &lt;code&gt;warp&lt;/code&gt; и перечисление &lt;code&gt;&lt;a href=&quot;https://doc.rust-lang.org/std/convert/enum.Infallible.html&quot; target=&quot;_blank&quot;&gt;Infallible&lt;/a&gt;&lt;/code&gt;, которое является типом ошибок, что никогда не могут произойти.&lt;/p&gt;
  &lt;h3 id=&quot;cE4j&quot;&gt;Список пользователей&lt;/h3&gt;
  &lt;p id=&quot;CuKV&quot;&gt;Обработчик &lt;code&gt;list_customers&lt;/code&gt;вобработчик принимает на вход ссылку на хранилище и возвращает &lt;code&gt;Result&lt;/code&gt;, который оборачивает JSON-ответ.&lt;/p&gt;
  &lt;pre id=&quot;rlPw&quot; data-lang=&quot;rust&quot;&gt;pub async fn list_customers(db: Db) -&amp;gt; Result&amp;lt;impl warp::Reply, Infallible&amp;gt; { 
   // ...  
} &lt;/pre&gt;
  &lt;p id=&quot;1BfU&quot;&gt;В теле функции нужно реализовать получение данных из хранилища и сериализация их в JSON объект. Для удобства &lt;code&gt;wrap&lt;/code&gt; предоставляет функцию, которая преобразовывает вектор в JSON объект.&lt;/p&gt;
  &lt;figure id=&quot;ds6W&quot; class=&quot;m_custom&quot;&gt;
    &lt;img src=&quot;https://miro.medium.com/max/700/0*uuRtqjJYa_2q87XG.png&quot; width=&quot;700&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;Sa4Z&quot;&gt;Строчка &lt;code&gt;let customers = db.lock().await;&lt;/code&gt; дожидается блокировки выполнения задачи, чтобы можно было безопасно обратиться к хранилищу.&lt;/p&gt;
  &lt;p id=&quot;cXFN&quot;&gt;Строка &lt;code&gt;let customers: Vec&amp;lt;Customer&amp;gt; = customers.clone()&lt;/code&gt; клонирует вектор из &lt;code&gt;MutexGuard.&lt;/code&gt;&lt;/p&gt;
  &lt;p id=&quot;T3cQ&quot;&gt;Последняя строка &lt;code&gt;Ok(warp::reply::json(&amp;amp;customers))&lt;/code&gt; оборачивает вектор в JSON объект и возвращает его.&lt;/p&gt;
  &lt;h3 id=&quot;FBH6&quot;&gt;Создание пользователя&lt;/h3&gt;
  &lt;p id=&quot;EyBB&quot;&gt;Обработчик &lt;code&gt;create_customer&lt;/code&gt; принимает на вход объект &lt;code&gt;Customer&lt;/code&gt; и ссылку на хранилище. В случае успешного создания возвращает статус код &lt;code&gt;CREATED&lt;/code&gt;, в ином случае &lt;code&gt;BAD_REQUEST.&lt;/code&gt;&lt;/p&gt;
  &lt;p id=&quot;dBus&quot;&gt;Но прежде нужно обновить импорты &lt;code&gt;wrap&lt;/code&gt;&amp;#x27;a в файле &lt;code&gt;handlers.rs&lt;/code&gt;.&lt;/p&gt;
  &lt;p id=&quot;r4JG&quot;&gt;Заменяем строчку &lt;code&gt;use wrap;&lt;/code&gt; на;&lt;/p&gt;
  &lt;pre id=&quot;uSgP&quot; data-lang=&quot;rust&quot;&gt;use warp::{self, http::StatusCode};&lt;/pre&gt;
  &lt;p id=&quot;9d65&quot;&gt;Данное изменение позволит использовать перечисление &lt;code&gt;StatusCode&lt;/code&gt;, как &lt;em&gt;респонс&lt;/em&gt;.&lt;/p&gt;
  &lt;p id=&quot;Yyts&quot;&gt;По аналогии с &lt;code&gt;list_customers&lt;/code&gt; определяем обработчик &lt;code&gt;create_customer&lt;/code&gt; с подобным содержанием:&lt;/p&gt;
  &lt;figure id=&quot;JMZ1&quot; class=&quot;m_custom&quot;&gt;
    &lt;img src=&quot;https://miro.medium.com/max/700/0*bfnu_sZg4bXg1MgK.png&quot; width=&quot;700&quot; /&gt;
  &lt;/figure&gt;
  &lt;h3 id=&quot;h2KQ&quot;&gt;Получение пользователя&lt;/h3&gt;
  &lt;p id=&quot;OBmb&quot;&gt;Обработчик get_customer на вход берет guid определенного пользователя и ссылку на базу данных и возвращает JSON-объект, если пользователь найден, иначе вернет заглушку в виде дефолтного объекта пользователя.&lt;/p&gt;
  &lt;p id=&quot;emEt&quot;&gt;Перед этим добавим еще один макрос в структуру пользователя:&lt;/p&gt;
  &lt;figure id=&quot;P9Tj&quot; class=&quot;m_custom&quot;&gt;
    &lt;img src=&quot;https://miro.medium.com/max/628/0*tIwigBAmb-Hy3eAy.png&quot; width=&quot;628&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;A0KU&quot;&gt;По аналогии с другими обработчиками добавляет что-то подобное в код:&lt;/p&gt;
  &lt;pre id=&quot;Kgye&quot; data-lang=&quot;rust&quot;&gt;pub async fn get_customer(guid: String, db: Db) -&amp;gt; Result&amp;lt;Box&amp;lt;dyn warp::Reply&amp;gt;, Infallible&amp;gt; {
}&lt;/pre&gt;
  &lt;p id=&quot;jiRV&quot;&gt;Возвращаемый примитив немного отличный от других, потому что на выходе мы получаем либо StatusCode, либо JSON-объект. Так как оба этих примитива реализуют warp::Reply, то мы можем использовать &lt;a href=&quot;https://doc.rust-lang.ru/book/ch17-02-trait-objects.html#%D0%9E%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5-%D1%82%D0%B8%D0%BF%D0%B0%D0%B6%D0%B0-%D0%B4%D0%BB%D1%8F-%D0%BE%D0%B1%D1%89%D0%B5%D0%B3%D0%BE-%D0%BF%D0%BE%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D1%8F&quot; target=&quot;_blank&quot;&gt;динамическое приведение&lt;/a&gt; через &lt;code&gt;dyn.&lt;/code&gt;&lt;/p&gt;
  &lt;figure id=&quot;xXhG&quot; class=&quot;m_custom&quot;&gt;
    &lt;img src=&quot;https://miro.medium.com/max/700/0*96M88fxKgbAMTsLz.png&quot; width=&quot;700&quot; /&gt;
  &lt;/figure&gt;
  &lt;h3 id=&quot;tsIQ&quot;&gt;Обновить пользователя&lt;/h3&gt;
  &lt;p id=&quot;CeHO&quot;&gt;Обработчик &lt;code&gt;update_customer&lt;/code&gt; на вход принимает &lt;code&gt;guid&lt;/code&gt; пользователя, измененный объект &lt;code&gt;Customer&lt;/code&gt; и ссылку на &lt;em&gt;бд&lt;/em&gt;. Возвращает &lt;code&gt;OK&lt;/code&gt;, если получилось изменить пользователя, &lt;code&gt;NOT_FOUND&lt;/code&gt; в случае, если нет пользователя в хранилище.&lt;/p&gt;
  &lt;figure id=&quot;BYsC&quot; class=&quot;m_custom&quot;&gt;
    &lt;img src=&quot;https://miro.medium.com/max/700/0*ROhNI2DEvwVwFpp6.png&quot; width=&quot;700&quot; /&gt;
  &lt;/figure&gt;
  &lt;h3 id=&quot;89cS&quot;&gt;Удаление пользователя&lt;/h3&gt;
  &lt;p id=&quot;drxg&quot;&gt;Обработчик &lt;code&gt;delete_customer&lt;/code&gt; на вход принимает &lt;code&gt;guid&lt;/code&gt; пользователя, которого нужно удалить и ссылку на хранилище.&lt;/p&gt;
  &lt;p id=&quot;eiOl&quot;&gt;Если удалось удалить пользователя из базы данных, то функция вернет &lt;code&gt;NO_CONTENT&lt;/code&gt;, иначе &lt;code&gt;NOT_FOUND&lt;/code&gt;.&lt;/p&gt;
  &lt;figure id=&quot;NfPm&quot; class=&quot;m_custom&quot;&gt;
    &lt;img src=&quot;https://miro.medium.com/max/700/0*ngz_PG8k5CD6L2C8.png&quot; width=&quot;700&quot; /&gt;
  &lt;/figure&gt;
  &lt;section style=&quot;background-color:hsl(hsl(0, 0%, var(--autocolor-background-lightness, 95%)), 85%, 85%);&quot;&gt;
    &lt;h2 id=&quot;PiEH&quot; data-align=&quot;center&quot;&gt;Роутинг&lt;/h2&gt;
  &lt;/section&gt;
  &lt;p id=&quot;ayW2&quot;&gt;Теперь у нас все обработчики собраны, теперь присвоим их соответствующим роутам.&lt;/p&gt;
  &lt;p id=&quot;eDkM&quot;&gt;В &lt;code&gt;main.rs&lt;/code&gt; определим еще один модуль:&lt;/p&gt;
  &lt;pre id=&quot;2LU6&quot; data-lang=&quot;rust&quot;&gt;mod routes;&lt;/pre&gt;
  &lt;p id=&quot;yCoR&quot;&gt;Теперь создадим файл &lt;code&gt;routes.rs&lt;/code&gt;:&lt;/p&gt;
  &lt;pre id=&quot;N2sS&quot; data-lang=&quot;rust&quot;&gt;use std::convert::Infallible;
use warp::{self, Filter};

use crate::db::Db;
use crate::handlers;
use crate::models::Customer;&lt;/pre&gt;
  &lt;p id=&quot;ndED&quot;&gt;Сначала нам нужна вспомогательная функция для передачи ссылки на хранилище данных в обработчики из маршрутов.&lt;/p&gt;
  &lt;pre id=&quot;7qkB&quot; data-lang=&quot;rust&quot;&gt;fn with_db(db: Db) -&amp;gt; impl Filter&amp;lt;Extract = (Db,), Error = Infallible&amp;gt; {
    warp::any().map(move || db.clone())
}&lt;/pre&gt;
  &lt;p id=&quot;hwDw&quot;&gt;Функция позволяет инжектить хранилище в роут и передавать его обработчику. Filter это трейт, который предоставляет функциональность для подбора маршрутов, что являются результатом одного или нескольких методов Filter.&lt;/p&gt;
  &lt;h3 id=&quot;8ff9&quot;&gt;GET /customers&lt;/h3&gt;
  &lt;figure id=&quot;Nr3z&quot; class=&quot;m_custom&quot;&gt;
    &lt;img src=&quot;https://miro.medium.com/max/700/0*hFQoRobNfeG2eIvh.png&quot; width=&quot;700&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;jq3x&quot;&gt;Функция возвращает тип, реализующий трейт &lt;code&gt;Filter&lt;/code&gt;. &lt;code&gt;Extract&lt;/code&gt; используется, когда происходит совпадение, и возвращается значение &lt;code&gt;Extract&lt;/code&gt;.&lt;/p&gt;
  &lt;p id=&quot;OiRB&quot;&gt;По сути, функция определяет маршрут, который соответствует запрошенному пути «/customers» и является GET запросом.&lt;/p&gt;
  &lt;p id=&quot;gMTy&quot;&gt;Кроме того, чтобы сохранить некоторую работу в будущем, я реализую еще одну функцию, которая будет служить оболочкой для всех маршрутов пользователей. Позже будет легче, когда мы соединим все вместе.&lt;/p&gt;
  &lt;pre id=&quot;XfDA&quot; data-lang=&quot;rust&quot;&gt;pub fn customer_routes(db: Db) -&amp;gt; impl Filter&amp;lt;Extract = impl warp::Reply, Error = warp::Rejection&amp;gt; + Clone {
    customers_list(db.clone())
}&lt;/pre&gt;
  &lt;h3 id=&quot;29e3&quot;&gt;POST /customers&lt;/h3&gt;
  &lt;p id=&quot;30VW&quot;&gt;Этот маршрут добавит нового клиента в хранилище данных, если он еще не существует.&lt;/p&gt;
  &lt;p id=&quot;XnFb&quot;&gt;Одна вещь, которую нужно добавить, прежде чем мы реализуем функцию для маршрута, — это вспомогательная функция для извлечения JSON из тела POST запроса.&lt;/p&gt;
  &lt;pre id=&quot;ueRb&quot; data-lang=&quot;rust&quot;&gt;fn json_body() -&amp;gt; impl Filter&amp;lt;Extract = (Customer,), Error = warp::Rejection&amp;gt; + Clone {
    warp::body::content_length_limit(1024 * 16)
        .and(warp::body::json())
}&lt;/pre&gt;
  &lt;p id=&quot;ZPNU&quot;&gt;Функция будет очень похожа на &lt;code&gt;customers_list&lt;/code&gt;, за исключением обработчика. Добавьте в &lt;code&gt;route.rs&lt;/code&gt; следующее:&lt;br /&gt;&lt;/p&gt;
  &lt;figure id=&quot;pwHY&quot; class=&quot;m_custom&quot;&gt;
    &lt;img src=&quot;https://miro.medium.com/max/700/0*QO6bpjm4AvFUeDwa.png&quot; width=&quot;700&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;ijU2&quot;&gt;Эта функция определяет маршрут, который соответствует пути «/customers» и является POST запросом. Затем JSON из POST запроса и ссылка на хранилище извлекаются и передаются обработчику.&lt;/p&gt;
  &lt;h3 id=&quot;d4a8&quot;&gt;GET /customers/{guid}&lt;/h3&gt;
  &lt;p id=&quot;lAEm&quot;&gt;Этот маршрут попытается получить одного клиента из хранилища данных. &lt;/p&gt;
  &lt;p id=&quot;QPpb&quot;&gt;В этой функции мы используем макрос &lt;code&gt;path!&lt;/code&gt; из &lt;code&gt;wrap&lt;/code&gt;, который позволяет передавать путь с переменной.&lt;/p&gt;
  &lt;figure id=&quot;ho5k&quot; class=&quot;m_custom&quot;&gt;
    &lt;img src=&quot;https://miro.medium.com/max/700/0*R700W6kAAIAS_sHR.png&quot; width=&quot;700&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;T82t&quot;&gt;Это определяет маршрут, который будет соответствовать «customers/{какое-то значение}» и GET запросу. Затем он извлекает хранилище и передает его обработчику.&lt;/p&gt;
  &lt;p id=&quot;yLZo&quot;&gt;Одна вещь, которую следует учитывать для маршрутов, заключается в том, что наиболее конкретный маршрут должен быть проверен первым, иначе маршрут может не совпасть.&lt;/p&gt;
  &lt;p id=&quot;8Jkl&quot;&gt;Например, если вспомогательная функция для маршрутов обновлена таким образом:&lt;/p&gt;
  &lt;pre id=&quot;LMiv&quot; data-lang=&quot;rust&quot;&gt;pub fn customer_routes(
    db: Db,
) -&amp;gt; impl Filter&amp;lt;Extract = impl warp::Reply, Error = warp::Rejection&amp;gt; + Clone {
    customers_list(db.clone())
        .or(create_customer(db.clone()))
        .or(get_customer(db.clone()))
}&lt;/pre&gt;
  &lt;p id=&quot;whc5&quot;&gt;Маршрут get_customer никогда не будет совпадать, потому что они имеют общий корневой путь — «/customers», что означает, что маршрут списка клиентов будет соответствовать «/customers» и «/customers/{guid}».&lt;/p&gt;
  &lt;p id=&quot;oitk&quot;&gt;Чтобы устранить проблему, упорядочите маршрут так, чтобы наиболее точное совпадение было первым. Как это:&lt;/p&gt;
  &lt;pre id=&quot;B6JG&quot; data-lang=&quot;rust&quot;&gt;pub fn customer_routes(
    db: Db,
) -&amp;gt; impl Filter&amp;lt;Extract = impl warp::Reply, Error = warp::Rejection&amp;gt; + Clone {
    get_customer(db.clone())
        .or(customers_list(db.clone()))
        .or(create_customer(db.clone()))
}&lt;/pre&gt;
  &lt;h3 id=&quot;d4a5&quot;&gt;PUT /customers/{guid}&lt;/h3&gt;
  &lt;p id=&quot;3VNi&quot;&gt;Этот маршрут попытается обновить клиента, если он существует, и вернуть код состояния &lt;code&gt;OK&lt;/code&gt;, в противном случае возвращается код состояния &lt;code&gt;NOT_FOUND&lt;/code&gt;.&lt;/p&gt;
  &lt;p id=&quot;YYM3&quot;&gt;По аналогии с созданием определяем обработчик:&lt;/p&gt;
  &lt;figure id=&quot;KAh9&quot; class=&quot;m_custom&quot;&gt;
    &lt;img src=&quot;https://miro.medium.com/max/700/0*p4qZ4YEyFNYvPJqf.png&quot; width=&quot;700&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;T0iH&quot;&gt;Затем обновим обертку над маршрутом пользователя:&lt;/p&gt;
  &lt;pre id=&quot;fYMW&quot; data-lang=&quot;rust&quot;&gt;pub fn customer_routes(
    db: Db,
) -&amp;gt; impl Filter&amp;lt;Extract = impl warp::Reply, Error = warp::Rejection&amp;gt; + Clone {
    get_customer(db.clone())
        .or(update_customer(db.clone()))
        .or(create_customer(db.clone()))
        .or(customers_list(db))
}&lt;/pre&gt;
  &lt;h3 id=&quot;4ce6&quot;&gt;DELETE /customers/{guid}&lt;/h3&gt;
  &lt;p id=&quot;Xwm2&quot;&gt;Последний маршрут просто удаляет клиента из хранилища данных, если он соответствует заданному &lt;code&gt;guid&lt;/code&gt;, а затем возвращает код состояния &lt;code&gt;NO_CONTENT&lt;/code&gt;, в противном случае возвращается код состояния &lt;code&gt;NOT_FOUND&lt;/code&gt;.&lt;/p&gt;
  &lt;figure id=&quot;lwRO&quot; class=&quot;m_custom&quot;&gt;
    &lt;img src=&quot;https://miro.medium.com/max/700/0*ihEuT6hZ44IwlHAV.png&quot; width=&quot;700&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;HkwI&quot;&gt;Затем обновим оболочку маршрута клиента. После добавления всех маршрутов обертка должна выглядеть так:&lt;/p&gt;
  &lt;figure id=&quot;FjHB&quot; class=&quot;m_custom&quot;&gt;
    &lt;img src=&quot;https://miro.medium.com/max/700/0*26Lt7FDRsebMAWXP.png&quot; width=&quot;700&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;NZOY&quot;&gt;На этом все маршруты заканчиваются. Теперь мы можем перейти к связыванию всего вместе.&lt;/p&gt;
  &lt;section style=&quot;background-color:hsl(hsl(0, 0%, var(--autocolor-background-lightness, 95%)), 85%, 85%);&quot;&gt;
    &lt;h2 id=&quot;QBGV&quot; data-align=&quot;center&quot;&gt;Функция main&lt;/h2&gt;
  &lt;/section&gt;
  &lt;p id=&quot;ParQ&quot;&gt;Файл &lt;code&gt;main.rs&lt;/code&gt; собирает все части кода воедино. Он инициализирует хранилище данных, получает все маршруты и запускает сервер. Это также довольно короткий файл, поэтому я просто покажу его целиком:&lt;/p&gt;
  &lt;figure id=&quot;ts75&quot; class=&quot;m_custom&quot;&gt;
    &lt;img src=&quot;https://miro.medium.com/max/610/0*ll-s_yV799pUOgTA.png&quot; width=&quot;610&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;n1ox&quot;&gt;Мы уже видели первые несколько строк, так что давайте пройдемся по основной функции. &lt;/p&gt;
  &lt;p id=&quot;NJxF&quot;&gt;Атрибут функции &lt;code&gt;#[tokio::main] &lt;/code&gt;устанавливает точку входа для среды выполнения &lt;code&gt;tokio&lt;/code&gt;. Это позволяет нам объявить основную функцию как асинхронную. &lt;/p&gt;
  &lt;p id=&quot;8gcn&quot;&gt;Первые две строки &lt;code&gt;main&lt;/code&gt; — это просто вызовы функций из наших модулей. Первый инициализирует хранилище данных, а второй получает оболочку маршрутов наших клиентов. &lt;/p&gt;
  &lt;p id=&quot;tdd9&quot;&gt;В последней строке используется &lt;code&gt;warp::server&lt;/code&gt; для создания сервера, а затем &lt;code&gt;run&lt;/code&gt; для запуска сервера на указанном хосте и порте. Мы используем ключевое слово &lt;code&gt;await&lt;/code&gt;, чтобы выполнить код до тех пор, пока функция запуска не завершится.&lt;/p&gt;
  &lt;hr /&gt;
  &lt;tt-tags id=&quot;nDxO&quot;&gt;
    &lt;tt-tag name=&quot;rust&quot;&gt;#rust&lt;/tt-tag&gt;
    &lt;tt-tag name=&quot;wrap&quot;&gt;#wrap&lt;/tt-tag&gt;
    &lt;tt-tag name=&quot;mutex&quot;&gt;#mutex&lt;/tt-tag&gt;
    &lt;tt-tag name=&quot;arc&quot;&gt;#arc&lt;/tt-tag&gt;
    &lt;tt-tag name=&quot;dyn&quot;&gt;#dyn&lt;/tt-tag&gt;
    &lt;tt-tag name=&quot;server&quot;&gt;#server&lt;/tt-tag&gt;
  &lt;/tt-tags&gt;

</content></entry><entry><id>paranoikcodit:bMJklbBgQej</id><link rel="alternate" type="text/html" href="https://teletype.in/@paranoikcodit/bMJklbBgQej?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=paranoikcodit"></link><title>Подборка библиотек для персонализации терминальных приложений на Python</title><published>2022-08-09T12:14:11.408Z</published><updated>2022-08-09T22:53:32.229Z</updated><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://img3.teletype.in/files/eb/35/eb3541d9-1e7b-4ecf-85ec-9725fcdb92fc.png"></media:thumbnail><tt:hashtag>python</tt:hashtag><tt:hashtag>питон</tt:hashtag><tt:hashtag>пайтон</tt:hashtag><tt:hashtag>программирование</tt:hashtag><tt:hashtag>textual</tt:hashtag><tt:hashtag>prompt</tt:hashtag><tt:hashtag>toolkit</tt:hashtag><tt:hashtag>teminal</tt:hashtag><tt:hashtag>tui</tt:hashtag><tt:hashtag>terminaluserinterface</tt:hashtag><tt:hashtag>anchor_exampletextuserinterfac</tt:hashtag><summary type="html">&lt;img src=&quot;https://img4.teletype.in/files/71/11/7111ccf9-2b14-4572-9df1-9b97fdc57e24.png&quot;&gt;приветики ^//^, тут представлена небольшая подборка библиотек, которые я так или иначе использовал в своих проектах для кастомизации консольного вывода.</summary><content type="html">
  &lt;figure id=&quot;1pNM&quot; class=&quot;m_custom&quot;&gt;
    &lt;img src=&quot;https://img4.teletype.in/files/71/11/7111ccf9-2b14-4572-9df1-9b97fdc57e24.png&quot; width=&quot;1116&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;8drZ&quot;&gt;приветики ^//^, тут представлена небольшая подборка библиотек, которые я так или иначе использовал в своих проектах для кастомизации консольного вывода.&lt;/p&gt;
  &lt;p id=&quot;MUAN&quot;&gt;&lt;/p&gt;
  &lt;section style=&quot;background-color:hsl(hsl(0, 0%, var(--autocolor-background-lightness, 95%)), 85%, 85%);&quot;&gt;
    &lt;h2 id=&quot;YopW&quot;&gt;Навигация:&lt;/h2&gt;
    &lt;ol id=&quot;7qmt&quot;&gt;
      &lt;li id=&quot;Wv7C&quot;&gt;&lt;strong&gt;&lt;a href=&quot;https://teletype.in/@paranoikcodit/bMJklbBgQej#HdOA&quot; target=&quot;_blank&quot;&gt;asciimatics&lt;/a&gt;&lt;/strong&gt;&lt;/li&gt;
      &lt;li id=&quot;4HDn&quot;&gt;&lt;strong&gt;&lt;a href=&quot;https://teletype.in/@paranoikcodit/bMJklbBgQej#gRLm&quot; target=&quot;_blank&quot;&gt;textual&lt;/a&gt;&lt;/strong&gt;&lt;/li&gt;
      &lt;li id=&quot;PYCC&quot;&gt;&lt;strong&gt;&lt;a href=&quot;https://teletype.in/@paranoikcodit/bMJklbBgQej#JsrG&quot; target=&quot;_blank&quot;&gt;rich&lt;/a&gt;&lt;/strong&gt;&lt;/li&gt;
      &lt;li id=&quot;NYTD&quot;&gt;&lt;strong&gt;&lt;a href=&quot;https://teletype.in/@paranoikcodit/bMJklbBgQej#vyjg&quot; target=&quot;_blank&quot;&gt;questionary&lt;/a&gt;&lt;/strong&gt;&lt;/li&gt;
      &lt;li id=&quot;blEm&quot;&gt;&lt;strong&gt;&lt;a href=&quot;https://teletype.in/@paranoikcodit/bMJklbBgQej#CHqu&quot; target=&quot;_blank&quot;&gt;python-prompt-toolkit&lt;/a&gt;&lt;/strong&gt;&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/section&gt;
  &lt;h2 id=&quot;HdOA&quot; data-align=&quot;center&quot;&gt;1. &lt;a href=&quot;https://github.com/peterbrittain/asciimatics&quot; target=&quot;_blank&quot;&gt;asciimatics&lt;/a&gt;&lt;/h2&gt;
  &lt;section style=&quot;background-color:hsl(hsl(0, 0%, var(--autocolor-background-lightness, 95%)), 85%, 85%);&quot;&gt;
    &lt;figure id=&quot;ZrKE&quot; class=&quot;m_custom&quot;&gt;
      &lt;img src=&quot;https://camo.githubusercontent.com/8e41949c9c884aea639c7a6e1c63cb74fbc72c22d8b53fdc3f735162e306d098/68747470733a2f2f61736369696e656d612e6f72672f612f31383735362e706e67&quot; width=&quot;599.1198769918926&quot; /&gt;
    &lt;/figure&gt;
    &lt;p id=&quot;lQOA&quot;&gt;&lt;strong&gt;&lt;em&gt;Asciimatics&lt;/em&gt;&lt;/strong&gt; — это пакет, помогающий людям создавать полноэкранные &lt;u&gt;текстовые пользовательские интерфейсы&lt;/u&gt; (от интерактивных форм до анимации ASCII) на любой платформе. Он распространяется под лицензией Apache Software Foundation License 2.0.&lt;/p&gt;
    &lt;p id=&quot;ozOY&quot;&gt;Является безусловным &lt;strong&gt;фаворитом&lt;/strong&gt; в больших проектах, которые используют консоль, как ui. Имеет &lt;em&gt;не очень&lt;/em&gt; сложную структуру, &lt;em&gt;приятный&lt;/em&gt; отклик, возможность анимировать объекты, а также большой ассортимент &lt;em&gt;винджетов&lt;/em&gt; и &lt;em&gt;контейнеров&lt;/em&gt;.&lt;/p&gt;
  &lt;/section&gt;
  &lt;h2 id=&quot;gRLm&quot; data-align=&quot;center&quot;&gt;2. &lt;a href=&quot;https://github.com/Textualize/textual&quot; target=&quot;_blank&quot;&gt;textual&lt;/a&gt;&lt;/h2&gt;
  &lt;section style=&quot;background-color:hsl(hsl(0, 0%, var(--autocolor-background-lightness, 95%)), 85%, 85%);&quot;&gt;
    &lt;figure id=&quot;RU4S&quot; class=&quot;m_original&quot;&gt;
      &lt;img src=&quot;https://github.com/Textualize/textual/raw/main/imgs/textual.png&quot; width=&quot;1280&quot; /&gt;
    &lt;/figure&gt;
    &lt;p id=&quot;1yoD&quot;&gt;&lt;strong&gt;Textual&lt;/strong&gt; — это полноценный фреймворк TUI (текстовый пользовательский интерфейс) для Python, вдохновленный современной веб-разработкой. &lt;/p&gt;
    &lt;p id=&quot;yg2w&quot;&gt;Все обработчики событий &lt;em&gt;асинхронные&lt;/em&gt;, из-за чего все виджеты могут &lt;em&gt;независимо обновляться&lt;/em&gt;, а также умеют общаться &lt;u&gt;друг с другом&lt;/u&gt; &lt;em&gt;отправкой сообщений&lt;/em&gt;.&lt;/p&gt;
    &lt;p id=&quot;4KYw&quot;&gt;Имеется также поддержка &lt;em&gt;кастомизации&lt;/em&gt; виджетов средствами &lt;strong&gt;CSS-стилей&lt;/strong&gt;. Также использует некоторые техники позаимствованные из &lt;em&gt;вью&lt;/em&gt; и других &lt;em&gt;веб-фреймворков&lt;/em&gt;. &lt;/p&gt;
  &lt;/section&gt;
  &lt;h2 id=&quot;JsrG&quot; data-align=&quot;center&quot;&gt;&lt;strong&gt;3. &lt;a href=&quot;https://github.com/Textualize/rich&quot; target=&quot;_blank&quot;&gt;rich&lt;/a&gt;&lt;/strong&gt;&lt;/h2&gt;
  &lt;section style=&quot;background-color:hsl(hsl(0, 0%, var(--autocolor-background-lightness, 95%)), 85%, 85%);&quot;&gt;
    &lt;figure id=&quot;og4q&quot; class=&quot;m_original&quot;&gt;
      &lt;img src=&quot;https://img2.teletype.in/files/db/cc/dbcc3122-64b4-4803-bc3c-ad470e8888f0.png&quot; width=&quot;750&quot; /&gt;
    &lt;/figure&gt;
    &lt;p id=&quot;bgld&quot;&gt;&lt;em&gt;Rich&lt;/em&gt; это Python библиотека, позволяющая отображать &lt;em&gt;красивый&lt;/em&gt; текст и форматировать терминал.&lt;/p&gt;
    &lt;p id=&quot;kCHH&quot;&gt;&lt;a href=&quot;https://rich.readthedocs.io/en/latest/&quot; target=&quot;_blank&quot;&gt;Rich API&lt;/a&gt; упрощает добавление цветов и стилей к выводу терминала. Rich также позволяет отображать &lt;em&gt;красивые таблицы&lt;/em&gt;, &lt;em&gt;прогресс бары&lt;/em&gt;, &lt;em&gt;markdown&lt;/em&gt;, &lt;em&gt;код с отображением синтаксиса&lt;/em&gt;, &lt;em&gt;ошибки&lt;/em&gt;, и т.д. — прямо после установки.&lt;/p&gt;
  &lt;/section&gt;
  &lt;h2 id=&quot;vyjg&quot; data-align=&quot;center&quot;&gt;4. &lt;a href=&quot;https://github.com/tmbo/questionary&quot; target=&quot;_blank&quot;&gt;questionary&lt;/a&gt;&lt;/h2&gt;
  &lt;section style=&quot;background-color:hsl(hsl(0, 0%, var(--autocolor-background-lightness, 95%)), 85%, 85%);&quot;&gt;
    &lt;figure id=&quot;OkIY&quot; class=&quot;m_original&quot;&gt;
      &lt;img src=&quot;https://img4.teletype.in/files/f6/3f/f63fbc7e-e25a-4db9-b9a7-03d5f006bb12.png&quot; width=&quot;836&quot; /&gt;
    &lt;/figure&gt;
    &lt;p id=&quot;gbNL&quot;&gt;Questionary — это библиотека Python для простого создания красивых интерфейсов командной строки.&lt;/p&gt;
    &lt;p id=&quot;7m9d&quot;&gt;Позволяет создавать &lt;em&gt;красивые промпты&lt;/em&gt;, которые украсят дефолтный &lt;em&gt;питоновский инпут&lt;/em&gt;.&lt;/p&gt;
    &lt;p id=&quot;t3ll&quot;&gt;Поддерживаемые промпты:&lt;/p&gt;
    &lt;ul id=&quot;NAJH&quot;&gt;
      &lt;li id=&quot;hbGn&quot;&gt;&lt;a href=&quot;https://questionary.readthedocs.io/en/stable/pages/types.html#text&quot; target=&quot;_blank&quot;&gt;Text&lt;/a&gt;&lt;/li&gt;
      &lt;li id=&quot;ya6m&quot;&gt;&lt;a href=&quot;https://questionary.readthedocs.io/en/stable/pages/types.html#password&quot; target=&quot;_blank&quot;&gt;Password&lt;/a&gt;&lt;/li&gt;
      &lt;li id=&quot;nNKA&quot;&gt;&lt;a href=&quot;https://questionary.readthedocs.io/en/stable/pages/types.html#file-path&quot; target=&quot;_blank&quot;&gt;File Path&lt;/a&gt;&lt;/li&gt;
      &lt;li id=&quot;4zbP&quot;&gt;&lt;a href=&quot;https://questionary.readthedocs.io/en/stable/pages/types.html#confirmation&quot; target=&quot;_blank&quot;&gt;Confirmation&lt;/a&gt;&lt;/li&gt;
      &lt;li id=&quot;qvbp&quot;&gt;&lt;a href=&quot;https://questionary.readthedocs.io/en/stable/pages/types.html#select&quot; target=&quot;_blank&quot;&gt;Select&lt;/a&gt;&lt;/li&gt;
      &lt;li id=&quot;HR2G&quot;&gt;&lt;a href=&quot;https://questionary.readthedocs.io/en/stable/pages/types.html#raw-select&quot; target=&quot;_blank&quot;&gt;Raw select&lt;/a&gt;&lt;/li&gt;
      &lt;li id=&quot;FgFb&quot;&gt;&lt;a href=&quot;https://questionary.readthedocs.io/en/stable/pages/types.html#checkbox&quot; target=&quot;_blank&quot;&gt;Checkbox&lt;/a&gt;&lt;/li&gt;
      &lt;li id=&quot;jq7u&quot;&gt;&lt;a href=&quot;https://questionary.readthedocs.io/en/stable/pages/types.html#autocomplete&quot; target=&quot;_blank&quot;&gt;Autocomplete&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/section&gt;
  &lt;h2 id=&quot;CHqu&quot; data-align=&quot;center&quot;&gt;5. &lt;a href=&quot;https://github.com/prompt-toolkit/python-prompt-toolkit&quot; target=&quot;_blank&quot;&gt;python-prompt-toolkit&lt;/a&gt;&lt;/h2&gt;
  &lt;section style=&quot;background-color:hsl(hsl(0, 0%, var(--autocolor-background-lightness, 95%)), 85%, 85%);&quot;&gt;
    &lt;figure id=&quot;3mvO&quot; class=&quot;m_original&quot;&gt;
      &lt;img src=&quot;https://github.com/prompt-toolkit/python-prompt-toolkit/raw/master/docs/images/logo_400px.png&quot; width=&quot;400&quot; /&gt;
    &lt;/figure&gt;
    &lt;p id=&quot;W90w&quot;&gt;python-prompt-toolkit - библиотека, позволяющая создавать интерактивные программы командной строки прямо на Python.&lt;/p&gt;
    &lt;p id=&quot;aT9A&quot;&gt;Обилие простых виджетов, &lt;em&gt;подсветка синтаксиса во время печати&lt;/em&gt;, &lt;em&gt;автокомплиты, поддержка нажатий мыши &lt;/em&gt;и множество других возможностей не оставят данную библиотеку в стороне. &lt;/p&gt;
  &lt;/section&gt;
  &lt;hr /&gt;
  &lt;tt-tags id=&quot;wTMv&quot;&gt;
    &lt;tt-tag name=&quot;python&quot;&gt;#python&lt;/tt-tag&gt;
    &lt;tt-tag name=&quot;питон&quot;&gt;#питон&lt;/tt-tag&gt;
    &lt;tt-tag name=&quot;пайтон&quot;&gt;#пайтон&lt;/tt-tag&gt;
    &lt;tt-tag name=&quot;программирование&quot;&gt;#программирование&lt;/tt-tag&gt;
    &lt;tt-tag name=&quot;textual&quot;&gt;#textual&lt;/tt-tag&gt;
    &lt;tt-tag name=&quot;prompt&quot;&gt;#prompt&lt;/tt-tag&gt;
    &lt;tt-tag name=&quot;toolkit&quot;&gt;#toolkit&lt;/tt-tag&gt;
    &lt;tt-tag name=&quot;teminal&quot;&gt;#teminal&lt;/tt-tag&gt;
    &lt;tt-tag name=&quot;tui&quot;&gt;#tui&lt;/tt-tag&gt;
    &lt;tt-tag name=&quot;terminaluserinterface&quot;&gt;#terminaluserinterface&lt;/tt-tag&gt;
    &lt;tt-tag name=&quot;anchor_exampletextuserinterfac&quot;&gt;#anchor_exampletextuserinterfac&lt;/tt-tag&gt;
  &lt;/tt-tags&gt;

</content></entry></feed>