Bitcoin іноді називають
програмованими грошима. Завдяки своєму цифровому характеру він дає користувачам велику гнучкість при налаштуванні умов витрачання коштів.
Говорячи про Bitcoin, ми говоримо про
гаманці та
монети. Але ми могли б також думати про гаманці як про ключі, про монети як про чеки, а про
блокчейн як про ряд заблокованих сейфів. У кожному сейфі є тонкий проріз, так що будь-хто може покласти чеки або заглянути всередину, щоб дізнатися, скільки в сейфі зберігається коштів. Однак доступ всередину зможе отримати лише власник ключа.
Коли власник ключа хоче віддати гроші комусь іншому, він відкриває свою скриньку. Сторони формують новий чек, який посилається на старий (який в свою чергу потім знищується), і замикають його в скриньці, яку може відкрити отримувач. Щоб витратити ці кошти, новий отримувач повторює процес.
У цій статті ми більш детально розглянемо Script, мову програмування, що інтерпретується
нодами в мережі Bitcoin. Script – це те, що керує механізмом блокування/розблокування "сейфів".
Використовуючи наведену вище аналогію, можна сказати, що кожна транзакція складається з двох частин – ключа (для розблокування вашої скриньки) і замка. Ви використовуєте свій ключ, щоб відкрити скриньку, яка містить чек, який ви хочете відправити, а потім додаєте новий до нової скриньки з іншим замком. Щоб здійснювати витрати з нової скриньки, знадобиться ще один ключ.
Досить просто. Ви також можете отримати невеликі варіації за типами замків в системі. Можливо, деякі сейфи вимагають, щоб ви надали
декілька ключів, а можливо, іншим потрібно, щоб ви довели, що знаєте секрет. Люди можуть встановити низку умов.
Наш ключ – це те, що ми називаємо scriptSig. Замок – це наш scriptPubKey. Якщо ми розглянемо ці компоненти докладніше, ми виявимо, що вони насправді складаються з бітів даних та блоків коду. У поєднанні вони створюють крихітну програму.
Коли ви здійснюєте транзакцію, ви транслюєте цю комбінацію в мережу. Кожна нода, яка її отримає, перевірить програму, і дізнається чи дана транзакція дійсна. Інакше транзакцію просто буде відхилено, і ви не зможете витратити заблоковані кошти.
Чеки (монети), які ви утримуєте, називаються
невитраченими результатами транзакцій (UTXO). Кошти може використовувати будь-хто, хто може надати ключ, що підходить до замку. Конкретніше, ключем є scriptSig, а замком – scriptPubKey.
Якщо UTXO знаходяться у вашому гаманці, вони, ймовірно, будуть мати умову, яка говорить, що
тільки людина, яка може довести володіння цим публічним ключем, може розблокувати ці кошти. Щоб розблокувати їх, ви надаєте scriptSig, який включає
цифровий підпис, використовуючи
приватний ключ, який зіставляється з публічним ключем, вказаним у scriptPubKey. Незабаром усе це стане зрозумілішим.
Script – це так звана мова, заснована на стеку. Все це означає, що коли ми читаємо набір інструкцій, ми поміщаємо їх у те, що можна представити як вертикальний стовпець. Наприклад, список A, B, C призведе до утворення стека з A внизу та C вгорі. Коли інструкції говорять нам щось зробити, ми працюємо з одним або декількома елементами, починаючи з вершини стека.
Елементи A, B і C додаються і витягуються зі стека.
Ми можемо розрізняти дані (наприклад:
підписи,
хеші, та приватні ключі) та інструкції (або
операційні коди). Інструкції знаходять дані та щось з ними роблять. Ось дуже простий приклад того, як може виглядати скрипт:
<xyz> <md5 hasher> <d16fb36f0911f878998c136191af705e> <check if equal>
Червоним кольором позначені дані, а синім – операційні коди. Ми читаємо зліва направо, тому спочатку поміщаємо у стек рядок <xyz>. Далі йде операційний код <md5 hasher>. Цього не існує у Bitcoin, але припустимо, що він видаляє верхній елемент стека (<xyz>) і хешує його за допомогою алгоритму MD5. Потім результати знову додаються до стека. Результат тут позначено як d16fb36f0911f878998c136191af705e.
Який збіг! Наш наступний елемент <d16fb36f0911f878998c136191af705e>, так що тепер у нашому стеку є два ідентичні елементи. Нарешті, <check if equal> бере два елементи зверху і перевіряє, чи вони однакові. Якщо так, то до стека додається <1>. Інакше додається <0>.
Ми підійшли до кінця нашого списку інструкцій. Наш скрипт міг зазнати невдачі з двох причин: якщо елемент, що залишився, дорівнював нулю, або якщо один з операторів викликав збій через невиконання деяких умов. У цьому прикладі ми не мали таких операторів, і ми отримали ненульовий елемент (<1>), тому наш скрипт був дійсним. Ці правила застосовуються і для реальних Bitcoin транзакцій.
Це була просто вигадана програма. А тепер давайте подивимося на справжні.
Pay-to-Pubkey (P2PK) надзвичайно проста. Вона включає блокування коштів певним публічним ключем. Якщо ви хочете отримувати кошти таким чином, ви повинні надати відправнику свій публічний ключ, а не Bitcoin адресу.
Перша транзакція між
Сатоші Накомото та Hal Finney у 2009 була P2PK транзакцією. Ця структура широко використовувалася на початку існування Bitcoin, але нині Pay-to-Pubkey-Hash (P2PKH) значною мірою замінила її.
Сценарій блокування для P2PK транзакції слідує формату <public key> OP_CHECKSIG. Досить просто. Ви могли здогадатися, що OP_CHECKSIG перевіряє підпис за наданим приватним ключем. Таким чином, наш scriptSig буде простим <signature>. Пам'ятайте, що scriptSig є ключем до замка.
Немає нічого простішого, ніж це. У стек додається підпис, за яким слідує приватний ключ. OP_CHECKSIG знаходить їх обох і перевіряє підпис за публічним ключем. Якщо вони співпадають, то до стека додається <1>. В іншому випадку додається <0>.
З причин, які ми докладно розглянемо у наступному розділі, P2PK насправді більше не використовується.
Pay-to-Pubkey-Hash (P2PKH) є найбільш поширеним типом транзакцій. Якщо ви не збираєтеся завантажувати застаріле програмне забезпечення, ваш гаманець, швидше за все, робить транзакції цього типу за замовчуванням.
ScriptPubKey у P2PKH має наступний вигляд:
OP_DUP OP_HASH160 <public key hash> OP_EQUALVERIFY OP_CHECKSIG
Перш ніж ми представимо scriptSig, давайте розберемося, що робитимуть нові операційні коди:
OP_DUP
OP_DUP бере перший елемент, і дублює його. Потім він додає обидва назад у стек. Зазвичай це робиться для того, щоб ми могли виконати операцію з дублікатом, не торкаючись оригіналу.
OP_HASH160
OP_HASH160 бере перший елемент і двічі хешує його. Перший раунд буде хешований за допомогою алгоритму SHA-256. Потім вихід SHA-256 хешується за допомогою алгоритму RIPEMD-160. Отриманий результат знову додається до стеку.
OP_EQUALVERIFY
OP_EQUALVERIFY об'єднує два інших оператори – OP_EQUAL та OP_VERIFY. OP_EQUAL бере два елементи і перевіряє, чи вони ідентичні. Якщо так, то до стеку додається 1. Якщо ні, то додається 0. OP_VERIFY бере верхній елемент і перевіряє, чи він має статус "True" (тобто ненульовий). Якщо це не так, то транзакція не виконується. У поєднанні з OP_EQUALVERIFY призводить до збою транзакції, якщо два верхні елементи не співпадають.
На цей раз scriptSig виглядає так:
<signature> <public key>
Вам необхідно надати підпис та відповідний приватний ключ, щоб розблокувати результати P2PKH.
Ви можете побачити, що відбувається на GIF-зображенні вище. Сценарій майже не відрізняється від сценарію P2PK. Ми просто додаємо додатковий крок, щоб перевірити, чи збігається відкритий ключ із хешем у скрипті.
Проте є на що звернути увагу. У сценарії блокування P2PKH публічний ключ не видно – ми можемо бачити лише його хеш. Якщо ми перейдемо до
блокчейн explorer і подивимося на вихід P2PKH, який ще не був витрачений, ми не зможемо визначити приватний ключ. Його можна знайти лише тоді, коли одержувач вирішує переказати кошти.
Це дає декілька переваг. По-перше, хеш публічного ключа простіше передати ніж повний публічний ключ. Сатоші запустив його у 2009 році саме з цієї причини. Хеш публічного ключа – це те, що ми сьогодні знаємо як Bitcoin адреса.
Друга перевага полягає в тому, що хеші з публічним ключем можуть забезпечити додатковий рівень захисту від
квантових обчислень. Оскільки наш приватний ключ невідомий, поки ми не витратимо кошти, іншим користувачам важко визначити приватний ключ. Щоб отримати його, їм довелося б відмінити два раунди хешування (RIPEMD-160 та SHA-256).
Pay-to-Script-Hash (P2SH) був дуже цікавою розробкою для
Bitcoin. Він дозволяє відправнику заблокувати кошти на хеші скрипта – їм не потрібно знати, що насправді робить скрипт. Наприклад, наступний хеш SHA-256:
e145fe9ed5c23aa71fdb443de00c7d9b4a69f8a27a2e4fbb1fe1d0dbfb6583f1
Вам не потрібно знати вхідний хеш, щоб заблокувати на ньому кошти. Тим не менш, особа, що витрачає повинна надати сценарій, який використовувався для його хешування, та потрібно задовільнити умови цього сценарію.
Наведений вище хеш був створений з наступного скрипту:
<multiply by 2> <4> <check if equal>
Якщо ви бажаєте витратити монети, прив'язані до цього scriptPubKey, ви не тільки надаєте ці команди. Вам також знадобиться scriptSig, що змушує завершений сценарій отримати значення "True". У цьому прикладі це елемент, який ви множите на 2 <multiply by 2>, щоб отримати результат <4>. Звичайно, це означає, що наш scriptSig просто <2>.
В реальності, scriptPubKey для результату P2SH:
OP_HASH160 <redeemScript hash> OP_EQUAL
Тут немає нових операторів. Але ми маємо новий елемент <redeemScript has>. Як випливає із назви, це хеш скрипта, який нам потрібно надати для викупу коштів (так званий redeemScript). ScriptSig змінюватиметься залежно від того, що знаходиться в redeemScript. Однак зазвичай ви виявите, що це комбінація підписів та прикріплених публічних ключів, за якими слідує (обов'язковий) redeemScript:
<signature> <public key> <redeemScript>
Наша оцінка трохи відрізняється від виконання стека, яке ми бачили до цих пір. Це відбувається двома частинами. Перша частина просто перевіряє, чи ви надали правильний хеш.
Зверніть увагу, що ми нічого не робимо з елементами, що передують redeemScript. На даний момент вони не використовуються. Ми добігли кінця цієї міні-програми, і верхній елемент не дорівнює нулю. Це означає, що вона дійсна.
Але ми ще не закінчили.
Ноди мережі розпізнають цю структуру як P2SH, тому вони фактично отримали елементи scriptSig, що очікують в іншому стеку. Ось де буде використовуватись підпис та приватний ключ.
До цього часу ми розглядали redeemScript як елемент. Але тепер це буде інтерпретуватися як інструкція, яка може бути будь-чим. Давайте візьмемо приклад скрипту блокування P2PKH, якому ми повинні надати <signature> та <public key>, які відповідають <public key hash> всередині <redeemScript>.
Після того, як ваш redeemScript був розширений, ви можете побачити, що ми маємо ситуацію, яка виглядає точно як звичайна транзакція P2PKH. Звідти ви просто запускаєте скрипт як зазвичай.
Ми продемонстрували тут те, що називається скриптом P2SH (P2PKH), але ви навряд чи знайдете один із таких у вільному доступі. Ніщо не заважає вам створити його, але це не дає додаткових переваг і, зрештою, займає більше місця в блоці (і, отже, коштує дорожче).
P2SH зазвичай зручний для таких речей, як
мультипідпис або транзакції, сумісні з
SegWit. Multisig транзакції можуть бути дуже великого розміру, оскільки для них може знадобитися декілька ключів. До реалізації Pay-to-Script-Hash відправник мав би перерахувати всі можливі публічні ключі у своєму скрипті блокування.
Але з P2SH немає значення, наскільки складні умови витрати коштів. Хеш redeemScript завжди має фіксований розмір. Таким чином витрати перекладаються на користувачів, які хочуть розблокувати скрипт блокування.
Сумісність із SegWit – ще один випадок, коли може стати в нагоді P2SH (ми докладно розглянемо відмінності в структурі транзакції у наступному розділі). SegWit був софт-форком, який призвів до зміни форматів блоків/транзакцій. Оскільки це додаткове оновлення, не все програмне забезпечення гаманців розпізнає зміни.
Не має значення, якщо клієнти переносять хеш скрипта SegWit до P2SH. Як і для всіх транзакцій цього типу, їм не потрібно знати, який буде код розблокування redeemScript.
Щоб зрозуміти формат транзакції в SegWit, вам просто потрібно знати, що у нас немає scriptSig і scriptPubKey. Тепер у нас також є нове поле під назвою witness. Дані, які ми використовували для зберігання scriptSig, переміщуються на witness, тому scriptSig залишається порожнім.
Якщо ви зустрічали адреси, що починаються з "bc1", це те, що ми називаємо SegWit-native (на відміну від SegWit-compatible, які починаються з "3", оскільки вони є P2SH адресами).
Pay-to-Witness-Pubkey-Hash (P2WPKH)
Pay-to-Witness-Pubkey-Hash (P2WPKH) – це SegWit-версія P2PKH. Наш witness виглядає так:
<signature> <public key>
Ви помітите, що це те ж саме, що і scriptSig від P2PKH. Тут scriptSig порожній. А scriptPubKey виглядає так:
<OP_0> <public key hash>
Виглядає трохи дивно, чи не так? Де операційні коди, щоб ми могли порівняти підпис, публічний ключ та його хеш?
Ми не показуємо тут додаткових операторів, тому що ноди, які отримують транзакцію, знають, що з нею робити, виходячи з довжини <public key hash>. Вони розрахують довжину і зрозуміють, що вона має виконуватися у тому стилі, як і стара добра транзакція P2PKH.
Неоновлені ноди не знають, як інтерпретувати транзакцію таким чином, але це не має значення. За старими правилами witness відсутній, тому вони читають порожній scriptSig та деякі дані. Вони оцінюють його і відзначають як дійсний. На їхню думку, будь-хто може витратити результат. Саме тому SegWit вважається зворотно сумісним
софт-форком.
Pay-to-Witness-Script-Hash (P2WSH)
Pay-to-Witness-Script Hash (P2WSH) – це новий P2SH. Якщо ви зайшли так далеко, ви, ймовірно, і самі зможете зрозуміти, як це виглядатиме, але ми все одно розглянемо його. Наш witness це те, що ми зазвичай поміщаємо в scriptSig. Наприклад, P2WSH, який обгортає транзакцію P2PKH, може виглядати приблизно так:
<signature 1> <public key>
Ось наш scriptPubKey:
<OP_0> <script hash>
Діють ті ж самі правила. SegWit ноди зчитують довжину хешу скрипту та визначають, що це результат P2WSH, який оцінюється аналогічно до P2SH. Тим часом, старі ноди просто бачать у цьому результат, який може витратити будь-хто.
У цій статті ми трохи дізналися про будівельні блоки Bitcoin. Давайте коротко їх підсумуємо:
Тип скрипта | Опис |
---|
Pay-to-Pubkey (P2PK) | Блокує кошти за допомогою певного приватного ключа |
Pay-to-Pubkey-Hash (P2PK) | Блокує кошти за допомогою певного хешу публічного ключа (тобто адреси) |
Pay-to-Script-Hash (P2SH) | Блокує кошти в хеш-коді скрипта, який може надати одержувач |
Pay-to-Witness-Pubkey-Hash (P2WPKH) | SegWit версія P2PK |
Pay-to-Witness-Script-Hash (P2WSH) | SegWit версія P2SH |
Як тільки ви поринете в Bitcoin, ви почнете розуміти, чому у нього такий великий потенціал. Транзакції можуть складатися з багатьох різних компонентів. Маніпулюючи цими будівельними блоками, користувачі отримують більшу гнучкість, коли справа доходить до встановлення умов того, як і коли можуть бути витрачені кошти.