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).
Diagram popisované transakce včetně předcházející
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)