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

День­ги всег­да кто‑то выпус­кал, учи­тывал и кон­тро­лиро­вал: государс­тво, банк или пла­теж­ная сис­тема. Так было до 2008 года, ког­да появил­ся бит­коин, который впол­не мож­но рас­смат­ривать не толь­ко в качес­тве самой популяр­ной на нашей пла­нете крип­товалю­ты, но еще и как сме­лый инже­нер­ный экспе­римент, пережив­ший мно­гочис­ленные кри­зисы, зап­реты, взло­мы бирж и «похоро­ны» в прес­се. Бит­коин тра­дици­онно счи­тают сво­его рода эта­лоном в мире сов­ремен­ной крип­ты — все осталь­ные про­екты, появив­шиеся поз­же, в той или иной сте­пени ори­енти­рова­лись имен­но на него.

При этом вок­руг бит­коина сущес­тву­ет немало заб­лужде­ний. Мно­гие пред­став­ляют его как некую вир­туаль­ную «монету» или набор сче­тов, подоб­ных бан­ков­ским, одна­ко в дей­стви­тель­нос­ти его устрой­ство нам­ного инте­рес­нее. Что­бы глуб­же понять прин­ципы работы крип­товалют в целом, давай пос­мотрим на бит­коин, как на слож­ную, но инте­рес­ную инже­нер­ную конс­трук­цию.

info

Это чет­вертая статья из цик­ла, пос­вящен­ного тех­ничес­ким осно­вам устрой­ства крип­товалют. Пре­дыду­щая носит наз­вание «Крип­та с нуля. Выяс­няем, как работа­ет май­нинг».

 

Транзакции: входы и выходы (UTXO)

Ес­ли ты живешь не на каком‑нибудь заб­рошен­ном остро­ве пос­реди Тихого оке­ана, ты пос­тоян­но поль­зуешь­ся бан­ков­ски­ми при­ложе­ниями и прек­расно зна­ешь, как они работа­ют: авто­ризо­вал­ся в прог­рамме и видишь на экра­не смар­тфо­на баланс всех сво­их сче­тов и активных карт. Если нуж­но, перево­дишь часть денег дру­гому челове­ку по номеру телефо­на или рек­визитам кар­ты, либо опла­чива­ешь какую‑то услу­гу, а может быть, куп­ленный товар. Это называ­ется «балан­совой моделью», и имен­но на ней пос­тро­ено боль­шинс­тво бан­ков­ских пла­теж­ных сис­тем: банк хра­нит информа­цию о том, сколь­ко денег находит­ся на каж­дом сче­те, а перевод сво­дит­ся к изме­нению двух чисел в базе дан­ных — у отпра­вите­ля сум­ма умень­шает­ся, у получа­теля уве­личи­вает­ся.

Крип­товалют­ный кошелек с точ­ки зре­ния поль­зовате­ля устро­ен прак­тичес­ки так же, вот толь­ко здесь есть один важ­ный тех­ничес­кий нюанс. Нес­мотря на то, что кошель­ки тоже показы­вают при­выч­ный баланс, сама сеть никаких балан­сов не хра­нит. В блок­чей­не нет записи вида «у Маши 3 BTC, у Васи 1 BTC», вмес­то это­го сис­тема опе­риру­ет тран­закци­ями и их резуль­татами. Каж­дый получен­ный перевод соз­дает новый фраг­мент цен­ности, которым впос­ледс­твии мож­но рас­порядить­ся в дру­гой тран­закции.

Та­кой под­ход осно­ван на модели UTXO (Unspent Transaction Output) — неиз­расхо­дован­ных выходов тран­закций. На пер­вый взгляд это выг­лядит как нез­начитель­ная тех­ничес­кая деталь, но имен­но на ней пос­тро­ена вся эко­номи­ка бит­коина, и имен­но это отли­чает его от тра­дици­онных пла­теж­ных сис­тем. Ког­да кошелек показы­вает поль­зовате­лю баланс, он фак­тичес­ки каж­дый раз вычис­ляет его самос­тоятель­но, сум­мируя все UTXO, которы­ми вла­делец может рас­поряжать­ся в дан­ный момент вре­мени.

Каж­дая тран­закция в Bitcoin сос­тоит из двух основных час­тей: вхо­дов (inputs) и выходов (outputs). Вхо­ды ука­зыва­ют, какие ранее соз­данные UTXO будут пот­рачены, а выходы — кому будут при­над­лежать новые UTXO пос­ле завер­шения перево­да. Допус­тим, нек­то получил два перево­да: 0,3 BTC и 0,7 BTC. В блок­чей­не это будут два незави­симых UTXO. Если затем поль­зователь захочет отпра­вить кому‑то 0,8 BTC, кошель­ку при­дет­ся исполь­зовать оба этих выхода в качес­тве вхо­дов новой тран­закции. Пос­ле это­го ста­рые UTXO будут счи­тать­ся пот­рачен­ными и боль­ше не смо­гут исполь­зовать­ся в качес­тве вхо­дов новых тран­закций, а вмес­то них появят­ся новые.

Здесь воз­ника­ет важ­ная осо­бен­ность. Тран­закция дол­жна пот­ратить вхо­ды целиком, вер­нее, UTXO, вклю­чен­ные во вхо­ды, тра­тят­ся пол­ностью. Если на вход пода­ются UTXO сум­марной сто­имостью 1 BTC, а дру­гому поль­зовате­лю сети нуж­но отпра­вить толь­ко 0,8 BTC, оставши­еся средс­тва нель­зя прос­то оста­вить «на вхо­де». Поэто­му обыч­но соз­дает­ся допол­нитель­ный выход, воз­вра­щающий оста­ток отпра­вите­лю. Его называ­ют change output — выход со сда­чей. В резуль­тате одна типич­ная тран­закция в сети бит­коин выг­лядит не как прос­той перевод денег от одно­го поль­зовате­ля дру­гому, а как опе­рация по унич­тожению ста­рых UTXO и соз­данию новых. Имен­но поэто­му в блок­чей­не невоз­можно най­ти баланс какого‑либо адре­са в готовом виде: его при­ходит­ся каж­дый раз вычис­лять по совокуп­ности еще не пот­рачен­ных выходов.

Эту тех­нологию мож­но наг­лядно объ­яснить на прос­том при­мере: положим, кто‑то зап­латил тебе за работу купюрой номина­лом 1000 руб­лей, а тебе нуж­но отдать дру­гому челове­ку долг в 700 руб­лей. Ты не можешь прос­то отор­вать от тысяч­ной купюры нуж­ный кусок, вмес­то это­го ты отдашь кре­дито­ру купюру целиком и поп­росишь сда­чу 300 руб­лей. Таким обра­зом, у тебя обра­зует­ся две новых тран­закции: 700 (долг) и 300 (сда­ча, change output), при­чем эти 300 руб­лей вер­нутся тебе обратно и затем могут быть исполь­зованы в сле­дующей тран­закции. То есть, сда­ча ста­новит­ся новым UTXO, при­над­лежащим отпра­вите­лю 1000 руб­лей, и этот UTXO потом мож­но исполь­зовать в будущих денеж­ных опе­раци­ях.

Ра­зуме­ется, реаль­ные тран­закции в сети бит­коин час­то ока­зыва­ются слож­нее нашего при­мера. Одна тран­закция может исполь­зовать сра­зу нес­коль­ко вхо­дов и соз­давать нес­коль­ко выходов для раз­ных получа­телей. Одна­ко базовый прин­цип оста­ется неиз­менным: ста­рые UTXO рас­ходу­ются целиком, а вза­мен появ­ляют­ся новые. Кро­ме того, в при­мере с тысяче­руб­левой купюрой мы не учли еще один важ­ный эле­мент любой тран­закции — комис­сию сети.

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

Ког­да ты фор­миру­ешь тран­закцию, ты не ука­зыва­ешь комис­сию как отдель­ное поле или отдель­ный выход май­неру. Ты прос­то соз­даешь на выходе сум­му нем­ного мень­ше, чем сум­ма вхо­да, а раз­ница меж­ду вхо­дами и выхода­ми ниг­де явно не записа­на как "комис­сия". Май­нер же при сбор­ке бло­ка сам счи­тает эти «невос­тре­бован­ные» раз­ницы по всем тран­закци­ям и прис­ваивает их себе через спе­циаль­ную тран­закцию, которая называ­ется coinbase. Ины­ми сло­вами, комис­сия — это эмер­джентное свой­ство, воз­ника­ющее из раз­ницы сумм, а не нап­равлен­ный пла­тёж какой‑то кон­крет­ной величи­ны.

Бе­зус­ловно, ты можешь пот­ребовать вер­нуть тебе всю сда­чу целиком до пос­ледне­го цен­та, тог­да раз­ница меж­ду вхо­дами и выхода­ми будет рав­на нулю. Это абсо­лют­но легаль­ная с точ­ки зре­ния про­токо­ла тран­закция. Но в этом слу­чае узлы сети (nodes) могут отка­зать­ся её при­нимать в мем­пул (оче­редь непод­твержден­ных тран­закций). У боль­шинс­тва реали­заций блок­чей­на есть нас­трой­ка минималь­ной relay fee — по умол­чанию тран­закции с нулевой или край­не низ­кой комис­сией за байт мно­гие ноды прос­то не будут пересы­лать даль­ше по сети. Кро­ме того, май­неры не обя­заны вклю­чать такую тран­закцию в блок. Май­нер выбира­ет, какие тран­закции из мем­пула положить в блок, и дела­ет это, как пра­вило, ори­енти­руясь на комис­сию за байт (sat/vByte). Тран­закция с нулевой комис­сией невыгод­на, и при запол­ненных бло­ках она будет вытес­нена более "вкус­ными" тран­закци­ями. В конеч­ном ито­ге такая тран­закция может завис­нуть в мем­пуле надол­го — иног­да на дни, недели или вооб­ще не попасть в блок, если её прос­то вычис­тят из мем­пула по тайм‑ауту.

Вот как может, нап­ример, выг­лядеть тран­закция в блок­чей­не бит­коин — мы получи­ли два вхо­дящих перево­да на сум­мы 0.3 и 0.7 BTC, переве­ли дру­гому поль­зовате­лю 0.8 BTC, получи­ли сда­чу 0.1998 BTC и зап­латили за всю эту исто­рию комис­сию в раз­мере 0.0002 BTC. Фак­тичес­ки, мы получи­ли 1 BTC и пот­ратили 1 BTC, при этом у нас обра­зовал­ся неиз­расхо­дован­ный оста­ток в раз­мере 0.1998 BTC, который мож­но пот­ратить в сле­дующий раз.

Inputs:
0.3 BTC (UTXO #1)
0.7 BTC (UTXO #2)
Outputs:
0.8 BTC -> получатель
0.1998 BTC -> сдача отправителю
Комиссия:
0.0002 BTC
 

Откуда сеть узнает, какие монеты можно потратить?

Как ты уже понял, вход тран­закции не содер­жит сами монеты и даже не хра­нит пол­ную информа­цию о пре­дыду­щем перево­де. Он лишь ссы­лает­ся на кон­крет­ный выход более ран­ней тран­закции, который нуж­но пот­ратить. Такая ссыл­ка сос­тоит из иден­тифика­тора пре­дыду­щей тран­закции (txid) и номера выхода внут­ри нее (vout). Здесь воз­ника­ет глав­ный воп­рос: отку­да сеть зна­ет, что человек, соз­дающий новую тран­закцию, дей­стви­тель­но име­ет пра­во пот­ратить этот выход?

От­вет спря­тан в самом устрой­стве UTXO. Каж­дый выход тран­закции соз­дает­ся не прос­то с ука­зани­ем сум­мы, а с опре­делен­ным усло­вием рас­ходова­ния. В прос­тей­шем слу­чае это усло­вие выг­лядит так: «этот выход может пот­ратить толь­ко тот, кто предъ­явит циф­ровую под­пись, соот­ветс­тву­ющую опре­делен­ному откры­тому клю­чу».

Ког­да вла­делец средств соз­дает новую тран­закцию, он не прос­то ука­зыва­ет ссыл­ку на ста­рый выход. Он добав­ляет дан­ные, которые поз­воля­ют про­верить выпол­нение это­го усло­вия: циф­ровую под­пись, соз­данную с помощью сво­его собс­твен­ного зак­рытого клю­ча. Получа­ется сво­еоб­разная пара: пре­дыду­щий выход содер­жит пра­вило, а новый вход содер­жит доказа­тель­ство того, что это пра­вило выпол­нено. За это отве­чает спе­циаль­ный Locking script (скрипт бло­киров­ки, так­же называ­емый scriptPubKey) —фраг­мент кода в выходе (output) тран­закции, который зада­ет усло­вия, при которых эти средс­тва мож­но пот­ратить. Если усло­вия не выпол­нены — средс­тва оста­ются недос­тупны­ми.

Ес­ли под­пись кор­рек­тна, сеть при­нима­ет этот вход и счи­тает выход пот­рачен­ным. Если под­пись не под­ходит или ука­зан­ный выход уже был исполь­зован ранее, тран­закция будет откло­нена. Важ­но понимать: сама ссыл­ка на UTXO не дает пра­во рас­поряжать­ся средс­тва­ми. Она лишь ука­зыва­ет, какой имен­но выход пыта­ется пот­ратить поль­зователь. Пра­во на рас­ходова­ние под­твержда­ется толь­ко кор­рек­тной циф­ровой под­писью, которая в клас­сичес­ких тран­закци­ях хра­нит­ся в спе­циаль­ном поле scriptSig, а в тран­закци­ях SegWit (это обновле­ние про­токо­ла Bitcoin, внед­ренное в августе 2017 года) вынесе­на в отдель­ную струк­туру witness.

Про­ще говоря, каж­дый вход говорит сети: «я хочу исполь­зовать вот этот кон­крет­ный выход, соз­данный вот такой тран­закци­ей». Узлы сети находят ука­зан­ную запись в блок­чей­не, про­веря­ют, что этот выход дей­стви­тель­но сущес­тву­ет и еще не был пот­рачен ранее, а затем про­веря­ют, име­ет ли отпра­витель пра­во им рас­поряжать­ся.

Имен­но поэто­му в Bitcoin фак­тичес­ки переда­ются не сами монеты, а пра­во пот­ратить ранее соз­данный выход. Новая тран­закция зак­рыва­ет ста­рые обя­затель­ства и соз­дает новые — уже для сле­дующих вла­дель­цев. При этом сущес­тву­ет нес­коль­ко вари­антов усло­вий, при которых мож­но пот­ратить выход, и они опре­деля­ются адре­сом получа­теля каж­дого кон­крет­ного выхода, вер­нее, типом адре­са, на который отправ­лены средс­тва.

 

Адрес в Bitcoin

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

Од­нако внут­ри сети адрес — это не иден­тифика­тор сче­та, на котором лежат монеты, и не запись в базе дан­ных вида «это­му адре­су при­над­лежит столь­ко‑то бит­коинов» (таких записей в блок­чей­не вооб­ще нет). Как я уже упо­минал чуть рань­ше, ког­да поль­зователь отправ­ляет кому‑то бит­коины, в блок­чейн попада­ет не сам адрес получа­теля, а выход тран­закции (output) с опре­делен­ным сце­нари­ем про­вер­ки. Вмес­то того что­бы переда­вать друг дру­гу длин­ный набор тех­ничес­ких дан­ных, поль­зователь отправ­ляет корот­кую стро­ку опре­делен­ного фор­мата. Кошелек пре­обра­зует ее в соот­ветс­тву­ющий сце­нарий бло­киров­ки под наз­вани­ем locking script, который затем записы­вает­ся в тран­закцию. Вот адре­са как раз и соот­ветс­тву­ют раз­ным типам сце­нари­ев рас­ходова­ния средств.

 

Legacy-адреса: первый массовый формат

Са­мыми ран­ними и до сих пор узна­ваемы­ми явля­ются адре­са фор­мата Legacy. Они начина­ются с циф­ры 1. Вот, нап­ример, адрес пер­вого бло­ка Bitcoin, так называ­емый «генезис‑адрес», содер­жащий наг­раду за блок, соз­данный самим Сатоши Накамо­то:

1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa

На тех­ничес­ком уров­не такие адре­са соот­ветс­тву­ют схе­ме P2PKH (Pay-to-Public-Key-Hash). При соз­дании пла­тежа средс­тва бло­киру­ются с усло­вием: пот­ратить их смо­жет тот, кто предъ­явит циф­ровую под­пись для соот­ветс­тву­юще­го пуб­лично­го клю­ча.

Схе­ма работа­ет сле­дующим обра­зом: из откры­того клю­ча с помощью крип­тогра­фичес­ких опе­раций получа­ется хеш, этот хеш кодиру­ется в удоб­ный для поль­зовате­ля фор­мат и ста­новит­ся адре­сом. Ког­да дру­гой человек отправ­ляет бит­коины на этот адрес, он фак­тичес­ки соз­дает UTXO, свя­зан­ный с этим хешем. При пос­леду­ющей тра­те получа­тель пре­дос­тавля­ет откры­тый ключ и под­пись. Сеть про­веря­ет, соот­ветс­тву­ет ли откры­тый ключ адре­су и дей­стви­тель­но ли под­пись соз­дана вла­дель­цем зак­рытого клю­ча.

Не­дос­таток Legacy-адре­сов зак­люча­ется в том, что они исполь­зуют ста­рый фор­мат тран­закций, где подоб­ные под­писи занима­ют замет­ное мес­то внут­ри самой тран­закции. Это уве­личи­вает раз­мер перево­дов и, соот­ветс­твен­но, комис­сию при высокой наг­рузке сети.

 

SegWit: адреса нового поколения

В 2017 году в сети бит­коин было акти­виро­вано круп­ное обновле­ние про­токо­ла Segregated Witness (SegWit). Одной из его целей было изме­нение струк­туры тран­закций: дан­ные под­писей были отде­лены от основной час­ти тран­закции и вынесе­ны в спе­циаль­ную область witness.

Для поль­зовате­лей это, сре­ди про­чего, про­яви­лось в виде нового фор­мата адре­сов, начина­ющих­ся с bc1:

bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kygt080

Та­кие адре­са обыч­но соот­ветс­тву­ют схе­мам P2WPKH (Pay-to-Witness-Public-Key-Hash). Они поз­воля­ют умень­шить раз­мер тран­закций по срав­нению с Legacy и тем самым сни­зить комис­сию. При­том сама логика про­вер­ки пра­ва на рас­ходова­ние UTXO прин­ципи­аль­но не изме­нилась. Изме­нилось мес­то, где хра­нят­ся дан­ные для про­вер­ки и то, как они учи­тыва­ются при рас­чете раз­мера тран­закции.

Ког­да кто‑то отправ­ляет бит­коины на адрес bc1q..., в блок­чейн попада­ет не сам адрес, а выход тран­закции с усло­вием при­мер­но такого вида: «Этот UTXO может пот­ратить толь­ко тот, кто предъ­явит откры­тый ключ, хеш которо­го равен ука­зан­ному зна­чению, и кор­рек­тную под­пись, соз­данную соот­ветс­тву­ющим зак­рытым клю­чом».

Продолжение доступно только участникам

Материалы из последних выпусков становятся доступны по отдельности только через два месяца после публикации. Чтобы продолжить чтение, необходимо стать участником сообщества «Xakep.ru».

Присоединяйся к сообществу «Xakep.ru»!

Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее

  • Подпишись на наc в Telegram!

    Только важные новости и лучшие статьи

    Подписаться

  • Подписаться
    Уведомить о
    2 комментариев
    Старые
    Новые Популярные
    Межтекстовые Отзывы
    Посмотреть все комментарии