Учебное пособие по Solidity: Введение в программирование на Solidity для начинающих

Учебник по Solidity

Solidity – это объектно-ориентированный язык высокого уровня для разработки dApps (децентрализованных приложений) на блокчейне Ethereum..

Блокчейн – это одноранговая сеть компьютеров, называемых узлами, которые совместно используют все данные и код в сети..

Итак, если вы устройство, подключенное к блокчейну, вы являетесь узлом в сети и общаетесь со всеми другими компьютерными узлами в сети (мы поговорим о том, как настроить узел Ethereum на вашем локальном компьютере, в следующих руководствах)..

Теперь у вас есть копия всех данных и кода в цепочке блоков. Больше нет необходимости в центральных серверах.

Что такое Ethereum?

Проще говоря, Ethereum – это открытая программная платформа, основанная на технологии блокчейн, которая позволяет разработчикам создавать и развертывать децентрализованные приложения..

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

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

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

Начиная с основ

Код Solidity заключен в контракты.

Блокчейн Ethereum позволяет нам выполнять код с виртуальной машиной Ethereum (EVM) на блокчейне с помощью так называемого смарт-контракта..

Смарт-контракты – это место, где живет вся бизнес-логика нашего приложения – все переменные и функции принадлежат контракту, и это будет отправной точкой всех ваших проектов..

Смарт-контакты написаны на языке программирования Solidity, который выглядит как смесь Javascript и C.

Ремикс IDE

Remix – это онлайн-инструмент, который позволяет вам писать смарт-контракты Solidity, затем развертывать их и запускать..

Просто зайдите в https://remix.ethereum.org из вашего браузера, и мы можем начать кодирование.

Как видите, вы можете выбирать между Solidity и Vyper. Оба являются языками для написания смарт-контрактов, Vyper похож на Python, а Solidity похож на javascript..

Оба могут компилироваться в байт-код EVM, вроде Javascript и Typescript. Мы выбираем Solidity.

Слева находится файловый менеджер. По умолчанию есть два файла .sol, просто для демонстрации базового синтаксиса (ballot.sol – это смарт-контракт, ballot_test.sol – скрипт для тестирования этого смарт-контракта)..

Вам просто нужно нажать кнопку с плюсом, и мы сможем начать кодирование нашего первого смарт-контракта..

Весь исходный код Solidity должен начинаться с «прагмы версии» – объявления версии компилятора Solidity, которую должен использовать этот код. Это сделано для предотвращения проблем с будущими версиями компилятора, потенциально вносящими изменения, которые могут нарушить ваш код..

Выглядит это так:

прагма solidity ^ 0.4.25;

(для версии Solidity выше 0.4.25)

или

прагматическая солидность >= 0,5,0 < 0.6.0;

(для версии Solidity от 0.5.0 до 0.6.0)

Затем вы создаете свой контракт, набирая зарезервированное слово договор и имя вашего файла .sol (важно, чтобы имя контракта совпадало с именем файла, почему мы обсудим позже). В нашем случае,

контракт MyFirstContract {

}

Давайте скомпилируем. Вам просто нужно перейти на вкладку компиляции слева и нажать большую кнопку компиляции. Если что-то не так с кодом, вы увидите здесь ошибки и предупреждения (проявите сострадание к Solidity, это все еще «молодой язык»)..

С нашим текущим контрактом все в порядке, потому что мы действительно ничего не сделали.

Теперь я специально сгенерирую ошибку, чтобы показать вам что-то. Вы можете вручную выбрать компилятор из этого раскрывающегося меню..

Выберем, например, версию 0.4.26. Теперь скомпилируйте его снова. Теперь вы увидите ошибку «Компилятор еще не загружен»..

Это связано с тем, что мы указали с помощью прагмы работу с версиями компилятора выше 0.5.0. Просто измените версию компилятора еще раз, и ошибка исчезнет.

Хорошо, давайте теперь код!

Мы начнем с простого кода «Hello world», а также будем получать и настраивать функции, чтобы лучше познакомиться с синтаксисом..

Контракт в смысле Solidity – это набор кода (его функции) и данных (его состояние), который находится по определенному адресу в цепочке блоков Ethereum..

Сначала давайте определим переменную состояния с именем message, например, и ее тип будет строковым..

Наша функция get вернет значение нашего сообщения переменной, а функция set присвоит новое значение нашему сообщению переменной..

Как набирать функции?

Первое, зарезервированное слово функция затем имя конкретной функции и параметров и после этого .

function myFunction () возвращает (bool) {

вернуть истину;

}

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

Вот список всех спецификаторов видимости функций:

  • общественный: видимый снаружи и внутри (создает функцию получения для переменных хранилища / состояния)
  • частный: отображается только в текущем контракте
  • внешний: виден только извне (только для функций) – т.е. может быть вызван только через сообщение (через this.func)
  • внутренний: виден только внутри

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

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

  • чистый: Запрещает изменение или доступ к состоянию.
  • Посмотреть: Запрещает изменение состояния.
  • подлежащий оплате: Позволяет им получать эфир вместе с вызовом.

Если функция возвращает какое-то значение, вам нужно указать это с зарезервированным словом возвращается а затем в обычных скобках, чтобы указать, какой тип возвращает функция. В нашем случае это будет строка (потому что мы возвращаем наше сообщение переменной, которое является строкой)

Если функция не возвращает никакого значения, нет необходимости в возвращается заявление.

Для доступа к переменной состояния вам не нужен префикс этот. как это принято в других языках.

Из-за этого распространенной практикой является запись аргументов функции с синтаксисом подчеркивания. (_сообщение). Это соглашение пришло из Javascript, где частные методы и переменные начинаются с _.

Чтобы было ясно, ваш код будет работать нормально и без подчеркивания, но с ними он будет чище..

Вы заметите зарезервированное слово объем памяти в нашем коде. Если вы напишете наш код без памяти и установите для прагмы некоторую версию ниже 0.5. *, Он будет работать нормально, но когда вы измените свой компилятор на выше 0.5. * EVM генерирует ошибку компиляции.

Почему это происходит?

Что ж, у виртуальной машины Ethereum есть три области, где она может хранить предметы..

  • Первый место хранения, где находятся все переменные состояния контракта. У каждого контракта есть собственное хранилище, оно сохраняется между вызовами функций и довольно дорогое в использовании..
  • Второй объем памяти, это используется для хранения временных значений. Он стирается между (внешними) вызовами функций и дешевле в использовании.
  • Третий – это куча, который используется для хранения небольших локальных переменных. Его можно использовать практически бесплатно, но он может содержать только ограниченное количество значений..

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

Но когда вы работаете с массивами или структурами, а также со строками в последних версиях, компилятор заставит вас указать область хранения..

Итак, наш код теперь выглядит так:

прагма solidity ^ 0.5.0;

контракт MyFirstContract {

строковое сообщение;

функция get () общественное представление возвращает (строковая память) {

ответное сообщение;

}

набор функций (строка памяти _message) public {

message = _message;

}

}

Обратите внимание, что некоторые разработчики Solidity разбивают эти спецификаторы видимости на отдельные строки, чтобы сделать код более чистым. Итак, нашу функцию get можно записать так:

функция get ()

общественный

Посмотреть

возвращает (строка)

{

ответное сообщение;

}

На самом деле вам решать, как вы решите писать свои функции.

Давайте сейчас скомпилируем наш контракт и протестируем его.

Для его компиляции просто повторите шаги, указанные ниже (Скомпилируйте .sol кнопка или cmd / ctrl + S с клавиатуры, и он автоматически перекомпилирует его)

Чтобы увидеть, как это работает (если компиляция не приводит к ошибкам), вам необходимо развернуть свой контракт..

Для этого перейдите на вкладку «Развертывание» слева, для среды выберите JavaScriptVM и нажмите кнопку «Развернуть»..

После развертывания мы можем видеть методы из нашего контракта. Давайте теперь сосредоточимся только на этой части экрана.

Как видите, есть две кнопки (получить & set) для двух наших общедоступных функций. Если бы что-то из этого было частным, мы бы не увидели его здесь.

Если мы нажмем кнопку получения, EVM выполнит нашу функцию получения.

Посмотрим, как это сработало.

У нас пустая строка. Не здорово, не ужасно. Но почему? Потому что мы изначально не инициализировали нашу переменную сообщения.

Всего одна короткая пауза. Я хочу, чтобы вы познакомились с Remix Terminal. Он находится в редакторе кода, и здесь вы можете отслеживать все свои транзакции, чтобы видеть, были ли они выполнены успешно или нет, отлаживать их, просматривать детали (хэш транзакции и т. Д.) И многое другое..

На данный момент у нас есть две успешные транзакции. Один из них – развертывание контракта, и он стоит нам эфира (но не волнуйтесь, мы в редакторе, теперь все виртуально), а второй – звонок нашего Посмотреть функция.

Хорошо, теперь вернемся. Что будет, если мы сейчас вызовем функцию set?

Нам нужно передать аргумент _message (например, «Hello World») и нажать кнопку транзакции, чтобы выполнить функцию. Вы можете отслеживать успешность транзакции в Терминале.

Теперь давайте снова вызовем функцию get. Теперь он возвращает наше сообщение.

Давайте внесем некоторые улучшения в наш код. Мы не инициализировали сообщение о переменной. Давай сделаем это.

контракт MyFirstContract {

строка message = "Привет, мир!";

функция get () общественное представление возвращает (строковая память) {

ответное сообщение;

}

набор функций (строка памяти _message) public {

message = _message;

}

}

Обратите внимание, что сообщение теперь называется «Hello world!», И когда мы вызываем функцию get в первый раз, она не возвращает пустую строку..

Чтобы проверить это, нам нужно скомпилировать наш контракт (cmd / ctrl + S).

Затем снова развернуть. Нам нужно создать новый экземпляр контракта (из-за внесенных нами изменений) и опубликовать его в блокчейне..

Просто удалите предыдущую версию из редактора (конечно, не из нашего виртуального блокчейна) и снова нажмите кнопку «Развернуть». Теперь вызовем нашу функцию get.

Отлично! Давайте сейчас вызовем функцию набора.

И получить снова.

Круто.

Давайте теперь сделаем наше сообщение постоянный.

Наш код сейчас:

прагма solidity ^ 0.5.0;

контракт MyFirstContract {

строковая константа message = "Привет, мир!";

функция get () общественное представление возвращает (строковая память) {

ответное сообщение;

}

набор функций (строка памяти _message) public {

message = _message;

}

}

Когда мы пытаемся его скомпилировать, мы получаем ошибку в нашей функции set. Это потому, что нельзя изменить значение константы.

Мы просто избавимся от этой постоянной сейчас.

Инициализация таких переменных не является ошибкой, но гораздо лучше, если мы сделаем это в конструкторе. Вы можете написать конструктор в Solidity с помощью:

constructor () public {

// сделай что-нибудь…

}

Конструктор – это еще одна функция, которая вызывается во время развертывания смарт-контракта. Наш код выглядит немного иначе, но работает так же.

прагма solidity ^ 0.5.0;

контракт MyFirstContract {

строковое сообщение;

constructor () public {

сообщение = "Привет, мир!";

}

функция get () общественное представление возвращает (строковая память) {

ответное сообщение;

}

набор функций (строка памяти _message) public {

message = _message;

}

}

Вы можете скомпилировать его снова и протестировать, если хотите.

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

Solidity сделает для каждой общедоступной переменной состояния метод с тем же именем, который может вызываться как обычная функция (вроде как функция получения).

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

Чем больше код, тем больше газа требуется для его выполнения и увеличивается стоимость запуска нашего dApp..

При разработке смарт-контрактов нам необходимо:

  • эффективный – расход газа должен быть низким
  • точный – после развертывания смарт-контракта его нельзя изменить, и он является общедоступным 24/7, каждая строчка кода (представьте себе хакера, который находит ошибку и может использовать ваше dApp)

Наш последний код на сегодня выглядит так:

прагма solidity ^ 0.5.0;

контракт MyFirstContract {

строковое публичное сообщение;

constructor () public {

сообщение = "Привет, мир!";

}

набор функций (строка памяти _message) public {

message = _message;

}

}

Давайте развернем и протестируем.

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

Если мы вызовем это, он должен вернуть нам значение, которое инициализируется через конструктор (это «Hello world!»)..

Отлично. Давайте теперь протестируем функцию набора.

Как научиться Solidity?

Сам по себе Solidity – довольно простой язык, но чтобы быть хорошим разработчиком Solidity, нужно понимать, как все работает в Ethereum..

  • Solidity – это язык программирования высокого уровня с синтаксисом, аналогичным ECMAScript (javascript)..
  • Он компилируется в байт-код EVM, что может понять только EVM..
  • Компилятор называется Solc.

В качестве примера возьмем этот простой контракт:

прагма solidity ^ 0.5.0;

Пример контракта {

uint a = 10 + 5;

}

Просто как тот. А теперь скомпилируем. Если мы перейдем к деталям контракта в Терминале, мы увидим много информации.

В этом случае скомпилированный код:

0x6080604052600f600055348015601457600080fd5b5060358060226000396000f3fe6080604052600080fdfea165627a7a72305820bf75c57b7d8745a79baee513ead21a9eb8b075896c17007e4c

Эти длинные значения являются шестнадцатеричным представлением окончательного контракта, также известным как байт-код. EVM понимает только байт-код.

Но, если что-то пойдет не так, мы застрянем с какой-то ошибкой, например, нельзя отладить байт-код.

Коды операций

Язык над байт-кодом – это код операции. Opcode – это язык программирования низкого уровня. Solidity и Opcode похожи, например, на C и язык ассемблера.

Поэтому, когда нам нужно отладить некоторую неудачную транзакцию, мы отлаживаем код операции.

Вы должны знать одну вещь о Solidity и отладке – это очень сложно. Но не невозможно, так что давайте погрузимся в это..

Это код операции нашего Примерного контракта:

0 PUSH1 60

02 PUSH1 40

04 МАГАЗИН

05 PUSH1 0f

07 PUSH1 00

09 МАГАЗИН

10 ЗНАЧЕНИЕ

11 DUP1

12 ISZERO

13 PUSH1 14

15 ДЖЕМПИ

16 PUSH1 00

18 DUP1

19 НАЗАД

20 прыжков

21 ПОП

22 PUSH1 35

24 DUP1

25 PUSH1 22

27 PUSH1 00

29 КОДЕКОПИЯ

30 PUSH1 00

32 ВОЗВРАТ

33 НЕДЕЙСТВИТЕЛЬНО

34 PUSH1 80

36 PUSH1 40

38 МАГАЗИН

39 PUSH1 00

41 DUP1

42 НАЗАД

43 НЕДЕЙСТВИТЕЛЬНО

44 LOG1

45 ПУШ6 627a7a723058

52 SHA3

53 НЕДЕЙСТВИТЕЛЬНО

54 PUSH22 c57b7d8745a79baee513ead21a9eb8b075896f8e4c59

77 НЕДЕЙСТВИТЕЛЬНО

78 DUP10

79 И

80 ДЖЕМПИ

81 НЕДЕЙСТВИТЕЛЬНО

82 БАЛАНС

83 PUSH29 750029

Коды операций – это низкоуровневые удобочитаемые инструкции программы. Все коды операций имеют свои шестнадцатеричные аналоги, например МАГАЗИН является 0x52.

EVM – это стековая машина. Он основан на структуре LIFO (Last In First Out). Для упрощения представьте, что складываете ломтики хлеба в микроволновую печь. ПОСЛЕДНИЙ ломтик, который вы кладете, – ПЕРВЫЙ, который вы вынимаете..

В обычной арифметике мы записываем уравнение следующим образом:

10 + 2 * 2

и ответ 14, потому что мы делаем умножение перед сложением.

В стековой машине он работает по принципу LIFO:

2 2 * 10 +

Это означает, что сначала положите в стек 2, затем еще 2, а затем действие умножения. В результате на вершине стека находится 4. Теперь сложите цифру 10 поверх 4 и в конечном итоге сложите 2 числа. Конечное значение стека становится 14.

Акт помещения данных в стек называется инструкцией PUSH, а действие удаления данных из стека называется инструкцией POP. Очевидно, что наиболее распространенный код операции, который мы видим в нашем примере выше, – это PUSH1, что означает помещение 1 байта данных в стек..

Итак, эта инструкция:

PUSH1 0x60

означает размещение в стеке 1-байтового значения «0x60». По совпадению, шестнадцатеричное значение для PUSH1 также оказывается «0x60». Удалив необязательный «0x», мы могли бы записать эту логику в байт-коде как «6060»..

Пойдем немного дальше.

PUSH1 0x60 PUSH1 0x40 MSTORE

MSTORE (0x52) принимает 2 входа и не производит выход. Приведенные выше коды операций означают:

PUSH1 (0x60): положить 0x60 в стек.

НАЖАТЬ1 (0x40): положить 0x40 в стек.

MSTORE (0x52): выделить 0x60 места в памяти и перейти в позицию 0x40.

Результирующий байт-код:

6060604052

Фактически, мы всегда видим это магическое число «6060604052» в начале любого байт-кода твердости, потому что именно так происходит загрузка смарт-контракта..

Чтобы еще больше усложнить вопрос, 0x40 или 0x60 нельзя интерпретировать как действительное число 40 или 60. Поскольку они шестнадцатеричные, 40 фактически равно 64 (16¹ x 4), а 60 равно 96 (16¹ x 6) в десятичном..

Короче говоря, «PUSH1 0x60 PUSH1 0x40 MSTORE» выделяет 96 байт памяти и перемещает указатель в начало 64-го байта. Теперь у нас есть 64 байта для рабочего пространства и 32 байта для хранения временной памяти..

В EVM есть 3 места для хранения данных. Во-первых, в стеке. Мы только что использовали код операции PUSH для хранения данных в соответствии с приведенным выше примером..

Во-вторых, в памяти (RAM), где мы используем код операции MSTORE, и, наконец, в дисковом хранилище, где мы используем SSTORE для хранения данных. Газ, необходимый для хранения данных в хранилище, является самым дорогим, а хранение данных в стеке – самым дешевым..

Теперь пришло время вернуться к нашему коду Solidity из этого руководства и подвести итоги того, что мы узнали о зарезервированном слове. объем памяти и как компилятор заставляет нас указывать, как мы храним строки, например.

Мы рассмотрели только основы байт-кода и несколько кодов операций..

Нам не нужно знать коды операций, чтобы начать писать смарт-контракт!

С другой стороны, обработка ошибок EVM по-прежнему очень примитивна, и ее удобно смотреть на коды операций, когда что-то идет не так..

Вывод

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

До тех пор &# 128075;