Пишемо розумний контракт на Solidity. Частина 1 — установка і Hello world

Люди, які цікавляться темою блокчейна, вже не раз чули про проект російсько-канадського програміста Віталіка Бутерина — Ethereum, а разом з ним і про так званих розумних контрактах. У даний статті я постараюся максимально просто описати суть Ethereum, розумних контрактів, концепцію газу і показати, як пишуться розумні контракти.
Smart Contract & Gas
Якщо на пальцях, "розумний контракт" — це певний код, який живе всередині блокчейна. Будь-який учасник мережі може його спричинити за невелику плату. Ця плата і називається Gas, дослівно "паливо". Навіщо це потрібно? Для захисту майнера від зловживання шахраєм його ресурсів.
Мало хто знає, але навіть в биткоине є можливість писати ці самі контракти, але в силу деяких причин цим мало хто займається. Одна з головних проблем — мову Script не Тьюринг-повний і написати щось більш-менш серйозне непросто (щоб ви розуміли масштаб проблеми — немає навіть можливості додати цикл). У випадку з Ethereum все трохи по іншому, мови Тьюринг-повні, і є ризик, що хтось напише контракт виду
// Це псевдокод
foo = 0;
while (True) {
foo++;
}

Зрозуміло, що майнер, запустив цей контракт, закінчить нескоро і за фактом просто витратить в нікуди свої ресурси. От щоб такого не сталося, розробники Ethereum і придумали газ — в реальності запускати код на зразок того, що я написав, буде просто економічно недоцільно, тому що викликав доведеться заплатити за кожну дію контракту.
Вартість виклику контракту в газі дуже легко порахувати — готовий код можна побудувати і представити у вигляді послідовності ассемблерних команд. Ось онлайн компілятор, в нього вже є приклад коду, треба тільки натиснути Compile > Toggle Deatails > Assembly. Для кожної команди є захардкоженая вартість.
На даний момент вартість одного газу дорівнює 50 wei — тобто 50 * 10^-18 ефіру .
Environment
Для того, щоб почати працювати з Ethereum, не обов'язково синхронізувати весь поточний блокчейн — платформа дозволяє легко створити так званий "private blockchain", що ми і зробимо. Це розумно не тільки тому що на скачування всього ланцюжка у вас піде приблизно пару днів, але і тому, що можна буде грати з контрактами не сплачуючи за це реальних грошей (а газ треба платити, в тому числі і за завантаження контракту в блокчейн).

Установка command line клієнта Ethereum

Ми скористаємося Geth. Він написаний на Go і його рекомендують використовувати у більшості статей. Ось його офіційна документація.
sudo apt-get install software-properties-common
sudo add-apt-repository -y ppa:ethereum/ethereum
sudo apt-get update
sudo apt-get install ethereum

Інструкції по установці на інші ОС можна знайти на тут. Тут же можна знайти інші імплементації клієнта — на C++ або на Python.

Піднімаємо ноду

Для початку роботи достатньо написати в терміналі
geth
. Ця команда запустить ноду для роботи в головному блокчейне і почне синхронізувати блоки. Ми цього робити не будемо, але якщо вам дуже потрібно, то рекомендую використовувати
geth --fast --cache 1024
. Важливий момент — з прапором --fast ви завантажуєте тільки заголовки блоків і, якщо вірити інтернет, то можна завантажити 500.000 блоків за пів години, що на порядок швидше класичної синхронізації. Але якщо ви вже починали скачування без цього прапора, то доведеться все видалити і почати заново. Детальніше тут.
Головна для нас опція — це console, яка дозволить нам працювати з geth в інтерактивному режимі. Також будемо використовувати прапор --dev, який запустить geth в режимі приватного блокчейна.
geth --dev console
. Будемо вважати, що тепер ви непогано розібралися з тонкощами geth. В якості прикладу нижче наведений код роботи з майнером, думаю ви все самі зрозумієте.
> personal.newAccount("123") // Створюємо новий акаунт з паролем "123"
"0x07ae7ebb7b9c65b51519fc6561b8a78ad921ed13" // Його адреса
> eth.accounts // Дивимося список акаунтів
["0x07ae7ebb7b9c65b51519fc6561b8a78ad921ed13"]
> miner.setEtherbase(eth.accounts[0]) // Встановлюємо його в якості облікового запису для майнінг
true
> eth.coinbase // Перевіряємо
"0x07ae7ebb7b9c65b51519fc6561b8a78ad921ed13" // Все вірно
> miner.start() // Я запускаю майнер не в перший раз, тому в мене номера блоків 31,32,...
true
I1005 09:25:44.363901 miner/miner.go:136] Starting mining operation (CPU=2 TOT=3)
I1005 09:25:44.364247 miner/worker.go:539] commit new work on block 31 with 0 txs & 0 uncles. Took 291.8 μs
I1005 09:25:45.049267 miner/worker.go:342] Mined block (#31 / 4ca861c2). Wait 5 blocks for confirmation
I1005 09:25:45.049567 miner/worker.go:539] commit new work on block 32 with 0 txs & 0 uncles. Took 133.101 μs
I1005 09:25:45.049976 miner/worker.go:539] commit new work on block 32 with 0 txs & 0 uncles. Took 121.3 μs
I1005 09:25:45.632474 miner/worker.go:342] Mined block (#32 / f79f0df7). Wait 5 blocks for confirmation
I1005 09:25:45.632796 miner/worker.go:539] commit new work on block 33 with 0 txs & 0 uncles. Took 182.601 μs
I1005 09:25:45.632915 miner/worker.go:539] commit new work on block 33 with 0 txs & 0 uncles. Took 86.9 μs
I1005 09:25:46.441888 miner/worker.go:342] Mined block (#33 / 16e99579). Wait 5 blocks for confirmation
I1005 09:25:46.442257 miner/worker.go:539] commit new work on block 34 with 0 txs & 0 uncles. Took 268.9 μs
I1005 09:25:46.442440 miner/worker.go:539] commit new work on block 34 with 0 txs & 0 uncles. Took 120.201 μs
> miner.stop()
true
> eth.getBalance(eth.coinbase) // Перевіримо баланс
15000000000000000000

Уважний читач помітив, що все написане підозріло нагадує JS — це він і є. Ось наприклад код функції, яка в легкочитаємом вигляді виводить акаунти з балансами:
function checkAllBalances() {
var totalBal = 0;
for (var acctNum in eth.accounts) {
var acct = eth.accounts[acctNum];
var acctBal = web3.fromWei(eth.getBalance(acct), "ether");
totalBal += parseFloat(acctBal);
console.log(" eth.accounts[" + acctNum + "]: \t" + acct + " \tbalance: " + acctBal + " ether");
}
console.log(" Total balance: " + totalBal + " ether");
};

Збережемо це код geth_scripts.js і запустимо його.
> loadScript("geth_scripts.js")
true
> checkAllBalances()
eth.accounts[0]: 0x07ae7ebb7b9c65b51519fc6561b8a78ad921ed13 balance: 15 ether
undefined

Тепер, коли ви стали визнаними експертами Geth, можна зі спокійною душею закрити консоль і як біла людина почати користуватися GUI.

Mist wallet

Mist — поки що найпоширеніший гаманець для Ethereum. Написаний з використанням Meteor, кроссплатформен, для установки потрібно просто завантажити інсталяційний файл сторінки релізів.
imageНа сьогоднішній день не існує так званих light wallet, які дозволили б працювати з контрактами, тому перше, що вам запропонує Mist — синхронізувати або Main network, або Test-net. Нам це все поки що не потрібно, ми запустимо Mist на нашому приватному блокчейне. Зробити це дуже просто:
geth --dev --rpc --rpcaddr "0.0.0.0" --rpcapi "admin,debug,miner,shh,txpool,personal,eth,net,web3" console
mist.exe --rpc http://loaclhost:8545

Перший рядок робить все теж саме, що й раніше, але цього разу ми ще й відкриваємо HTTP-сервер RPC, по дефолту на localhost:8545. Прапор rpcapi визначає набір дозволів, який буде мати Mist, після підключення до сервера. У цьому випадку зазначене взагалі все що є. Якщо, наприклад, не вказати personal, то Mist не зможе створювати нові облікові записи і т. д. Звістка набір опцій командного рядка перераховано тут.
Другий рядок запускає Mist із зазначеним RPC сервером. Якщо все спрацював правильно, то при запуску повинна з'явитися напис Private-net.
Hello, world!
Саме час створити ваш перший контракт. Для цього натискаємо на Contracts > Deploy new contract.
У вікні Solidity contract source code пишемо:
contract mortal {
/*Для адрес є окремий тип змінних*/
address owner;

/*Ця функція виконується лише одного разу - при завантаження контракту в блокчейн
Називається також як і контракт
Змінної owner присвоїти значення адреси відправника контракту, тобто вашу адресу*/
function mortal() { owner = msg.sender; }

/*Функція selfdestruct знищує контракт і відправляє всі кошти з рахунку контракту на адресу, вказаний в аргументі*/
/*В Ethereum будь-учасник мережі може викликати будь-яку функцію
Перевірка адреси дозволить знищити контракт тільки вам*/
function kill() { if (msg.sender == owner) selfdestruct(owner); }
}

/*Оператор is відповідає за успадкування*/
/*Можливо множинне спадкування виду contract_1 is contract_2, contract_3*/
contract greeter is mortal {
string greeting;

/*В цьому разі при ініціалізації контракту потрібно буде вказати рядок-аргумент
У нашому випадку це і буде "Hello, world!"*/
function greeter(string _greeting) public {
greeting = _greeting;
}

// Ця функція і відповідає за повернення "Hello, world!"
function greet() constant returns (string) {
return greeting;
}
}

Сам по собі мова досить простий, ось його документація, багато питання вже обговорювалися на ethereum.stackexchange.com, в принципі є шанс отримати відповіді gitter.

Завантаження в блокчейн і запуск контракту

Після того, як ви вибрали контракт greeting, вказали "Hello, world" в якості аргументу, натиснули Deploy і ввели пароль, ваш контракт залишається тільки "замайнить" в блокчейн. Для цього відкриваємо термінал з включеним Geth і майним пару блоків (як це зробити написано вище). Все!
Тепер зайшовши на вкладку Contracts ви побачите новий контракт з іменем GREETER 7D5D, або типо того. Натискаємо на нього і насолоджуємося результатом. При бажанні можна його вбити, вибравши в списку праворуч функцію Kill.
p.s. Моя перша стаття, лайте строго. У наступних частинах думаю написати криптовалюту, розповісти про нюанси EVM, показати, як прикрутити браузерні інтерфейси до Ethereum і можливо ще щось. Здорова критика вітається, спасибі за увагу :)
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.