K popisu anatomie bitcoinové transakce byla náhodně vybrána segwitová transakce 40e285bd2b757bf455f9e3ce92aa8e51910f88a1ca9dd497465d346310e7df11 z bloku č. 676274. Jedná se o typickou transakci s jedním vstupem a dvěma výstupy – to znamená, že transakce utrácí (spotřebovává) jeden UTXO a dva nové UTXO vytváří. Následuje hexadecimální reprezentace serializace transakce:
locktime; může udávat č. bloku, případně čas, po kterém teprve může být tx vytěžena
4 bajty LE
Serializace této tx čítá 222 bajtů z čehož je 109 bajtů „segwitových“ (marker, flag, witness). Pro výpočet váhy tx vynásobíme počet bajtů „nesegwitových“ dat čtyřmi a výsledek přičteme k počtu segwitových bajtů (113 × 4 = 452; 452 + 109 = 561 WU). Když poté vydělíme WU čtyřmi, dostaneme tzv. virtuální velikost (561 ÷ 4 = 140,25 vB).
Odečtením součtu částek výstupů od součtu částek vstupů (hodnoty částek vstupů nejsou součástí dat této tx; je potřeba se podívat do předchozích) zjistíme absolutní hodnotu poplatku (9566387 − (9545393 + 20000) = 994). Relativní hodnoty poplatků pak zjistíme už prostým dělením velikostí, resp. virtuální velikostí (994 ÷ 222 = 4,5 satů/bajt; 994 ÷ 140 = 7,1 satů/vBajt).
Anatomie skriptu
Prostřednictvím skriptovacího jazyka Bitcoinu, který je záměrně co možná nejjednodušší (a turingovsky neúplný) stanovujeme a následně i vyhodnocujeme podmínky utracení každého UTXO. V našem případě útraty druhého výstupu (počítáno od nuly je to výstup číslo jedna) předchozí transakce 61149…17 musela „naše“ transakce dodat takový podpisový skript (scriptSig), který bude vyhodnocen v kombinaci s uzamykacím skriptem (scriptPubKey) utráceného výstupu jako pravdivý. Skriptovací jazyk Bitcoinu pracuje se zásobníkem a samotný skript je složen z operátoru a elementů (dat).
Než zjistíme, jakým způsobem došlo k vyhodnocení skriptů naší rozpitvané segwitové (je utráceno segwitové UTXO) transakce, podíváme se na to, jak tuto transakci vidí předsegwitové uzly. Takový starý uzel vezme scriptPubKey odkazovaného výstupu předešle tx (61149…17:1):
Před tento uzamykací skript (scriptPubKey) předešlé tx je vložen podpisový skript (scriptSig) aktuální tx. ScriptSig naší transakce je ale prázdný! No to nevadí, uzel začne s tím to co má. OP_0 vloží na vrch zásobníku nulu, OP_PUSHBYTES_20 vloží na vrch zásobníku následujících 32 bajtů skriptu. Uzel vykonal celý skript a na vrchu zásobníku má nenulovou hodnotu (hash veřejného klíče). Skript je pravdivý, transakce je platná. I bez podpisu je platná! Není to chyba, je to schválně – předsegwitové uzly neví, že podpisový skript je nyní v poli witness, proto je zajištěno, že se těmto uzlům bude kombinace segwitového uzamykacího skriptu a prázdného podpisového skriptu segwit transakce jevit jako platná. Taky se tomu říká zpětná kompatibilita.
Jak si s vyhodnocením skriptů poradí segwitový uzel? Začátek je stejný, sáhne si do předchozí transakce pro uzamykací skript. Rozpozná strukturu skriptu (p2wpkh) a uplatní speciální pravidlo, které skript modifikuje do podoby:
Konečně máme skript, který nebude platný s i prázdným podpisovým skriptem! Toto je již mimochodem struktura shodná s předsegwitovým p2pkh. Zkušebně si vyhodnotíme tento skript bez podpisu: OP_DUP duplikuje element na vrchu zásobníku. Zásobník je ale prázdný – skript selhal. Nyní už to bez podpisu nepůjde.
Segwitový uzel ale ví, že v této transakci musí podpisový skript hledat v poli witness. Finální odemykací skript naší transakce (resp. jejího prvního a jediného vstupu) je tedy:
Vykonáme skript – 71 následujících bajtů (podpis) se vloží na vršek zásob.; 33 následujících bajtů (veřejný klíč) se vloží na vršek zásob.; element na vršku zásob. se zduplikuje; element na vršku zásobníku se nahradí svým vlastním hashem (RIPEMD160(SHA256)). Na zásobníku je nyní pohledem z vrchu hash (adresa), veřejný klíč a podpis; dále se 20 následujících bajtů vloží na vršek zásob. (tj. adresa z uzamykacího skriptu); dva vrchní elementy se odeberou ze zásob. a porovnají se – jsou‐li shodné, skript pokračuje, v opačném případě končí chybou; konečně poslední operátor odebere z vrchu zásob. nejprve veřejný klíč a pak i podpis – zkontroluje podpis proti danému klíči a předobrazu podepisované transakce a vrátí na vrch zásobníku 1 pro platný, 0 pro neplatný podpis. Jsme na konci skriptu a je‐li na vrchu zásobníku 1, transakce je platná.
Bonus: Předobraz transakce pro podpis
V anatomii podpisu se dočtete více o ověřování podpisu. Zde na tomto místě však využijeme výše rozebranou transakci, resp. její předchůdkyni a podíváme se, jakým způsobem vznikl její podepisovaný předobraz.
Před segwitem vznikal předobraz tak, že v případě ověření nejprve došlo k vyprázdnění všech scriptSigů (v případě podpisu není co vyprazdňovat, scriptSig ještě nebyl naplněn). Následně jsou tato prázdná místa – vždy po jednom pro každý podpis – vyplněna skripty scriptPubKey utrácených výstupů. Pro ty je potřeba sáhnout do předchozí(ch) transakce/í. Nakonec jen přidán hashtyp – typicky SIGHASH_ALL (číslo 1 v čtyřbajtovém LE).
Tento přístup má však dvě hlavní nevýhody. Zaprvé – výpočetní náročnost hashování roste kvadraticky s počtem vstupů podepisované transakce. A zadruhé – předobraz v sobě neobsahuje informaci o částce utrácených vstupů, ze kterých by kupříkladu hardwarová peněženka dokázala bez dalších informací vypočíst a zobrazit částku poplatku k potvrzení. Proto byl u v rámci segwitu – konkrétně BIPem 143 – zaveden nový způsob výroby předobrazu pro segwitové transakce. Předobraz výše rozebrané tranasakce bude tedy vypadat následovně (užité hashe jsou vždy dvojité SHA256):
verze tx (4 bajty LE)
hash všech txid a indexů utracených výstupů; společný pro všechny podpisy (32 bajtů)
hash všech sequence útracených výstupů; společný pro všechny podpisy (32 bajtů)
txid a index utráceného (podepisovaného) výstupu (32 + 4 bajty LE)
výše uvedený modifikovaný uzamykací skript utráceného (podepisovaného) výstupu (script)