Move narodził się na wczesnych etapach projektu Libra w 2018 roku – dwaj założyciele Mysten (Evan i ja) byli także zespołem założycielskim Libra. Zanim zdecydowaliśmy się na stworzenie nowego języka, wczesny zespół Libry intensywnie badał istniejące przypadki użycia inteligentnych kontraktów i języki, aby zrozumieć, co chcieli zrobić programiści, a gdzie istniejące języki nie dały rezultatu. Kluczowym problemem, który odkryliśmy, jest to, że inteligentne kontrakty dotyczą wyłącznie zasobów i kontroli dostępu, jednak wczesnym językom inteligentnych kontraktów brakowało reprezentacji typu/wartości w obu przypadkach. Nasza hipoteza jest taka, że ​​jeśli zapewnimy najwyższej klasy abstrakcje dla tych kluczowych pojęć, możemy znacznie poprawić bezpieczeństwo inteligentnych kontraktów i produktywność programistów inteligentnych kontraktów – posiadanie odpowiedniego słownictwa dla danego zadania może mieć ogromne znaczenie. Przez lata wiele osób brało udział w projektowaniu i wdrażaniu Move, podczas gdy język ten ewoluował od kluczowej idei do niezależnego od platformy języka inteligentnych kontraktów, którego śmiałym celem było stać się „JavaScriptem web3”.

Dzisiaj z radością ogłaszamy kamień milowy w integracji Move i Sui. Funkcjonalność Sui Move jest kompletna, wspierana przez zaawansowane narzędzia oraz obszerną dokumentację i przykłady, w tym następujące sekcje:

Seria tutoriali na temat programowania przy użyciu obiektów Sui Move Dokument programistyczny dotyczący podstawowej wiedzy, wzorców projektowych i przykładów Sui Move. Wtyczka ulepszająca VSCode opracowana przez zespół Mysten Move, która wspiera analizę kodu i diagnostykę błędów oraz integruje budowa, testowanie i zarządzanie pakietami Move, generowanie dokumentacji i walidator Move zintegrowany z sui CLI

 

Co sprawia, że ​​Move jest wyjątkowy

Move to wieloplatformowy język osadzony. Sama składnia rdzenia jest bardzo prosta: zawiera wspólne pojęcia, takie jak struktury, liczby całkowite i adresy, ale nie zawiera pojęć specyficznych dla blockchain, takich jak konta i transakcje), czasu, kryptografii itp. Funkcje te musi zapewniać platforma blockchain zintegrowana z Move. Co ważne, te łańcuchy bloków nie wymagają własnych forków Move — każda platforma korzysta z tej samej maszyny wirtualnej Move, walidatora kodu bajtowego, kompilatora, walidatora, menedżera pakietów i interfejsu CLI, ale opierając się na kodzie na tych podstawowych komponentach, aby dodać funkcjonalność specyficzną dla blockchain . Diem był pierwszym blockchainem, w którym osadzono Move, a kolejne blockchainy oparte na Move (w tym 0L, StarCoin i Aptos) w większości przyjęły podejście w stylu Diem. Chociaż ruch w stylu Diem ma kilka świetnych cech, zarówno dozwolony charakter Diem, jak i pewne szczegóły implementacji łańcucha bloków Diem (zwłaszcza model przechowywania) sprawiają, że niektóre podstawowe przypadki użycia inteligentnych kontraktów są trudne do wdrożenia. W szczególności oryginalne projekty Move i Diem poprzedzają eksplozję popularności NFT i mają kilka dziwactw, które sprawiają, że implementacja przypadków użycia związanych z NFT jest szczególnie trudna. W tym poście zademonstrujemy problemy z osadzeniem oryginalnego Move w stylu Diem na trzech takich przykładach i opiszemy, jak rozwiązaliśmy ten problem w Sui Move. Zakładamy podstawową wiedzę o Move, ale mamy nadzieję, że te kluczowe punkty będą zrozumiałe dla każdego, kto ma doświadczenie w programowaniu.

Jedwabiste doświadczenie podczas tworzenia zasobów na dużą skalę

Możliwość zbiorczego tworzenia i dystrybucji zasobów ma kluczowe znaczenie zarówno dla wdrażania, jak i angażowania użytkowników Web3. Być może streamer na Twitchu chce rozdać pamiątkowe NFT, twórca chce wysłać bilety na specjalne wydarzenie, a twórca gry chce rozdać wszystkim graczom nowe przedmioty. Oto (nieudana) próba napisania kodu masowego wydobywania aktywów w ruchu w stylu Diem. Ten kod przyjmuje jako dane wejściowe wektor adresów odbiorców, generuje zasób dla każdego odbiorcy i próbuje przenieść zasób.

W przypadku ruchu w stylu Diem pamięć globalna jest wpisana za pomocą par (adres, nazwa typu) - to znaczy, że każdy adres może przechowywać co najwyżej jeden zasób określonego typu. Dlatego move_to(receiverient, CoolAsset { ...} próbuje przenieść CoolAsset, przechowując go pod adresem odbiorcy. Jednak ten kod nie kompiluje się w linii move_to(receiverient, ...). Kluczowym pytaniem jest, w W stylu Diem W Move nie można wysłać wartości typu CoolAsset na adres A, chyba że transakcja zostanie wysłana z adresu innego niż A, a właściciel konta A utworzy transakcję, która jawnie wybierze otrzymanie obiektu typu CoolAsset, czyli dwie transakcje , tylko po to, aby otrzymać zasób! Decyzja o zrobieniu tego była dobra dla Diem W tym sensie, że jest to system z uprawnieniami, należy ostrożnie ograniczać tworzenie kont i zapobiegać przechowywaniu na kontach zbyt wielu aktywów ze względu na ograniczenia systemu przechowywania. Jednakże w przypadku systemu otwartego, który chce wykorzystywać alokację aktywów jako metodę mechanizmu onboardingu, czyli po prostu umożliwienie swobodnego przepływu zasobów między użytkownikami, tak jak ma to miejsce w przypadku Ethereum i podobnych łańcuchów bloków, jest bardzo ograniczone.

Kod implementujący tę samą funkcję w Sui Move jest następujący:

Globalna pamięć Sui Move jest kodowana według identyfikatora obiektu. Każda struktura zawierająca pary klucz-wartość jest „obiektem Sui”, który musi mieć globalnie unikalne pole identyfikatora. Sui Move wprowadza prymityw przenoszenia, którego można użyć na dowolnym obiekcie Sui, zamiast używać restrykcyjnej struktury move_to. W skrócie ten prymityw odwzorowuje identyfikator na CoolAsset w pamięci globalnej i dodaje metadane wskazujące, że wartość jest własnością odbiorcy. Interesującą właściwością wersji Mass_mint w Sui jest to, że jest ona zamieniana ze wszystkimi innymi transakcjami (w tym innymi transakcjami wywołującymi masę_mint!). Środowisko wykonawcze Sui zauważy to i wyśle ​​transakcje wywołujące tę funkcję za pośrednictwem bizantyjskiej transmisji konsensusu „szybką ścieżką”, która nie wymaga konsensusu. Takie transakcje mogą być zatwierdzane lub wykonywane równolegle. Nie wymaga to żadnego wysiłku ze strony programisty (po prostu pisze powyższy kod, a środowisko wykonawcze zajmuje się resztą). Być może subtelnie, nie jest tak w przypadku wariantu Diem tego kodu - mimo że powyższy kod działa, istnieje i guid::create wywołania utworzą punkty niezgody z innymi transakcjami, które generują identyfikatory GUID lub dotykają zasobów konta. W niektórych przypadkach możliwe jest przepisanie kodu Move w stylu Diem, aby uniknąć kłótni, ale wiele konwencjonalnych sposobów pisania Move w stylu Diem wprowadza małe przeszkody, które utrudniają wykonywanie równoległe.

Lokalna własność aktywów i transfery

Rozszerzmy kod Move w stylu Diem o obejście, które faktycznie się kompiluje i uruchamia. Idiomatycznym sposobem na to jest „wzorzec opakowania”: ponieważ Bob nie może przenieść CoolAsset bezpośrednio na adres Alicji, prosimy Alicję, aby „wyraziła zgodę” na otrzymanie CoolAsset, publikując najpierw opakowanie typu CoolAssetStore, które zawiera typ kolekcji (tabela). Alicja może to zrobić wywołując funkcję opt_in. Następnie dodajemy kod, który pozwala Bobowi przenieść CoolAsset z jego CoolAssetStore do CoolAssetStore Alicji. W tym kodzie dodajmy jeszcze jedno zdrobnienie: pozwolimy na transfery CoolAssets tylko jeśli zostały utworzone co najmniej 30 dni temu. Tego rodzaju zasady są ważne dla twórców, którzy (na przykład) chcą powstrzymać spekulantów od kupowania/promowania biletów na wydarzenia, aby prawdziwym fanom łatwiej było zdobyć te bilety w rozsądnej cenie.

Ten kod jest ważny. Jest to jednak dość skomplikowany sposób na wykonanie zadania polegającego na przeniesieniu majątku Alicji na Boba! Przyjrzyjmy się kolejnej realizacji Sui Move

Ten kod jest znacznie krótszy. Najważniejszą rzeczą, na którą należy zwrócić uwagę, jest to, że cool_transfer jest funkcją wejściową (co oznacza, że ​​może zostać wywołana bezpośrednio przez środowisko wykonawcze Sui poprzez transakcję), jednakże jako dane wejściowe ma parametr typu CoolAsset. To znowu magia Sui Runtime. Transakcja zawiera zestaw identyfikatorów obiektów, na których chce działać, a kiedy Sui Runtime:

Przeanalizuj identyfikator w wartość obiektu (nie ma potrzeby stosowania części pożycz_global_mut i table_remove w powyższym kodzie w stylu Diem). Sprawdza, czy obiekt jest własnością nadawcy transakcji (eliminuje potrzebę stosowania sekcjisigner::address_of i powiązanego kodu powyżej). Ta część jest szczególnie interesująca i wkrótce ją wyjaśnimy: W Sui sprawdzanie własności bezpiecznego obiektu jest częścią środowiska wykonawczego. Sprawdź typ wartości obiektu zgodnie z typem parametru wywoływanej funkcji cool_transfer. Powiąż wartość obiektu i inne parametry do parametrów cool_transfer i wywołaj funkcję

Umożliwiło to programistom Sui Move pominięcie szablonu części logiki „Wypłata” i przejście bezpośrednio do zabawnej części: sprawdzania 30-dniowej polityki wygaśnięcia. Podobnie część dotycząca „depozytu” jest również znacznie uproszczona dzięki wyjaśnionej powyżej strukturze transferu Sui Move. Wreszcie nie ma potrzeby wprowadzania typu wrappera z kolekcją wewnętrzną jak CoolAssetStore – globalny sklep Sui z indeksowanym identyfikatorem pozwala na przechowywanie pod jednym adresem dowolnej liczby wartości danego typu. Kolejną różnicą, na którą warto zwrócić uwagę, jest to, że cool_transfer w stylu Diem ma 5 sposobów przerwania (tj. kończy się niepowodzeniem i obciąża użytkownika opłatą za gaz bez ukończenia transferu), podczas gdy Sui Move cool_transfer ma tylko 1 sposób przerwania: w przypadku naruszenia 30-dniowej polityki. Przeniesienie kontroli własności obiektów do środowiska wykonawczego to duża korzyść, nie tylko pod względem ergonomii, ale także pod względem bezpieczeństwa. Implementacja bezpieczeństwa na poziomie wykonawczym zapobiega błędom we wdrażaniu tych kontroli w konstrukcji (lub całkowitym zapomnieniu o nich!). Na koniec zauważ, że sygnatura funkcji punktu wejścia Sui Move cool_transfer (zasoby: CoolAsset, ...) dostarcza nam wielu informacji o tym, co ta funkcja zamierza zrobić (jest bardziej nieprzejrzysta w porównaniu z sygnaturami funkcji w stylu Diem). Możemy pomyśleć, że ta funkcja prosi o pozwolenie na transfer CoolAsset, podczas gdy inna funkcja f(asset: &mut CoolAsset, ...) prosi o pozwolenie na zapis (ale nie przesyłanie) CoolAsset, a g(asset: &CoolAsset, ..) prosi tylko o pozwolenie na odczyt.

Ponieważ informacje te są dostępne bezpośrednio w sygnaturze funkcji (nie jest wymagane wykonanie ani analiza statyczna!), mogą być wykorzystywane bezpośrednio przez portfele i inne narzędzia klienckie. W Sui Wallet pracujemy nad żądaniami podpisów czytelnymi dla człowieka, wykorzystując te ustrukturyzowane podpisy funkcji, aby zapewnić użytkownikom monity o pozwolenie w stylu iOS/Android. Portfel może powiedzieć coś w stylu: „Ta transakcja wymaga pozwolenia na odczyt Twojego CoolAsset, zapis do Twojej AssetCollection i transfer Twojego ConcertTicket. Kontynuować?”.

Żądania podpisu czytelnego dla człowieka rozwiązują wektor ataków na dużą skalę występujący na wielu istniejących platformach, w tym na tych korzystających z ruchu w stylu Diem!, gdzie użytkownicy portfeli muszą na ślepo podpisywać transakcje, nie rozumiejąc, jaki mogą one mieć wpływ. Wierzymy, że zmniejszenie ryzyka korzystania z portfela jest kluczowym krokiem w promowaniu powszechnego przyjęcia portfeli kryptowalut i zaprojektowaliśmy Sui Move, aby wspierać ten cel, włączając funkcje takie jak prośby o podpis czytelny dla człowieka.

Połącz różne zasoby

Na koniec rozważmy przykład łączenia różnych typów aktywów. Jest to dość powszechny przypadek użycia: programiści mogą chcieć spakować różne typy NFT w kolekcję, połączyć elementy w celu sprzedaży na rynku lub dodać załączniki do istniejących elementów. Załóżmy, że mamy następującą sytuację:

Alicja definiuje obiekt postaci do wykorzystania w grze. Alicja chce wspierać dekorowanie swojej postaci różnymi rodzajami akcesoriów innych firm, które zostaną później utworzone. Każdy powinien mieć możliwość stworzenia akcesorium, ale właściciel postaci powinien zdecydować, czy dodać akcesorium. Transfer postaci powinien automatycznie przenieść wszystkie jej akcesoria.

Tym razem zacznijmy od kodu dla Sui Move. Wykorzystamy inny aspekt funkcjonalności własności obiektów wbudowanej w środowisko wykonawcze Sui: jeden obiekt może być własnością innego obiektu. Każdy obiekt ma unikalnego właściciela, ale obiekt nadrzędny może mieć dowolną liczbę obiektów podrzędnych. Relacja obiekt rodzic/dziecko tworzona jest za pomocą funkcji transfer_to_object, która jest obiektem skojarzonym z przedstawioną powyżej funkcją transferu.

W tym kodzie moduł roli zawiera funkcję accessorize, która umożliwia właścicielowi roli dodanie obiektu dodatkowego dowolnego typu jako obiektu podrzędnego. Dzięki temu Bob i Clarissa mogą tworzyć własne typy ozdób o różnych właściwościach i funkcjonalnościach, których Alice nie posiada, ale opierają się na tym, co Alice już zrobiła. Na przykład koszulę Boba można założyć tylko wtedy, gdy jest to ulubiony kolor postaci, a mieczem Clarissy można władać tylko wtedy, gdy postać jest wystarczająco silna. Poniżej znajduje się praktyczna demonstracja Ruchu w stylu Diem, ale żaden z nich nie zakończył się sukcesem, to znaczy Ruch w stylu Diem nie może osiągnąć powyższego scenariusza.

Można zauważyć, że problemy w ruchu w stylu Diem są następujące

Obsługiwane są tylko kolekcje tego samego typu (jak pokazał pierwszy test), ale akcesoria nie są zasadniczo typem obiektu. Relację między obiektami można osiągnąć jedynie poprzez „opakowanie” (tj. przechowywanie jednego obiektu w innym obiekcie). zbiór obiektów, które można zawijać, musi być predefiniowany (jak w drugiej próbie) lub dodany tymczasowo, a dodatkowa kompozycja obiektów nie jest obsługiwana (jak w trzecim teście).

 

Streszczać

Sui to pierwsza platforma, która znacznie odbiega od oryginalnego projektu Diem w sposobie wykorzystania Move. Zaprojektowanie kombinacji, która w pełni wykorzystuje Move i unikalne możliwości platformy, jest zarówno sztuką, jak i nauką, wymagającą głębokiego zrozumienia języka Move i możliwości leżącego u jego podstaw łańcucha bloków. Jesteśmy bardzo podekscytowani postępem, jaki robi Sui Move i nowymi zastosowaniami, które umożliwi! „. [1] Kolejnym argumentem przemawiającym za polityką Move w stylu Diem jest to, że „musisz wyrazić zgodę, zanim będziesz mógł otrzymać określony typ zasobu”. Jest to dobry mechanizm zapobiegania spamowi. Myślimy jednak o zapobieganiu spamowi jako należący do warstwy aplikacji, zamiast wymagać od użytkowników wysyłania kosztownych transakcji w celu wyrażenia zgody na otrzymywanie zasobów, spam można łatwo wyeliminować (na przykład) na poziomie portfela za pomocą rozbudowanych zasad definiowanych przez użytkownika i automatycznych filtrów spamu. .