Autor oryginalny: @Web3 Mario (https://x.com/web3_mario)

Po przeczytaniu poprzedniego artykułu na temat wprowadzenia technologii TON szczegółowo przestudiowałem oficjalne dokumenty rozwojowe TON z tego okresu. Uważam, że nadal istnieją pewne bariery w nauce. Obecna treść dokumentu bardziej przypomina wewnętrzny dokument programistyczny. który jest odpowiedni dla nowych programistów. Nie jest zbyt przyjazny, więc spróbowałem uporządkować serię artykułów na temat rozwoju projektów TON Chain w oparciu o moją własną ścieżkę uczenia się. Mam nadzieję, że będzie to pomocne dla każdego, aby szybko rozpocząć pracę nad TON DApp . Jeśli w tekście są jakieś błędy, możesz mnie poprawić i wspólnie się uczyć.

Jakie są różnice między rozwojem NFT w EVM a rozwojem NFT w łańcuchu TON?

Wystawienie FT lub NFT jest często podstawową potrzebą programistów DApp. Dlatego też traktuję to jako punkt wyjścia do nauki. Najpierw zrozummy następujące różnice pomiędzy opracowaniem NFT w stosie technologii EVM i łańcuchem TON. NFT oparte na EVM często decydują się na dziedziczenie standardu ERC-721. Tak zwany NFT odnosi się do niepodzielnego rodzaju zaszyfrowanego zasobu, a każdy zasób jest unikalny, to znaczy ma pewne wyjątkowe cechy. ERC-721 jest powszechnym paradygmatem rozwoju tego typu aktywów. Przyjrzyjmy się, jakie funkcje musi wdrożyć wspólna umowa ERC 721 i jakie informacje są rejestrowane. Poniższy rysunek przedstawia interfejs ERC 721. Można zauważyć, że w przeciwieństwie do FT w interfejsie przelewu należy wprowadzić tokenId, a nie kwotę. Ten tokenId jest również najbardziej podstawowym przejawem wyjątkowości zasobów NFT. Oczywiście, aby przenosić więcej atrybutów, dla każdego tokenId zwykle rejestrowane są metadane. Te metadane są zewnętrznym łączem, które zapisuje inne skalowalne dane NFT, np jako Linki do obrazów PFP, nazw niektórych właściwości itp.

Dla programistów, którzy znają Solidity lub są zaznajomieni z obiektowością, wdrożenie takiego inteligentnego kontraktu jest łatwe. Wystarczy zdefiniować typy danych wymagane w umowie, takie jak niektóre kluczowe relacje mapowania, i opracować odpowiednie zgodnie z wymaganiami Funkcje logiczne modyfikacji tych danych mogą realizować NFT.

Jednak w TON Chain nie jest tak samo. Istnieją dwie główne przyczyny tej różnicy:

  • Przechowywanie danych w TON jest realizowane w oparciu o komórki, a komórki tego samego konta są realizowane poprzez skierowane grafy acykliczne. Oznacza to, że dane, które należy przechowywać, nie mogą rosnąć bez granic, ponieważ w przypadku skierowanego grafu acyklicznego koszt zapytania zależy od głębokości danych. Gdy głębokość rozciąga się w nieskończoność, koszt zapytania może być zbyt wysoki, co prowadzi do Umowa utknęła w martwym punkcie.

  • Aby uzyskać wysoką wydajność współbieżności, firma TON porzuciła architekturę wykonywania szeregowego i zamiast tego przyjęła paradygmat programistyczny specjalnie zaprojektowany pod kątem równoległości, model aktora, w celu zrekonstruowania środowiska wykonawczego. Ma to wpływ. Inteligentne kontrakty można wywoływać tylko asynchronicznie poprzez wysyłanie tak zwanych komunikatów wewnętrznych. Należy pamiętać, że niezależnie od tego, czy jest to wywołanie typu modyfikacji stanu, czy typu tylko do odczytu, należy dodatkowo przestrzegać tej zasady należy dokładnie rozważyć, jak postępować z przywracaniem danych w przypadku niepowodzenia wywołania asynchronicznego.

Oczywiście inne różnice techniczne zostały szczegółowo omówione w poprzednim artykule. W tym artykule mamy nadzieję skupić się na rozwoju inteligentnych kontraktów, więc nie będą one omawiane. Powyższe dwie zasady projektowania stanowią dużą różnicę między rozwojem inteligentnych kontraktów w TON i EVM. Z początkowej dyskusji wiemy, że umowa NFT musi definiować pewne relacje mapowania, to znaczy mapowania w celu zapisywania danych związanych z NFT. Najważniejszym z nich są właściciele. To mapowanie przechowuje relację mapowania adresu właściciela NFT odpowiadającą określonemu tokenID, który określa własność NFT. Przeniesienie jest modyfikacją własności. Ponieważ jest to struktura danych, która teoretycznie może być nieograniczona, należy jej unikać w miarę możliwości. Dlatego oficjalnie zaleca się stosowanie istnienia nieograniczonych struktur danych jako standardu shardingu. Oznacza to, że gdy istnieją podobne wymagania dotyczące przechowywania danych, zamiast tego stosowany jest paradygmat umowy master-slave, a danymi odpowiadającymi każdemu kluczowi zarządza się poprzez tworzenie umów podwykonawstwa. Zarządzaj parametrami globalnymi w ramach umowy głównej lub pomagaj w obsłudze wewnętrznych interakcji pomiędzy umowami podwykonawczymi.

Oznacza to, że transakcje NFT w TON również muszą być zaprojektowane przy użyciu podobnej architektury. Każda NFT jest niezależną umową podwykonawstwa, która zapisuje wyłączne dane, takie jak adres właściciela, metadane itp., i zarządza ogólną sytuacją za pośrednictwem danych umowy głównej takie jak nazwa NFT, symbol, całkowita podaż itp.

Po wyjaśnieniu architektury następnym krokiem jest rozwiązanie podstawowych wymagań funkcjonalnych. Ponieważ przyjęto tę metodę umowy master-slave, konieczne jest wyjaśnienie, które funkcje są realizowane w ramach umowy głównej, a które w ramach umowy podwykonawstwa. i oba są przekazywane przez: Jakie informacje wewnętrzne są przekazywane i jak przywrócić poprzednie dane, gdy wystąpi błąd wykonania. Zwykle przed opracowaniem złożonego projektu na dużą skalę konieczne jest przekazanie diagramu klas i wyjaśnienie przepływu informacji między sobą oraz dokładne przemyślenie logiki wycofywania po niepowodzeniu wewnętrznego wywołania. Oczywiście powyższy rozwój NFT jest prosty , ale może również przeprowadzić podobną weryfikację.

Naucz się tworzyć inteligentne kontrakty TON na podstawie kodu źródłowego

TON zdecydował się zaprojektować język o typie statycznym, podobny do C, o nazwie Func, jako język tworzenia inteligentnych kontraktów. Następnie nauczmy się, jak tworzyć inteligentne kontrakty TON na podstawie kodu źródłowego. Wybrałem przykład NFT z oficjalnego dokumentu TON Zainteresowani znajomi mogą to sprawdzić sami. W tym przypadku zaimplementowano prosty przykład TON NFT. Przyjrzyjmy się strukturze kontraktu, która jest podzielona na dwa kontrakty funkcjonalne i trzy niezbędne biblioteki.

Te dwa główne kontrakty funkcjonalne zostały zaprojektowane zgodnie z powyższymi zasadami. Najpierw przyjrzyjmy się kodowi głównego kontraktu nft-collection:

To wprowadza pierwszy punkt wiedzy, jak trwale przechowywać dane w inteligentnych kontraktach TON. Wiemy, że trwałe przechowywanie danych w Solidity jest automatycznie obsługiwane przez EVM w oparciu o typ parametrów. Zwykle są to zmienne stanu inteligentnego kontraktu zostaną automatycznie utrwalone i zapisane w oparciu o najnowszą wartość po wykonaniu, a programiści nie muszą brać pod uwagę tego procesu. Ale w Func tak nie jest. Programiści muszą sami wdrożyć odpowiednią logikę przetwarzania. Ta sytuacja jest nieco podobna do tej, w której C i C++ muszą uwzględniać proces GC, ale inne nowe języki programistyczne zwykle automatyzują tę część logiki. . Przyjrzyjmy się kodowi. Najpierw wprowadzamy kilka wymaganych bibliotek, a następnie widzimy, że pierwsza funkcja loading_data służy do odczytywania trwale przechowywanych danych. Jej logika polega na zwróceniu najpierw komórki magazynu trwałej umowy za pomocą metody get_data odbywa się to poprzez standard Zaimplementowany przez bibliotekę stdlib.fc, niektóre z tych funkcji mogą być zwykle używane jako funkcje systemowe.

Typ wartości zwracanej przez tę funkcję to komórka, która jest typem komórki w TVM. Z poprzedniego wstępu wiemy już, że wszystkie trwałe dane w łańcuchu bloków TON przechowywane są w drzewie komórek. Każda komórka zawiera do 1023 bitów dowolnych danych i do czterech odniesień do innych komórek. Komórki są używane jako pamięć w TVM opartym na stosie. W komórce przechowywane są dane zakodowane w sposób kompaktowy. Aby uzyskać określone dane w postaci zwykłego tekstu, komórkę należy przekonwertować na typ zwany plasterkiem. Komórkę można przekonwertować na typ plasterka za pomocą funkcji Begin_parse, a następnie dane w komórce można uzyskać, ładując bity danych i odniesienia do innych komórek z plasterka. Należy zauważyć, że metodą wywołującą w linii 15 jest cukier składniowy w funkcji, która bezpośrednio wywołuje drugą funkcję po wartości zwracanej przez pierwszą funkcję. I na koniec załaduj odpowiednie dane w kolejności zgodnej z kolejnością trwałości danych. Zauważ, że ten proces różni się od solidności i nie jest wywoływany w oparciu o hashmap, więc nie można pomylić kolejności wywołań.

W funkcji save_data logika jest podobna, z tą różnicą, że jest to proces odwrotny, który wprowadza kolejny punkt wiedzy, nowy konstruktor typów, czyli typ konstruktora komórek. Bity danych i odniesienia do innych komórek można przechowywać w kreatorach, które następnie można sfinalizować w nowe komórki. Najpierw utwórz konstruktora za pomocą standardowej funkcji Begin_cell i po kolei powiązane z nim funkcje za pomocą funkcji związanych ze sklepem. Pamiętaj, że powyższa kolejność wywoływania musi być zgodna z kolejnością przechowywania. Na koniec, end_cell służy do zakończenia budowy nowej komórki. W tym momencie komórka jest zarządzana w pamięci. Wreszcie, poprzez najbardziej zewnętrzne set_data, można zakończyć trwałe przechowywanie komórki.

Następnie przyjrzyjmy się funkcjom związanym z biznesem. Najpierw musimy przedstawić kolejny punkt wiedzy, jak utworzyć nowy kontrakt za pomocą kontraktu, który będzie często używany w właśnie wprowadzonej architekturze master-slave. Wiemy, że w TON połączenia pomiędzy inteligentnymi kontraktami realizowane są poprzez wysyłanie komunikatów wewnętrznych. Osiąga się to poprzez komunikat o nazwie send_raw_message. Należy pamiętać, że pierwszy parametr to komórka zakodowana w komunikacie, a drugi parametr to bit identyfikacyjny, który służy do wskazania różnicy w sposobie wykonania transakcji. Ustawiono różne ustawienia wewnętrzne w TON Istnieją obecnie 3 tryby wiadomości i 3 flagi wiadomości dla trybu realizacji wysyłania wiadomości. Pojedynczy tryb można połączyć z wieloma (być może żadnymi) flagami, aby uzyskać pożądany tryb. Łączenie oznacza po prostu wypełnienie sumy ich wartości. Tabela opisów trybów i flag znajduje się poniżej:

Przyjrzyjmy się więc pierwszej głównej funkcji, Deploy_nft_item. Jak sama nazwa wskazuje, jest to funkcja używana do tworzenia lub rzutowania nowej instancji NFT. Po zakodowaniu wiadomości, kontrakt wewnętrzny jest wysyłany poprzez send_raw_message i wybierana jest flaga wysyłania Bit flagi o wartości 1 wykorzystuje tylko opłatę określoną w kodowaniu jako opłatę za gaz dla tego wykonania. Po powyższym wstępie łatwo zdajemy sobie sprawę, że ta zasada kodowania powinna odpowiadać sposobowi tworzenia nowego inteligentnego kontraktu. Przyjrzyjmy się zatem, jak jest to zaimplementowane.

Spójrzmy bezpośrednio na linię 51. Powyższe dwie funkcje są funkcjami pomocniczymi używanymi do generowania informacji wymaganych dla wiadomości, więc przyjrzymy się temu później. Jest to proces kodowania służący do tworzenia wewnętrznych wiadomości inteligentnych kontraktów. Niektóre liczby w środkowe są w rzeczywistości Niektóre bity identyfikacyjne są używane do opisu wymagań wiadomości wewnętrznej. Następny punkt wiedzy jest tutaj wprowadzony. TON wybrał język binarny zwany TL-B do opisu metody wykonania wiadomości i implementuje go poprzez ustawienie innej flagi. bits. W przypadku informacji wewnętrznych na temat określonych funkcji dwa najłatwiejsze scenariusze użycia to utworzenie nowego kontraktu i wywołania wdrożonych funkcji kontraktu. Metoda w linii 51 odpowiada pierwszej, tworząc nowy kontrakt na pozycje nft, który jest określony głównie w liniach 55, 56 i 57. Po pierwsze, duży ciąg liczb w linii 55 to ciąg bitów identyfikacyjnych. Należy zwrócić uwagę, że pierwszym parametrem wejściowym store_uint jest wartość, a drugim długość bitu, która określa, czy wiadomość wewnętrzna jest tworzona przez kontrakt. , trzy ostatnie bity zaznaczające i odpowiadająca im wartość binarna to 111 (dziesiętna to 4+2+1), z czego dwa pierwsze wskazują, że do wiadomości będą dołączone dane StateInit. Dane te stanowią nowy kod źródłowy umowy i danych wymaganych do inicjalizacji. Ten ostatni bit flagi wskazuje załącznik wiadomości wewnętrznej, to znaczy, że oczekuje się wykonania odpowiedniej logiki i wymaganych parametrów. Dlatego zobaczysz, że trzycyfrowe dane nie są ustawione w linii 66 kodu, co wskazuje na wywołanie funkcji wdrożonego kontraktu. Szczegółowe zasady kodowania znajdziesz tutaj.

Następnie reguły kodowania StateInit odpowiadają 49 linijkom kodu, obliczonym za pomocą obliczenia_nft_item_state_init. Należy zauważyć, że kodowanie danych stateinit również jest zgodne z ustaloną regułą kodowania TL-B. Oprócz niektórych bitów flag obejmuje to głównie dwie części nowego kontraktu kod i I zainicjuj dane. Kolejność kodowania danych musi być zgodna z kolejnością przechowywania komórek trwałości określoną w nowej umowie. Jak widać w linii 36, dane inicjujące zawierają item_index, który jest podobny do tokenId w ERC 721, oraz bieżący adres kontraktu zwracany przez standardową funkcję my_address, czyli adres_kolekcji. Kolejność tych danych jest zgodna z deklaracją w pozycji nft.

Następnym punktem wiedzy jest to, że w TON wszystkie niewygenerowane inteligentne kontrakty mogą wstępnie obliczyć wygenerowane adresy. Jest to podobne do funkcji tworzenia 2 w Solidity. Generowanie nowych adresów w TON składa się z dwóch części: workchain. Bit identyfikacyjny jest łączony ​​z wartością skrótu stateinit Jak widzieliśmy w poprzednim wprowadzeniu, należy określić tę pierwszą, aby odpowiadała architekturze nieskończonego fragmentowania TON. Jest to obecnie ujednolicona wartość. Uzyskane ze standardowego łańcucha roboczego funkcji. Tę ostatnią uzyskuje się za pomocą standardowej funkcji cell_hash. Wracając do tego przykładu, obliczenia_nft_item_address to funkcja, która wstępnie oblicza nowy adres kontraktu. I zakoduj wygenerowaną wartość w wiadomości w linii 53 jako adres odbiorczy wiadomości wewnętrznej. nft_content odpowiada wywołaniu inicjującemu utworzonego kontraktu. Konkretna implementacja zostanie przedstawiona w następnym artykule.

Jeśli chodzi o send_royalty_params, musi to być odpowiedź na wewnętrzny komunikat żądania tylko do odczytu. W poprzednim wstępie specjalnie podkreśliliśmy, że komunikat wewnętrzny w TON zawiera nie tylko operacje mogące modyfikować dane, ale także funkcję read-on. tylko operacja musi zostać wykonana w ten sposób. Implementacja, więc ten kontrakt jest taką operacją. Przede wszystkim warto zauważyć, że linia 67 reprezentuje znak funkcji wywołania zwrotnego requestera po udzieleniu odpowiedzi na żądanie. Zapisz to będą zwróconymi danymi, którymi są żądany indeks pozycji i odpowiadające mu dane licencyjne.

Następnie wprowadźmy następny punkt wiedzy. W TON istnieją tylko dwa ujednolicone wejścia do inteligentnych kontraktów, nazwane recv_internal i recv_external. Pierwsze to ujednolicone wejście wywołujące dla wszystkich wiadomości wewnętrznych, a drugie to ujednolicone wejście wywołujące dla wszystkich zewnętrznych komunikaty Rozwój Operator musi użyć metody podobnej do przełącznika, aby odpowiedzieć na różne żądania w oparciu o różne bity flagi określone przez komunikat w funkcji, zgodnie z wymaganiami. Bit flagi w tym przypadku jest flagą funkcji wywołania zwrotnego w linii 67 powyżej. Wracając do tego przykładu, najpierw sprawdź, czy wiadomość jest pusta, a następnie przeanalizuj odpowiednio informacje zawarte w wiadomości. Najpierw przeanalizuj linię 83, aby uzyskać adres nadawcy. Ten parametr będzie używany do kolejnych kontroli uprawnień. Należy pamiętać, że operator ~ tutaj należy do innej składni cukru. Nie będę tego tutaj rozwijać. Następnie analizowane są bity flag operacji op, a następnie odpowiednie żądania są przetwarzane zgodnie z różnymi bitami flag. Wśród nich powyższe funkcje wywoływane są odpowiednio według pewnej logiki. Na przykład odpowiedz na żądanie parametru royalty lub utwórz nowy nft i zwiększ indeks globalny.

Następny punkt wiedzy odpowiada wierszowi 108. Myślę, że możesz także poznać logikę przetwarzania tej funkcji, nadając jej nazwę. Podobnie jak w przypadku funkcji require w Solidity, wyjątki są zgłaszane w Func poprzez standardową funkcję Throw_unless. Pierwszym parametrem wejściowym jest kod błędu, drugi polega na sprawdzeniu wartości logicznej bitu, jeśli bit jest fałszywy, zostanie zgłoszony wyjątek z kodem błędu. W tym wierszu parametr valid_slices służy do określenia, czy adres_nadawcy przeanalizowany powyżej jest równy adresowi_właściciela trwałego magazynu umowy i dokonywana jest ocena uprawnień.

Na koniec, aby uczynić strukturę kodu bardziej przejrzystą, opracowano szereg funkcji pomocniczych pomagających uzyskać informacje o trwałości, które nie zostaną tutaj wprowadzone. Programiści mogą odwoływać się do tej struktury przy opracowywaniu własnych inteligentnych kontraktów.

 

Rozwój DApp w ekosystemie TON jest naprawdę interesujący i bardzo różni się od paradygmatu programowania EVM, dlatego w serii artykułów przedstawię, jak rozwijać DApp w TON Chain. Ucz się razem ze wszystkimi i wykorzystaj tę falę możliwości. Możesz także kontaktować się ze mną na Twitterze, aby spotkać się z nowymi i interesującymi pomysłami na dapp i wspólnie je rozwijać.