Kontynuujemy nasz cykl o architekturze blockchaina Monero, a dzisiejszy artykuł poświęcony będzie protokołowi RingCT (Ring Confidential Transactions), który wprowadza transakcje prywatne oraz nowe podpisy ringowe. Niestety, w internecie jest mało informacji na temat jego działania, a my postanowiliśmy zaspokoić tę lukę.

Omówimy, w jaki sposób ten protokół ukrywa kwoty transferów w sieci, dlaczego zrezygnowano z klasycznych podpisów ringowych typowych dla cryptonote i jak ta technologia będzie się rozwijać w przyszłości.
Ponieważ ten protokół jest jedną z najbardziej skomplikowanych technologii w Monero, czytelnik będzie potrzebował podstawowej wiedzy na temat działania tego blockchaina oraz podstawowej znajomości kryptografii na krzywych eliptycznych (żeby odświeżyć te informacje, można przeczytać pierwsze rozdziały naszego poprzedniego artykułu o ).
Protokół RingCT
Jednym z możliwych ataków na waluty cryptonote jest analiza blockchaina oparta na znajomości kwoty i czasu wysłania transakcji. Pozwala to znacznie zawęzić obszar poszukiwań interesujących hakerów wyjść. Aby chronić się przed taką analizą, w Monero wprowadzono protokół anonimowych transakcji, który całkowicie ukrywa kwoty transferów w sieci.
Warto zauważyć, że pomysł ukrywania kwot nie jest nowy. Jednym z pierwszych, który opisał to rozwiązanie, był programista Bitcoin Core Greg Maxwell w swoim . Obecna realizacja RingCT jest modyfikacją tej koncepcji, umożliwiającą stosowanie podpisów ringowych (jakżeby bez nich), i tak właśnie otrzymała swoją nazwę — Ring Confidential Transactions.
Protokół pomaga również wyeliminować problemy związane z mieszaniem niewielkich wyjść — wyjść o małej kwocie (zwykle powstających jako reszta z transakcji), które generowały więcej problemów, niż same kosztowały.
W styczniu 2017 roku miało miejsce hard fork sieci Monero, który umożliwił opcjonalne korzystanie z transakcji poufnych. A już we wrześniu tego samego roku z hard forka wersji 6 takie transakcje stały się jedynymi dozwolonymi w sieci.
RingCT wykorzystuje kilka mechanizmów: wielowarstwowe powiązane spontaniczne anonimowe podpisy grupowe (Multilayered Linkable Spontaneous Anonymous Group Signature, w skrócie MLSAG), schemat zobowiązań (Pedersen Commitments) oraz dowody zakresu (w polskim języku nie ma ustalonego tłumaczenia tego terminu).
Protokół RingCT wprowadza dwa typy anonimowych transakcji: simple i full. Pierwszy jest generowany przez portfel, gdy transakcja wykorzystuje więcej niż jeden wejście, a drugi – w odwrotnej sytuacji. Różnią się one walidacją sum transakcji oraz danymi podpisywanymi podpisem MLSAG (więcej na ten temat poniżej). Co więcej, transakcje typu full można generować z dowolną liczbą wejść, zasadniczo nie ma tutaj różnicy. W książce na ten temat mówi się, że decyzja o ograniczeniu transakcji full do jednego wejścia została podjęta w pośpiechu i w przyszłości może ulec zmianie.
Podpis MLSAG
Przypomnijmy, jakie są podpisywane wejścia transakcji. Każda transakcja wydaje jakieś środki i generuje. Generacja środków odbywa się poprzez tworzenie wyjść transakcji (bezpośredni odpowiednik – banknoty), a wyjście, które transakcja wydaje (bo w rzeczywistości wydajemy tylko banknoty), staje się wejściem (ostrożnie, tutaj łatwo się zgubić).
Wejście odnosi się do kilku wyjść, ale wydaje tylko jedno, tworząc w ten sposób „dymną zasłonę”, aby utrudnić analizę historii przelewów. Jeśli transakcja ma więcej niż jedno wejście, taką strukturę można przedstawić w postaci macierzy, gdzie wiersze to wejścia, a kolumny to mieszane wyjścia. Aby udowodnić sieci, że transakcja wydaje właśnie swoje wyjścia (zna ich tajne klucze), wejścia podpisują się podpisem pierścieniowym. Taki podpis daje gwarancję, że podpisujący znał tajne klucze wszystkich elementów w jakiejkolwiek kolumnie.
Transakcje prywatne nie używają już klasycznych podpisów pierścieniowych, ich miejsce zajęły MLSAG – wersja dostosowana do wielu wejść analogicznych do jednowarstwowych podpisów pierścieniowych, .
Nazywają się wielowarstwowymi, ponieważ podpisują jednocześnie kilka wejść, z których każde jest mieszane z kilkoma innymi, tzn. podpisywana jest macierz, a nie jeden wiersz. Jak zobaczymy później, pomaga to zaoszczędzić na rozmiarze podpisu.
Przyjrzyjmy się, jak formułowany jest podpis pierścieniowy, na przykładzie transakcji, która wydaje 2 rzeczywiste wyjścia i wykorzystuje do mieszania m – 1 losowe z blockchaina. Oznaczmy klucze publiczne wyjść, które wydajemy, jako
, a obrazy kluczy dla nich odpowiednio:
W ten sposób otrzymujemy macierz o wymiarach 2 x m. Najpierw musimy obliczyć tzw. wyzwania dla każdej pary wyjść:

Obliczenia zaczynamy od wyjść, które wydajemy, używając ich kluczy publicznych:
i liczb losowych
W rezultacie otrzymujemy wartości:
, które wykorzystujemy do obliczenia wyzwania
następnej pary wyjść (aby łatwiej zrozumieć, co i gdzie podstawiamy, wyróżniliśmy te wartości różnymi kolorami). Wszystkie następne wartości są obliczane cyklicznie według wzorów podanych na pierwszej ilustracji. Ostatnim obliczanym wyzwaniem jest to dla pary rzeczywistych wyjść.
Jak widać, we wszystkich kolumnach poza tą zawierającą rzeczywiste wyjścia używane są losowo wygenerowane liczby
. Dla π-tej kolumny również będą nam potrzebne. Przekształcamy
w s:
Podpis sam w sobie jest krotką wszystkich tych wartości:

Następnie te dane są zapisywane w transakcji.
Jak widać, MLSAG zawiera tylko jedno wyzwanie c0, co pozwala zaoszczędzić miejsce na podpisie (które i tak zajmuje dużo miejsca). Następnie każdy weryfikator, używając danych
, przywraca wartości c1,…, cm i sprawdza, że
. W ten sposób nasze koło się zamknęło, a podpis przeszedł weryfikację.
Dla transakcji RingCT typu full dodawany jest jeszcze jeden wiersz do macierzy z wymieszanymi wyjściami, ale o tym opowiemy później.
Pedersen Commitments
(częściej używa się anglojęzycznego terminu — commitments) są używane do tego, aby jedna strona mogła udowodnić, że zna pewien sekret (liczbę), faktycznie go nie ujawniając. Na przykład, rzucasz kostką pewną liczbę, obliczasz zobowiązanie i przekazujesz je stronie weryfikującej. W ten sposób, w momencie ujawnienia sekretniej liczby, weryfikator samodzielnie oblicza zobowiązanie, upewniając się, że go nie oszukiwałeś.
W Monero zobowiązania są używane do ukrywania kwot przelewów i stosują najpopularniejszy wariant — Pedersen commitments. A przy okazji ciekawostka — początkowo deweloperzy proponowali ukrywać kwoty zwykłym zamieszaniem, to znaczy dodawając wyjścia o losowych kwotach, aby wprowadzić niepewność, ale później przeszli na zobowiązania (przy czym nie jest pewne, że zaoszczędzili na wielkości transakcji, jak zobaczymy poniżej).
W ogólnym przypadku zobowiązanie wygląda następująco:
Gdzie C — wartość samego zobowiązania, a — ukrywana suma, H — stała punkt na eliptycznej krzywej (dodatkowy generator), a x — pewna losowa maska, ukrywający czynnik, generowany losowo. Maska jest potrzebna, aby strona trzecia nie mogła w prosty sposób dobrać wartości commitment.
Podczas generowania nowego wyjścia portfel oblicza dla niego commitment, a przy wydatkowaniu bierze albo wartość obliczoną podczas generacji, albo przelicza ją na nowo — w zależności od rodzaju transakcji.
RingCT simple
W przypadku transakcji simple RingCT, aby zagwarantować, że transakcja stworzyła wyjścia o równowartości sumy wejść (nie wytworzyła pieniędzy z powietrza), suma commitments pierwszych i drugich musi być taka sama, to znaczy:

Commitment opłat oblicza się trochę inaczej — bez maski:
, gdzie a — suma opłaty, jest publicznie dostępna.
Takie podejście pozwala udowodnić stronie kontrolującej, że używamy tych samych kwot, nie ujawniając ich.
Aby wszystko stało się jaśniejsze, rozważmy przykład. Załóżmy, że transakcja wydaje dwa wyjścia (czyli stają się one wejściami) na 10 i 5 XMR i generuje trzy wyjścia na sumę 12 XMR: 3, 4 i 5 XMR. Przy tym płaci opłatę 3 XMR. W ten sposób suma wydanych pieniędzy plus suma wygenerowanych i opłata wynosi 15 XMR. Spróbujmy obliczyć commitments i zobaczyć różnicę ich sum (przypomnijmy sobie matematykę):

Tutaj widzimy, że aby równanie się zgadzało — sumy masek wejść i wyjść muszą być takie same. W tym celu portfel generuje losowo x1, y1, y2 i y3, a pozostałe x2 oblicza tak:
![]()
Używając tych masek, możemy udowodnić każdemu kontrolującemu, że nie generujemy środków więcej, niż wydajemy, nie ujawniając sum. Oryginalnie, prawda?
RingCT full
W full RingCT transakcjach kontrola sum przelewów przebiega nieco bardziej skomplikowanie. W tych transakcjach portfel nie przelicza commitments dla wejść, ale używa tych obliczonych podczas ich generacji. Należy przyjąć, że różnica sum nie będzie już równa zeru, a zamiast tego:

Tutaj z — różnica masek wejść i wyjść. Jeśli rozważyć zG jako klucz publiczny (czym de facto jest), to z — to jest klucz prywatny. W ten sposób znamy klucz publiczny oraz odpowiadający mu klucz prywatny. Mając te dane, możemy je wykorzystać w podpisie ringowym MLSAG razem z publicznymi kluczami mieszanych wyjść:

W ten sposób ważny podpis ringowy gwarantuje, że znamy wszystkie klucze prywatne jednego z kolumn, a klucz prywatny w ostatnim wierszu możemy znać tylko wtedy, gdy transakcja nie generuje środków więcej, niż wydaje. Przy okazji, tutaj odpowiedź na pytanie „dlaczego różnica sum zobowiązań nie prowadzi do zera” — jeśli zG = 0, to ujawniamy kolumnę z rzeczywistymi wyjściami.
A jak odbiorca środków dowie się, ile pieniędzy mu przesłano? Tutaj wszystko jest proste — nadawca transakcji i odbiorca wymieniają się kluczami za pomocą protokołu Diffiego-Hellmana, używając klucza transakcji i klucza view odbiorcy oraz wyliczają wspólny sekret. Nadawca zapisuje w specjalnych polach transakcji dane o kwotach wyjść, zaszyfrowane tym wspólnym kluczem.
Dowody zakresu
A co się stanie, jeśli jako sumę w zobowiązaniach użyjemy liczby ujemnej? To może prowadzić do generacji dodatkowych monet! Taki wynik jest niedopuszczalny, dlatego potrzebna jest gwarancja, że używane przez nas kwoty są nieujemne (bez ujawnienia tych kwot, rzecz jasna, w przeciwnym razie tyle trudu i wszystko na nic). Innymi słowy, musimy udowodnić, że suma znajduje się w zakresie [0, 2n — 1].
W tym celu suma każdego wyjścia dzieli się na bity i liczy się zobowiązanie dla każdego bitu osobno. Jak to wygląda, najlepiej omówić na przykładzie.
Załóżmy, że kwoty są niewielkie i mieszczą się w 4 bitach (w praktyce to 64 bity), a my tworzymy wyjście na kwotę 5 XMR. Liczymy zobowiązania dla każdego bitu i ogólne zobowiązanie na całą kwotę:
Następnie każde zobowiązanie jest mieszane z surrogatem (Ci-2iH) i podpisywane parami podpisem ringowym Borromeo (jeszcze jednym podpisem ringowym), zaproponowanym przez Grega Maxwella w 2015 roku (więcej o nim można przeczytać ):
Wszystko razem nazywa się dowodem zakresu i pozwala zapewnić, że w zobowiązaniach używane są kwoty w ramach [0, 2n — 1].
Co dalej?
W bieżącej implementacji dowody zakresu zajmują bardzo dużo miejsca — 6176 bajtów na jedno wyjście. Prowadzi to do dużych transakcji i, w konsekwencji, wyższych opłat. Aby zmniejszyć rozmiar transakcji Monero, deweloperzy wprowadzają zamiast podpisów Borromeo bulletproofs — mechanizm dowodu zakresu bez commitmentów bitowych. , są w stanie zmniejszyć rozmiar dowodu zakresu o 94%. Przy okazji, w połowie lipca technologia przeszła przeprowadzony przez firmę Kudelski Security, która nie wykazała istotnych wad ani w samej technologii, ani w jej realizacji. Technologia jest już stosowana w sieci testowej, a po nowym hard forku może trafić także do sieci głównej.
Zadawaj pytania, proponuj tematy do nowych artykułów o technologiach w obszarze kryptowalut, a także subskrybuj naszą grupę w , aby być na bieżąco z naszymi wydarzeniami i publikacjami.
Źródło: habr.com
