Bitcoin in a nutshell — Transaction

Якщо говорити про вже існуючу банківську систему, то транзакція усередині якого-небудь Альфа-банку — це просто редагування таблиці балансів, де зменшується кількість навпроти одного імені і збільшується навпроти іншого. У випадку з міжбанківськими перекладами підключаються деякі сторонні організації, наприклад SWIFT, але, по суті, все працює приблизно так само.
Коли ми маємо справу з фінансовою системою на основі блокчейна, то процес грошового переказу виглядає зовсім інакше. В Bitcoin не існує ніякої загальної таблиці виду <адреса, баланс>, рівно як і не існує регулятора, який би цю таблицю редагував. У цій статті я покажу, що з себе представляє транзакція в Bitcoin, як вона будується, і поясню, навіщо ж всередині Bitcoin додано свою мову програмування, про який всі чули, але ніхто не бачив.
мем
Table of content
  1. Вступ
  2. Inputs & outputs
  3. Fee
  4. UTXO
  5. Txn structure
  6. Script
  7. Lock & unlock transaction
  8. Password script
  9. Pay to Public Key Hash (P2PKH)
  10. P2P storage
  11. Links
Вступ
Як я вже сказав вище, в Bitcoin не існує ніякої єдиної структури, в якій кожному адресою був би відповідав його поточний баланс. Замість цього використовується той самий горезвісний блокчейн, тобто зберігаються взагалі всі транзакції. Для простоти поки що можете вважати, що це повідомлення виду
<address 1> sent <amount> BTC to <address 2>

А значить, якщо пройтися по всьому блокчейну, то можна порахувати, скільки монет "належить" конкретною адресою.
Inputs & outputs
Реальна транзакція в мережі Bitcoin, насправді, трохи складніше описаної вище. Насправді, це деяка громіздка структура, головними складовими якої є входи (inputs) і виходи (outputs).
Inputs — це транзакції, на які ви "посилаєтеся". Уявімо, що на вашу адресу X коли-то було відправлено три операції:
  • TXN_ID — 123456, VALUE — 40 BTC
  • TXN_ID — 6453795, VALUE — 10 BTC
  • TXN_ID — 888888, VALUE — 100 BTC
Якщо вам потрібно витратити, наприклад, 45 BTC, то ви можете послатися на транзакцію 888888, або відразу на дві транзакції: 123456 і 6453795. При бажанні ви можете навіть послатися на всі три транзакції, правда незрозуміло навіщо.
Outputs — дослівно "виходи". Поки що можете вважати, що це адреси (хоча це не так), на які в результаті виконання транзакції будуть "відправлені" кошти. Виходів може бути кілька, і кожному з них зазначається своя сума.
На картинці нижче створюється нова транзакція C, яка посилається на два виходи — A і B. В результаті на вході у транзакції виходить 0.008 BTC, які потім розділяються на два виходи — на першу адресу відправляється 0.003 BTC, а на другий 0.004 BTC.
Inputs,outputs
Можливість вказати відразу кілька виходів — це дуже важлива фіча, тому що транзакцію (а якщо точніше — її вихід) можна використовувати як вхід тільки один раз і тільки цілком. Тобто якщо у вас є вхідна трансакція на 10 BTC, а вам потрібно витратити 8 з них у якому-небудь Старбаксе, ви просто створюєте транзакцію з одним входом і двома виходами: на 8 BTC в магазин і на 2 BTC назад на свою адресу. Якщо ж ви створите транзакцію, в якій сума виходів менше суми входів (як на картинці), то різниця відправляється на адресу майнера, записав вашу транзакцію в блок.
Fee
Саме ця різниця між сумою входів і сумою виходів і називається transaction fee, тобто комісія за транзакцію. Вона є другим за важливістю джерелом доходу для майнер і саме від неї залежить включення транзакції в блокчейн. Це пов'язано з тим, що у кожного майнера існує певний пул неперевірених транзакцій, які претендують на потрапляння в блок, і, як правило, майнер просто сортує їх за спаданням комісії, тим самим максимізуючи свій прибуток. Тому чим більше комісія, тим вище ви опинитеся в черзі і тим швидше пройде ваш платіж.
На картинці нижче ви можете бачити фоторобот майнера, якому прийшла транзакція з комісією в 135.000$.

UTXO
Як тільки нова транзакція занесена в блокчейн, її виходи можуть бути використані в якості входів. Для таких, поки ще непотраченних виходів, існує спеціальна назва — UTXO (unspent transaction output). Як я вже говорив, кожен вихід може бути використаний в якості входу тільки один раз, тому на практиці інтерес представляють саме невитрачене виходи, а вже використані зберігаються швидше як данина безпеки системи.
BTW під UTXO часто розуміють увесь масив непотраченних виходів, хоча виховані молоді люди повинні писати UTXO pool ну або в крайньому випадку UTXO set.
Повертаючись до початку статті, тепер вам повинно бути зрозуміло, що для підрахунку балансу адреси не потрібно перебирати весь блокчейн, а досить обійтися тільки перебором UTXO pool, що, очевидно, швидше.
Structure
Загальний вигляд транзакції описаний в офіційної специфікації протоколу, тут же я наведу живий приклад, взятий з блогу Ken Shirriff.

За якийсь загадкової причини, value і previous output hash повинні бути представлені в little endian формі, тобто в нашому випадку хеш транзакции, на яку ми посилаємося, взагалі-то дорівнює 81 b4 c8 32..., хоча в транзакції він записується у вигляді ...32 c8 b4 81. Точно так само сума транзакції дорівнює 0.00091234 BTC або 0x016462 в hex, але в протоколі вона записується як 62 64 01 00 00 00 00 00.
BTW хеш транзакції вважається вкрай просто — берете всю транзакцію у вигляді послідовності байт (у прикладі вище, виходить рядок виду 010000000148....00), два рази вважаєте від неї хеш SHA-256 і уявляєте результат в little endian формі.
previous output index — посилаємося не на саму трансакцію, хеш якої зазначений в previous output hash, а на один з її виходів. В цьому параметрі ми й вказуємо, який конкретно вихід нас цікавить, нумерація починається з нуля. До речі, в тексті я часто буду говорити саме про "посиланням на транзакцію", але це тільки заради виразності мови.
block time lock — цей параметр досить рідко використовується на практиці. Якщо він не дорівнює 0 і менше 500 млн, то це номер блоку, починаючи з якого даною транзакцією можна скористатися в якості входу. Так як в середньому блоки з'являються раз на 10 хвилин, то нескладно підрахувати час, коли транзакція "відкриється".
Якщо lock time більше 500 млн, то він означає UNIX timestamp, починаючи з якого транзакція стане доступна. У нашому випадку там стоїть 0, тобто транзакція доступна відразу.
sequence — ця фіча більше не використовується, почитати про неї можна тут.
Установки зі словом script в назві істотно складніше, про них буде розказано нижче.
Script
Швидше за все ви вже чули, що в мережі Bitcoin існує механізм, заснований на криптостойких алгоритмах + парі приватний / публічний ключ, що дозволяє створити систему, в якій тільки власник приватного ключа може скористатися монетами, асоційованими з адресою, отриманим з цього ключа. Зараз я покажу, як це реалізується "під капотом".
Почнемо з того, що всередині Bitcoin існує свій власний мову програмування, названий Script. Ось що про нього пише Bitcoin wiki:
Bitcoin uses a scripting system for transactions. Forth-like, Script is simple, stack-based, and processed from left to right. It is purposefully not Тьюрінга-complete, with no loops.
Суть в тому, що мова простий як пробка, stack-based і Тьюринг-неповний. Ось приклад типової програми:
1 OP_DUP OP_DUP 5 OP_HASH160

Кожна інструкція називається opcodeвсього їх близько 80, так що мова дійсно досить примітивний. На картинці нижче зображено процес виконання програми
2 3 OP_ADD 5 OP_EQUAL
:
opadd
Lock & unlock transaction
Повернімося до мови трохи пізніше, а спочатку давайте розберемося, навіщо він взагалі потрібен.
Для цього згадуємо структуру транзакції і два параметри: scriptSig і scriptPubKey. На відміну від інших параметрів, призначення цих двох взагалі не очевидно, і імхо це найскладніше, що є в Bitcoin.
Я бачив багато спроб пояснити (як правило невдалих), що ж із себе представляють скрипти в Bitcoin і як треба їх сприймати на інтуїтивному рівні. Тим не менше я ризикну і спробую навести ще одну аналогію. Для цього давайте розглянемо заповіт, на зразок такого:
1.000.000$ переходять до Аліси тільки після того, як їй виповниться 18 років
В цьому випадку сам текст заповіту — це деяка умова, при якому можна скористатися грошима (читай можна скористатися транзакцією на 1.000.000$ як входом), а ксерокопія паспорта в 19 років — це доказ того, що умова виконана і саме час отримати гроші.
Саме для того, щоб задати умову, при якому можна буде витратити вихід, і для можливості підтвердити те, що умова виконано та потрібний SCRIPT, приватні / публічні ключі та інші складності.
У разі Bitcoin, заповіт — це locking script, який вказується в транзакції усередині поля pk_script. Його ще часто називають scriptPubKey з-за того, що найчастіше це програма, що містить публічний ключ або адресу, хоча, взагалі кажучи, він може не мати нічого спільного з криптографією.
Свого роду "доказ" того, що умова locking script виконано, називається unlocking script, пишеться в полі signature script і часто називається scriptSig, здогадайтеся чому.
Сам механізм перевірки скрипта на валідність дуже проста — для цього потрібно з'єднати unlocking script + locking script і запустити отриману програму як одне ціле. Якщо після виконання, зверху стека залишиться
TRUE
, то транзакція валидна, і невалидна в будь-якому іншому випадку.
Multiplication-based script
Швидше за все, ви нічого не зрозуміли, тому давайте напишемо який-небудь максимально простий скрипт, щоб остаточно в усьому розібратися. Ідея полягає в тому, щоб заблокувати кошти з допомогою якого-небудь числа, наприклад
370
. Locking script буде виглядати як
OP_MUL 370 OP_EQUAL
для того, щоб розблокувати транзакцію, потрібно буде вказати два числа, що дають 370 у творі.
Для експериментів зі Script скористаємося онлайн майданчиком для запуску і дебага Bitcoin скриптів. unlocking script запишемо наприклад
10 37
. Проверяем:
OP_MUL
Pay to Public Key Hash (P2PKH)
P2PKH використовується, напевно, 99 транзакції з 100, так що варто розуміти, як він працює. Ось його загальний вигляд:
p2pkh2
Цей скрипт відомий з самого появи Bitcoin і, можливо, придуманий самим Сатоши. Саме він виконує те завдання, про яке я писав вище: зробити так, щоб тільки власник приватного ключа зміг скористатися монетами, асоційованими з адресою, отриманим з цього ключа.
На пальцях це виглядає наступним чином: нехай другові B належить приватний ключ P. Він отримує з нього публічний ключ K адреса A та повідомляє адресу вам. Далі ви відправляєте на адресу A 1 BTC і в полі locking script пишіть приблизно наступне:
Тільки той, хто володіє приватним ключем для адреси A, зможе витратити цю транзакцію. В якості доказу запишіть у unlocking script, по-перше, публічний ключ K, а по-друге підпис свій транзакції приватним ключем P.
Коли B вирішить використовувати вашу транзакцію в якості входу, то він створить свою, наприклад, на 0.5 BTC, а в полі unlocking script вставить підпис своєї транзакції приватним ключем P
<sig>
і публічний ключ K
<PubK>
.
p2pkh1
script
  1. Підпис транзакції додається в стек
  2. Публічний ключ додається в стек
  3. OP_DUP
    бере верхній елемент стеку і дублює його, тепер в стеку зверху два публічних ключа
  4. OP_HASH160
    замінює верхній елемент стеку на його хеш
    RIPEMD160(SHA256(x))
  5. В стек додається такий же хеш публічного ключа, але вже посчитанный відправником транзакції. Якщо ви уважно читали [Bitcoin in a nutshell — Cryptography](), то для вас має бути очевидно, що
    RIPEMD160(SHA256(public_key))
    та адресу — це в принципі одне і те ж.
  6. OP_EQUALVERIFY
    видаляє два верхні елементи стека, і якщо вони не рівні, то виконання програми переривається з помилкою
  7. OP_CHECKSIG
    перевіряє підпис на відповідність транзакції. Якщо все вірно, то видаляє підпис, видаляє публічний ключ і додає
    TRUE
P2P storage
xzibit
Одне з найцікавіших властивостей Bitcoin, так і технології блокчейн взагалі, — це незмінюваність і гіпотетична "вічність" все, що туди потрапляє. Недивно, що з часом знайшлися люди, які захотіли використати це в своїх цілях. І перше, що спало їм на голову — спробувати зберегти в блокчейн які-небудь сторонні дані і отримати P2P дропбокс.
Я думаю ви вже зрозуміли, як це робиться. Беремо рядок
Make great America again
і просто записуємо її в locking script. Це все ще буде цілком коректний скрипт, інша справа, що до нього не вийде придумати такий unlocking script, щоб розблокувати кошти. Але якщо ви відправите на вихід з таким сриптом, умовно кажучи, 0.0000001 BTC, то в принципі і не шкода. Єдине обмеження — це розмір вашої транзакції. Вважайте, що вона не може бути більше 100 КБ, хоча в реальності там все трохи складніше, можете почитати здесь.
Ясна річ, що такий стан справ з душі не всім. У Bitcoin і так великі проблеми з масштабністю, а тут ще й блокчейн, без того немаленький, починає забиватися всякими лівими даними. Більш того, пам'ятаємо, що такі транзакції не можна витратити, а значить вони назавжди залишаться в UTXO pool, нітрохи не краще.
Для того, щоб досягти компромісу, був доданий
OP_RETURN
, який дозволяє "легально" зберігати в блокчейне до 40 КБ даних.
OP_RETURN is a script opcode used to mark a transaction output as invalid. Since the data after OP_RETURN are irrelevant to Bitcoin payments, arbitrary data can be added into the output after an OP_RETURN — Bitcoin wiki
Ось так виглядає найпростіший locking script з його участю:
OP_RETURN <40kb data>
. Що примітно, вихід з таким скриптом набуває статус provably unspendable, доказово непотрачиваемый. З-за цього він навіть не потрапляє в UTXO pool, тим самим заощаджуючи дорогоцінний місце. Інші причини використовувати
OP_RETURN <data>
замість
<data>
ви можете знайти на здесь.
Спойлер — їх немає, якщо ви звичайно не переконаний альтруїст.
Links
Джерело: Хабрахабр

0 коментарів

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