Przejdź do głównej treści

The Graph: Naprawa zapytań o dane w Web3

Solidity
inteligentne kontrakty
zapytania
the graph
React
Średniozaawansowany
Markus Waas
6 września 2020
7 minut czytania

Tym razem przyjrzymy się bliżej The Graph, który w zeszłym roku stał się w zasadzie częścią standardowego stosu technologicznego do tworzenia zdecentralizowanych aplikacji (dapp). Zobaczmy najpierw, jak zrobilibyśmy to w tradycyjny sposób...

Bez The Graph...

Posłużmy się więc prostym przykładem w celach ilustracyjnych. Wszyscy lubimy gry, więc wyobraźmy sobie prostą grę, w której użytkownicy obstawiają zakłady:

Załóżmy teraz, że w naszej zdecentralizowanej aplikacji (dapp) chcemy wyświetlać łączną liczbę zakładów, łączną liczbę przegranych/wygranych gier, a także aktualizować te dane za każdym razem, gdy ktoś ponownie zagra. Podejście wyglądałoby następująco:

  1. Pobranie totalGamesPlayerWon.
  2. Pobranie totalGamesPlayerLost.
  3. Subskrypcja zdarzeń BetPlaced.

Możemy nasłuchiwać zdarzenia w Web3 (opens in a new tab), jak pokazano po prawej stronie, ale wymaga to obsłużenia całkiem sporej liczby przypadków.

To wciąż jest w miarę w porządku dla naszego prostego przykładu. Ale powiedzmy, że chcemy teraz wyświetlać kwoty przegranych/wygranych zakładów tylko dla obecnego gracza. Cóż, mamy pecha, lepiej wdrożyć nowy kontrakt, który przechowuje te wartości i je pobiera. A teraz wyobraź sobie znacznie bardziej skomplikowany inteligentny kontrakt i dapp – sprawy mogą się szybko skomplikować.

One Does Not Simply Query

Widać, że nie jest to optymalne:

  • Nie działa dla już wdrożonych kontraktów.
  • Dodatkowe koszty gazu za przechowywanie tych wartości.
  • Wymaga kolejnego wywołania w celu pobrania danych z węzła Ethereum.

Thats not good enough

Spójrzmy teraz na lepsze rozwiązanie.

Pozwól, że przedstawię Ci GraphQL

Najpierw porozmawiajmy o GraphQL, pierwotnie zaprojektowanym i zaimplementowanym przez Facebook. Być może znasz tradycyjny model REST API. Wyobraź sobie teraz, że zamiast tego możesz napisać zapytanie o dokładnie te dane, których potrzebujesz:

GraphQL API vs. REST API

Animated demonstration of a GraphQL query in The Graph playground

Te dwa obrazy w zasadzie oddają istotę GraphQL. Za pomocą zapytania po prawej stronie możemy dokładnie zdefiniować, jakich danych chcemy, więc otrzymujemy wszystko w jednym żądaniu i nic więcej poza tym, czego dokładnie potrzebujemy. Serwer GraphQL zajmuje się pobieraniem wszystkich wymaganych danych, więc jest to niezwykle łatwe w użyciu dla strony konsumenckiej (frontendu). Oto fajne wyjaśnienie (opens in a new tab) tego, jak dokładnie serwer obsługuje zapytanie, jeśli jesteś zainteresowany.

Mając tę wiedzę, przejdźmy wreszcie do przestrzeni blockchain i The Graph.

Czym jest The Graph?

Blockchain to zdecentralizowana baza danych, ale w przeciwieństwie do tego, z czym zazwyczaj mamy do czynienia, nie mamy języka zapytań dla tej bazy danych. Rozwiązania do pobierania danych są uciążliwe lub całkowicie niemożliwe. The Graph to zdecentralizowany protokół do indeksowania i odpytywania danych z blockchaina. I jak można się domyślić, używa GraphQL jako języka zapytań.

The Graph

Przykłady są zawsze najlepsze do zrozumienia czegoś, więc użyjmy The Graph dla naszego przykładu GameContract.

Jak utworzyć podgraf

Definicja sposobu indeksowania danych nazywa się podgrafem. Wymaga on trzech komponentów:

  1. Manifest (subgraph.yaml)
  2. Schemat (schema.graphql)
  3. Mapowanie (mapping.ts)

Manifest (subgraph.yaml)

Manifest to nasz plik konfiguracyjny, który definiuje:

  • które inteligentne kontrakty indeksować (adres, sieć, ABI...)
  • jakich zdarzeń nasłuchiwać
  • inne rzeczy do nasłuchiwania, takie jak wywołania funkcji lub bloki
  • wywoływane funkcje mapujące (zobacz mapping.ts poniżej)

Możesz tu zdefiniować wiele kontraktów i handlerów. Typowa konfiguracja miałaby folder podgrafu wewnątrz projektu Hardhat z własnym repozytorium. Wtedy możesz łatwo odwołać się do ABI.

Dla wygody możesz również chcieć użyć narzędzia do szablonów, takiego jak mustache. Wtedy tworzysz subgraph.template.yaml i wstawiasz adresy na podstawie najnowszych wdrożeń. Bardziej zaawansowany przykład konfiguracji można znaleźć na przykład w repozytorium podgrafu Aave (opens in a new tab).

Pełną dokumentację można znaleźć tutaj (opens in a new tab).

Schemat (schema.graphql)

Schemat to definicja danych GraphQL. Pozwoli Ci zdefiniować, jakie encje istnieją i jakie są ich typy. Obsługiwane typy przez The Graph to:

  • Bytes
  • ID
  • String
  • Boolean
  • Int
  • BigInt
  • BigDecimal

Możesz również używać encji jako typu do definiowania relacji. W naszym przykładzie definiujemy relację jeden-do-wielu od gracza do zakładów. Znak ! oznacza, że wartość nie może być pusta. Pełną dokumentację można znaleźć tutaj (opens in a new tab).

Mapowanie (mapping.ts)

Plik mapowania w The Graph definiuje nasze funkcje, które przekształcają przychodzące zdarzenia w encje. Jest napisany w AssemblyScript, podzbiorze TypeScript. Oznacza to, że może zostać skompilowany do WASM (WebAssembly) w celu bardziej wydajnego i przenośnego wykonywania mapowania.

Będziesz musiał zdefiniować każdą funkcję nazwaną w pliku subgraph.yaml, więc w naszym przypadku potrzebujemy tylko jednej: handleNewBet. Najpierw próbujemy załadować encję Player z adresu nadawcy jako id. Jeśli nie istnieje, tworzymy nową encję i wypełniamy ją wartościami początkowymi.

Następnie tworzymy nową encję Bet. Identyfikatorem dla niej będzie event.transaction.hash.toHex() + "-" + event.logIndex.toString(), co zapewnia zawsze unikalną wartość. Użycie samego hasha nie wystarczy, ponieważ ktoś może wywołać funkcję placeBet kilka razy w jednej transakcji za pośrednictwem inteligentnego kontraktu.

Na koniec możemy zaktualizować encję Player wszystkimi danymi. Tablice nie mogą być bezpośrednio modyfikowane przez push, ale muszą być aktualizowane w sposób pokazany tutaj. Używamy id, aby odwołać się do zakładu. A .save() jest wymagane na końcu, aby zapisać encję.

Pełną dokumentację można znaleźć tutaj: https://thegraph.com/docs/en/developing/creating-a-subgraph/#writing-mappings (opens in a new tab). Możesz również dodać logowanie do pliku mapowania, zobacz tutaj (opens in a new tab).

Użycie we frontendzie

Używając czegoś takiego jak Apollo Boost, możesz łatwo zintegrować The Graph w swojej zdecentralizowanej aplikacji (dapp) w React (lub Apollo-Vue). Zwłaszcza przy użyciu hooków React i Apollo, pobieranie danych jest tak proste, jak napisanie pojedynczego zapytania GraphQL w komponencie. Typowa konfiguracja może wyglądać tak:

A teraz możemy napisać na przykład takie zapytanie. Pobierze ono dla nas:

  • ile razy obecny użytkownik wygrał
  • ile razy obecny użytkownik przegrał
  • listę znaczników czasu ze wszystkimi jego poprzednimi zakładami

Wszystko to w jednym żądaniu do serwera GraphQL.

Magic

Brakuje nam jednak ostatniego elementu układanki, czyli serwera. Możesz go uruchomić samodzielnie lub skorzystać z usługi hostowanej.

Serwer The Graph

Graph Explorer: Usługa hostowana

Najprostszym sposobem jest skorzystanie z usługi hostowanej. Postępuj zgodnie z instrukcjami tutaj (opens in a new tab), aby wdrożyć podgraf. Dla wielu projektów można znaleźć już istniejące podgrafy w eksploratorze (opens in a new tab).

The Graph-Explorer

Uruchomienie własnego węzła

Alternatywnie możesz uruchomić własny węzeł. Dokumentacja znajduje się tutaj (opens in a new tab). Jednym z powodów, dla których warto to zrobić, może być korzystanie z sieci, która nie jest obsługiwana przez usługę hostowaną. Obecnie obsługiwane sieci można znaleźć tutaj (opens in a new tab).

Zdecentralizowana przyszłość

GraphQL obsługuje również strumienie dla nowo przychodzących zdarzeń. Są one obsługiwane w The Graph poprzez Substreams (opens in a new tab), które są obecnie w fazie otwartej bety.

W 2021 roku (opens in a new tab) The Graph rozpoczął przejście na zdecentralizowaną sieć indeksującą. Więcej o architekturze tej zdecentralizowanej sieci indeksującej możesz przeczytać tutaj (opens in a new tab).

Dwa kluczowe aspekty to:

  1. Użytkownicy płacą indeksatorom za zapytania.
  2. Indeksatorzy stakują tokeny Graph (GRT).