Урок за солидност: Въведение в програмирането на солидност за начинаещи

Урок за солидност

Solidity е обектно-ориентиран език на високо ниво за разработване на dApps (Децентрализирани приложения) в блокчейн Ethereum.

Блокчейнът е peer-to-peer мрежа от компютри, наречени възли, които споделят всички данни и кода в мрежата.

Така че, ако сте устройство, свързано към блокчейна, вие сте възел в мрежата и говорите с всички останали компютърни възли в мрежата (ще говорим как да настроите Ethereum възел на вашата локална машина в по-късни уроци).

Вече имате копие на всички данни и кода в блокчейна. Вече няма нужда от централни сървъри.

Какво е Ethereum?

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

Докато блокчейнът Bitcoin се използва за проследяване на собствеността върху цифрова валута (биткойни), блокчейнът Ethereum се фокусира върху стартирането на кода на децентрализираните приложения.

В блокчейна Ethereum, вместо да добиват за биткойни, майньорите работят, за да печелят Ether, вид крипто токен, който захранва мрежата. Освен търгуемата криптовалута, Ether се използва и от разработчиците на приложения за плащане на такси за транзакции и услуги в мрежата Ethereum.

Има втори тип токен, който се използва за плащане на такси за миньори за включване на транзакции в техния блок, той се нарича газ и всяко изпълнение на интелигентен договор изисква да бъде изпратено определено количество газ заедно с него, за да примами майньорите да го вкарат блокчейна.

Започвайки с основите

Кодът на Solidity е капсулиран в договори.

Ethereum blockchain ни позволява да изпълняваме код с Ethereum Virtual Machine (EVM) на blockchain с нещо, наречено интелигентен договор.

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

Интелигентните контакти са написани на език за програмиране, наречен Solidity, който изглежда като комбинация от Javascript и C.

Remix IDE

Remix е онлайн инструмент, който ви позволява да пишете интелигентни договори Solidity, след това да ги разгръщате и стартирате.

Просто отидете на https://remix.ethereum.org от вашия браузър и можем да започнем да кодираме.

Както можете да видите, можете да избирате между Solidity и Vyper. И двата езика са за писане на интелигентни договори, Vyper е подобен на python, а Solidity е подобен на javascript.

И двете могат да се компилират в байт кода на EVM, като Javascript и Typescript. Избираме Solidity.

От лявата страна има File Explorer. По подразбиране има два файла .sol, само за демонстрация на основния синтаксис (ballot.sol е интелигентен договор, ballot_test.sol е скрипт за тестване на този интелигентен договор).

Трябва само да щракнете върху този плюс и ние можем да започнем да кодираме първия си интелигентен договор.

Всички изходни кодове на 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. Сега го компилирайте отново. Сега ще видите грешка „Компилаторът все още не е зареден“.

Това е така, защото ние определихме с pragma да работи с версии на компилатора над 0.5.0. Просто променете версията на компилатора отново и грешката ще изчезне.

Добре, нека да кодираме сега!

Ще започнем с прост код „Hello world“ и ще получим и зададем функции, само за да се запознаем по-добре със синтаксиса.

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

Първо, нека дефинираме променлива на състоянието, наречена например съобщение и нейният тип ще бъде низ.

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

Как да пишете функции?

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

функция myFunction () връща (bool) {

връщане вярно;

}

Функциите могат да бъдат публично или частни. Ако дадена функция е публична, тя може да бъде извикана извън договора. Ако функцията е частна, тя има ограничен обхват и може да бъде извикана само от текущия й договор (например от друга функция).

Ето списъка на всички спецификатори за видимост на функциите:

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

Функциите могат да бъдат чист, изглед, или платими. Ако дадена функция не записва никакви данни в блокчейн, много се препоръчва да бъдете преглед, тъй като функциите за преглед не струват никакъв бензин.

Ето списъка с всички модификатори на функции (има и модификатори за променливи на състоянието, събития и аргументи за събития, но ще говори за тях по-късно):

  • чист: Забранява модификация или достъп до състояние.
  • изглед: Забранява модификация на състоянието.
  • платими: Позволява им да получават Ether заедно с повикване.

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

Ако функцията не връща никаква стойност, няма нужда от се завръща изявление.

За да получите достъп до променлива на състоянието, не се нуждаете от префикса това. както е често срещано в други езици.

Поради това често срещана практика е да се пишат аргументи на функциите със синтаксис за подчертаване (_съобщение). Тази конвенция идва от Javascript, където частните методи и променливи започват с _.

За да бъдем ясни, вашият код ще работи добре и без подчертавания, но с тях е по-чист.

Ще забележите запазена дума памет в нашия код. Ако напишете нашия код без памет и зададете pragma на някаква версия под 0.5. * Ще работи добре, но когато смените компилатора си на над 0.5. * EVM генерира грешка при компилиране.

Защо се случва това??

Е, виртуалната машина Ethereum има три области, където може да съхранява елементи.

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

За почти всички типове не можете да посочите къде да се съхраняват, защото се копират всеки път, когато се използват.

Но когато работите с масиви или структури, а от най-новите версии и със низове, компилаторът ще ви принуди да посочите зоната за съхранение.

И така, нашият код сега изглежда така:

твърдост на прагма ^ 0,5,0;

договор MyFirstContract {

низ съобщение;

функция get () връща публичен изглед (низ памет) {

съобщение за връщане;

}

набор от функции (низ памет _message) public {

съобщение = _съобщение;

}

}

Моля, обърнете внимание, че някои разработчици на Solidity разбиват тези спецификатори на видимост на отделни редове, за да направят кода по-чист. Така че нашата функция get може да бъде написана по следния начин:

функция get ()

публично

изглед

връща (низ)

{

съобщение за връщане;

}

Наистина зависи от вас как ще изберете да напишете функциите си.

Нека да съставим нашия договор сега и да го тестваме.

За да го компилирате, просто повторете стъпките отдолу (Съставяне .sol бутон или cmd / ctrl + S от клавиатурата и ще го прекомпилира автоматично)

За да видите действително как работи (ако компилирането не генерира грешки), трябва да внедрите договора си.

За да направите това, отворете раздела за разполагане отляво, за среда изберете JavaScriptVM и натиснете бутона за внедряване.

След внедряването вече можем да видим методи от нашия договор. Нека се фокусираме точно върху тази част от екрана сега.

Можете да видите, че има два бутона (get & set) за нашите две публични функции. Ако някой от тях беше частен, нямаше да го видим тук.

Ако щракнем върху бутона за получаване, EVM ще изпълни нашата функция за получаване.

Нека да видим как се получи.

Имаме празен низ. Не е страхотно, не е ужасно. Но защо? Ами защото първоначално не инициализираме нашата променлива за съобщения.

Само една бърза пауза. Искам да се запознаете с терминала Remix. Той е под редактора на кода и тук можете да проследявате всичките си транзакции, да видите дали са изпълнени успешно или не, да ги отстраните, да видите подробности (хеш на транзакции и т.н.) и други.

За момента имаме две успешни транзакции. Едното е разполагане на договор и ни струва етер (но не се притеснявайте, сега сме в редактора всичко е виртуално), а второто е Call of our изглед функция.

Добре, да се върнем сега. Какво ще се случи, ако извикаме функцията set сега?

Трябва да предадем аргумент _message (например „Hello World“) и да натиснем бутона за транзакция, за да изпълним функцията. Можете да проследите успеха на транзакцията в терминала.

Сега нека извикаме функцията get отново. Сега връща нашето съобщение.

Нека направим някои подобрения в нашия код. Не инициализираме нашето съобщение с променлива Да го направим.

договор MyFirstContract {

низ съобщение = "Здравей свят!";

функция get () връща публичен изглед (низ памет) {

съобщение за връщане;

}

набор от функции (низ памет _message) public {

съобщение = _съобщение;

}

}

Забележете, че съобщението вече е „Здравей, свят!“ И когато извикаме функцията get за първи път, тя няма да върне празен низ.

За да тестваме това, трябва да съставим нашия договор (cmd / ctrl + S).

След това да го разположите отново. Трябва да създадем нов екземпляр на договор (поради промените, които направихме) и да го публикуваме в блокчейн.

Просто изтрийте предишната версия от редактора (не от нашата виртуална блокчейн, разбира се) и натиснете бутона за внедряване отново. Нека извикаме нашата функция get сега.

Хубаво! Нека извикаме функцията за набор сега.

И вземете отново.

Готино.

Нека сега направим нашето съобщение a постоянна.

Нашият код сега:

твърдост на прагма ^ 0,5,0;

договор MyFirstContract {

низ константа съобщение = "Здравей свят!";

функция get () връща публичен изглед (низ памет) {

съобщение за връщане;

}

набор от функции (низ памет _message) public {

съобщение = _съобщение;

}

}

Когато се опитваме да го компилираме, получаваме грешка в зададената ни функция. Това е така, защото човек не може да промени стойността на константа.

Просто ще се отървем от тази константа сега.

Инициализирането на променливи като тази не е грешка, но е много по-добре, ако правим това в конструктора. Можете да напишете конструктор в Solidity с:

конструктор () public {

// направи нещо…

}

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

твърдост на прагма ^ 0,5,0;

договор MyFirstContract {

низ съобщение;

конструктор () public {

съобщение = "Здравей свят!";

}

функция get () връща публичен изглед (низ памет) {

съобщение за връщане;

}

набор от функции (низ памет _message) public {

съобщение = _съобщение;

}

}

Можете да го компилирате отново и да го тествате, ако искате.

И накрая, може да се промени видимостта на променливите на състоянието. Ако направите вашите променливи на състоянието публично това означава, че човек може да претендира за стойностите си извън договора.

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

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

Колкото по-голям е кодът, толкова повече газ е необходим за изпълнението му и цената за стартиране на нашето dApp се увеличава.

Когато разработваме интелигентни договори, трябва да бъдем:

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

Последният ни код за днес изглежда така:

твърдост на прагма ^ 0,5,0;

договор MyFirstContract {

низ публично съобщение;

конструктор () public {

съобщение = "Здравей свят!";

}

набор от функции (низ памет _message) public {

съобщение = _съобщение;

}

}

Нека го разгърнем и тестваме.

Можете да видите този бутон за съобщение. Създава се, защото съобщението ни за променлива на състоянието е публично.

Ако извикаме това, то трябва да ни върне стойност, която се инициализира чрез конструктор (това е „Здравей, свят!“).

Хубаво. Нека тестваме функцията за задаване сега.

Как да науча солидност?

Самият Solidity е доста прост език, но за да бъде добър разработчик на Solidity, човек трябва да разбере как всичко работи на Ethereum.

  • Solidity е език за програмиране на високо ниво със синтаксис, подобен на ECMAScript (javascript).
  • Той се компилира в EVM байт код, нещо, което само EVM може да разбере.
  • Съставителят се нарича Solc.

Нека вземем за пример този прост договор:

твърдост на прагма ^ 0,5,0;

пример за договор {

uint a = 10 + 5;

}

Просто като това. Сега нека го компилираме. Ако отидем на Подробности за договора в терминала, можем да видим много информация.

В този случай компилираният код е:

0x6080604052600f600055348015601457600080fd5b5060358060226000396000f3fe6080604052600080fdfea165627a7a72305820bf75c57b7d8745a79baee513ead21a9eb8b075896f8e4c1

Тези дълги стойности са шестнадесетично представяне на окончателния договор, известен също като байт код. EVM разбира само байт код.

Но ако нещо се обърка, ние се забиваме с някаква грешка, например, не можем да отстраним грешки в байт кода.

Опкодове

Езикът над байт кода е opcode. Opcode е език за програмиране на ниско ниво. Solidity и Opcode са като C и асемблерен език например.

Така че, когато трябва да отстраним грешка в някаква неуспешна транзакция, ние отстраняваме грешки в opcode.

Едно нещо, което трябва да знаете за Solidity и отстраняване на грешки – това е много трудно. Но не и невъзможно, така че нека се потопим в него.

Това е opcode на нашия примерен договор:

0 PUSH1 60

02 PUSH1 40

04 МАГАЗИН

05 PUSH1 0f

07 PUSH1 00

09 МАГАЗИН

10 КАЛВА

11 DUP1

12 ISZERO

13 PUSH1 14

15 JUMPI

16 PUSH1 00

18 DUP1

19 ОБРАТНО

20 СКОРО

21 POP

22 PUSH1 35

24 DUP1

25 PUSH1 22

27 PUSH1 00

29 КОДЕКОПИЯ

30 PUSH1 00

32 ВРЪЩАНЕ

33 ИНВАЛИДНИ

34 PUSH1 80

36 PUSH1 40

38 MSTORE

39 PUSH1 00

41 DUP1

42 ОБРАТНО

43 ИНВАЛИДНИ

44 ЛОГАЛ1

45 PUSH6 627a7a723058

52 SHA3

53 ИНВАЛИДНИ

54 PUSH22 c57b7d8745a79baee513ead21a9eb8b075896f8e4c59

77 НЕВЕРНО

78 DUP10

79 И

80 JUMPI

81 НЕВЕРНО

82 БАЛАНС

83 PUSH29 750029

Опкодовете са инструкциите на програмата за четене от ниско ниво. Всички опкодове имат своите шестнадесетични аналози, напр MSTORE е 0x52.

EVM е стекова машина. Тя се основава на LIFO структура (Last In First Out). За да опростите, представете си, че подреждате филийки хляб в микровълнова печка, ПОСЛЕДНАТА филия, която поставяте, е ПЪРВАТА, която изваждате.

В нормална аритметика ние пишем нашето уравнение по следния начин:

10 + 2 * 2

и отговорът е 14, защото правим умножение преди събиране.

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

2 2 * 10 +

Това означава, първо поставете 2 в стека, последвано от още 2, след което последвано от действие за умножение. Резултатът е 4 седнали на върха на стека. Сега добавете число 10 върху 4 и в крайна сметка добавете 2 числа заедно. Крайната стойност на стека става 14.

Актът за поставяне на данни в стека се нарича инструкция PUSH, а актът за премахване на данни от стека се нарича инструкция POP. Очевидно е, че най-често срещаният opcode, който виждаме в нашия пример по-горе, е PUSH1, което означава поставяне на 1 байт данни в стека.

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

PUSH1 0x60

означава поставяне на 1 байтова стойност на „0x60” в стека. По случайност шестнадесетичната стойност за PUSH1 също е „0x60“. Премахвайки незадължителното „0x“, бихме могли да напишем тази логика в байт код като „6060“.

Нека отидем малко по-нататък.

PUSH1 0x60 PUSH1 0x40 MSTORE

MSTORE (0x52) приема 2 входа и не извежда изход. Опкодовете по-горе означават:

PUSH1 (0x60): поставете 0x60 в стека.

PUSH1 (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;