
Subscribe to Katsu

Subscribe to Katsu
Share Dialog
Share Dialog


<100 subscribers
<100 subscribers
Bu makalede ele alınan önemli noktalar:
Sui Move, güvenli ve verimli akıllı sözleşmeler yazmanız için eksiksiz ve hazırdır
Sui, Move’u nasıl entegre ettiği konusunda orijinal Diem tasarımını geliştiren ilk blok zinciridir ve bu iyileştirmelerin somut örneklerini paylaşıyoruz.
Giriş
Move, 2018 yılında Libra projesinin ilk günlerinde doğdu — iki Mysten kurucusu (Evan ve ben) aynı zamanda Libra’nın kurucu ekibindeydik. Yeni bir dil yaratmaya karar vermeden önce, Libra’nın ilk ekibi, geliştiricilerin ne yapmak istediğini ve mevcut dillerin nerede yetersiz kaldığını anlamak için mevcut akıllı sözleşme kullanım durumlarını ve dillerini yoğun bir şekilde inceledi. Tespit ettiğimiz temel sorun, akıllı sözleşmelerin tamamen varlıklar ve erişim kontrolü ile ilgili olması, ancak ilk akıllı sözleşme dillerinin her ikisinin de tür/değer temsilinden yoksun olmasıdır. Move hipotezine göre, bu temel kavramlar için birinci sınıf soyutlamalar sağlarsak, hem akıllı sözleşmelerin güvenliğini hem de akıllı sözleşme programcılarının üretkenliğini önemli ölçüde artırabiliriz — eldeki görev için doğru kelime dağarcığına sahip olmak her şeyi değiştirir. Yıllar boyunca Move’un tasarımına ve uygulanmasına çok sayıda kişi katkıda bulundu ve dil, “web3'ün JavaScript’i” olma gibi cesur bir fikri temel alma hedefiyle platformdan bağımsız bir akıllı sözleşme diline dönüştü.
Bugün, Move’un Sui’ye entegrasyonundaki kilometre taşını duyurmaktan heyecan duyuyoruz: Sui Move, gelişmiş araçlarla desteklenen eksiksiz bir özelliktir ve aşağıdakiler de dahil olmak üzere kapsamlı dokümanlara ve örneklere sahiptir:
Sui Move objeleri ile programlama üzerine bir eğitim serisi
Sui Move temelleri, tasarım modelleri ve örneklerinden oluşan bir tarif kitabı
Mysten Move ekibi tarafından oluşturulan kod anlama ve hata tanılama desteği ile geliştirilmiş VSCode eklentisi!
Move derlemeleri, testleri, paket yönetimi, dokümantasyon oluşturma ve Move Prover’ın sui* *CLI ile entegrasyonu
Fungible** **tokenlar, NFT’ler, DeFi ve oyunları içeren bir dizi örnek.
2021'in sonlarına doğru Sui üzerinde çalışmaya başladığımızda, Move’a yeni bir bakış attık ve hangi erken tasarım kararlarının yeterince iyi oturmadığını hem de Move’un Sui’nin benzersiz özelliklerinden yararlanmak için nasıl geliştirilebileceğini düşündük. Daha önce Sui Move’da dil seviyesinde nelerin yeni olduğu hakkında bir yazı yazmıştık, ancak bu farklılıkları ortaya koyma motivasyonunu derinlemesine incelememiştik. Bu yazının geri kalanında, bu sorgulama örnek odaklı bir şekilde ayrıntılı olarak ele alınmaktadır.
Bir Dakika, farklı Move’lar da mı var?
Move platformlar arası, gömülü bir dildir. Çekirdek dilin kendisi çok basittir: yapılar, tamsayılar ve adresler gibi genel kavramlara sahiptir, ancak hesaplar, işlemler, zaman, kriptografi vb. gibi blok zincirine özgü kavramlara sahip değildir. Bu özellikler Move’a entegre blok zinciri platformu tarafından sağlanmalıdır. Daha da önemlisi, bu blok zincirlerinin kendi Move çatallarına ihtiyacı yoktur — her platform aynı Move VM, bytecode doğrulayıcı, derleyici, prover, paket yöneticisi ve CLI’yi kullanır, ancak bu temel bileşenlerin üzerine inşa edilen kod aracılığıyla blok zincirine özgü özellikler ekler.
Diem, Move’a gömülen ilk blok zinciriydi ve sonraki Move tabanlı zincirler (0L, StarCoin ve Aptos dahil) büyük ölçüde Diem tarzı yaklaşımı kullandı. Diem tarzı Move bazı başarılı niteliklere sahip olsa da, hem Diem’in izinli yapısı hem de Diem blok zincirinin uygulama ayrıntıları (özellikle depolama modeli) bazı temel akıllı sözleşme kullanım durumlarının uygulanmasını zorlaştırmaktadır. Özellikle Move ve Diem’in orijinal tasarımları, NFT’lerin popülerlik patlamasından önce ortaya çıkmıştır ve NFT ile ilgili kullanım durumlarının uygulanmasını özellikle zorlaştıran bazı uygunsuzluklara sahiptir.
Bu yazıda, orijinal Diem tarzı Move gömme ile ilgili bir sorunu sergileyen ve Sui Move’da bu sorunu nasıl ele aldığımızı gösteren üç tür örnek üzerinden yürüyeceğiz. Move hakkında temel bir anlayışa sahip olduğunuzu varsayıyoruz, ancak asıl kilit noktaların programlama geçmişi olan herkes tarafından anlaşılabilir olacağını umuyoruz.
Kolay toplu varlık oluşturma
Varlıkları toplu olarak oluşturma ve dağıtma becerisi, hem web3 kullanıcılarını takıma katmak hem de kullanıcıların ilgisini çekmek için son derece önemlidir. Belki de bir Twitch yayıncısı hatıra NFT’leri dağıtmak, bir içerik üreticisi özel bir etkinlik için bilet göndermek veya bir oyun geliştiricisi tüm oyuncularına yeni öğeler dağıtmak istiyordur.
İşte Diem tarzı Move’da bir varlığın toplu basımı için kod yazmaya yönelik (başarısız) bir girişim. Bu kod girdi olarak alıcı adreslerinin bir vektörünü alır, her biri için bir varlık oluşturur ve varlığı aktarmaya çalışır.
struct CoolAsset { id: GUID, creation_date: u64 } has key, storepublic entry fun mass_mint(creator: &signer, recipients: vector<address>) {
assert!(signer::address_of(creator) == CREATOR, EAuthFail);
let i = 0;
while (!vector::is_empty(recipients)) {
let recipient = vector::pop_back(&mut recipients);
assert!(exists<Account>(recipient), ENoAccountAtAddress);
let id = guid::create(creator);
let creation_date = timestamp::today();
// error! recipient must be `&signer`, not `address`
move_to(recipient, CoolAsset { id, creation_date })
}
Diem tarzı Move’da, global depolama (adres, tür adı) çiftleri ile şifrelenir — yani, her adres belirli bir türden en fazla bir varlık depolayabilir. Böylece, move_to(recipient, CoolAsset { ... }* *satırı CoolAsset’i recipientadresi altında depolayarak aktarmaya çalışır.
Ancak, bu kod move_to(recipient, …) satırında derlenemeyecektir. Temel sorun, Diem tarzı Move’da CoolAssettüründe bir değeri A adresine gönderemezsiniz:
1- A olmayan bir adres A’da bir hesap oluşturmak için bir işlem gönderir
2- A’nın sahibi, CoolAsset türündeki nesneleri almayı açıkça tercih etmek için bir işlem gönderir
Bu sadece bir varlık almak için iki işlem demek! İşleri bu şekilde yapma kararı, hesap oluşturmayı dikkatlice kısıtlaması ve depolama sistemindeki sınırlamalar nedeniyle hesapların yüksek miktarda varlık tutmasını önlemesi gereken izinli bir sistem olan Diem için mantıklıydı. Ancak bu, varlık dağıtımını ilk katılım mekanizması olarak kullanmak isteyen ya da genel olarak varlıkların Ethereum ve benzeri blok zincirlerinde olduğu gibi kullanıcılar arasında serbestçe gönderilmesine izin veren açık bir sistem için son derece kısıtlayıcıdır [1].
Şimdi aynı kodu Sui Move’da inceleyelim:
struct CoolAsset { id: VersionedID, creation_date: u64 } has keypublic entry fun mass_mint(recipients: vector<address>, ctx: &mut TxContext) {
assert!(tx_context::sender(ctx) == CREATOR, EAuthFail);
let i = 0;
while (!vector::is_empty(recipients)) {
let recipient = vector::pop_back(&mut recipients);
let id = tx_context::new_id(ctx);
let creation_date = tx_context::epoch(); // Sui epochs are 24 hours
transfer(CoolAsset { id, creation_date }, recipient)
}
}
Sui Move’un küresel depolaması nesne kimlikleri tarafından şifrelenir. keyyeteneğine sahip her yapı, küresel olarak benzersiz bir idalanına sahip olması gereken bir “Sui nesnesidir”. Sui Move, kısıtlı move_to yapısını kullanmak yerine, herhangi bir Sui nesnesi üzerinde kullanılabilecek bir transfer ilkeli sunar. Bu ilkel, id’yi global depodaki CoolAsset ile eşleştirir ve değerin recipient’a ait olduğunu belirtmek için meta veri ekler.
mass_mint’in Sui versiyonunun ilginç bir özelliği, diğer tüm işlemlerle (mass_mint’i çağıran diğerleri dahil!) döngü oluşturmasıdır. Sui çalışma zamanı bunu fark edecek ve bu işlevi çağıran işlemleri, konsensusa ihtiyaç duymayan Byzantine “hızlı yolu” üzerinden gönderecektir. Bu tür işlemler hem işlenebilir hem de paralel olarak yürütülebilir! Bu, programcıya ekstra bir is çıkarmaz — sadece yukarıdaki kodu yazar ve çalışma zamanı gerisini halleder.
Bu kodun Diem varyantı için bu durum geçerli değildir — yukarıdaki kod çalışsa bile, hem exists<Account> hem de guid::create çağrıları GUID üreten veya Accountkaynağına dokunan diğer işlemlerle uyuşmazlık yaratacaktır. Bazı durumlarda, uyumsuzluklardan kaçınmak için Diem tarzı Move kodunu yeniden yazmak mümkündür, ancak Diem tarzı Move yazmanın uyumlu yollarının çoğu, paralel yürütmeye ket vuran darboğazlar sorunlarını ortaya çıkarır.
Yerel varlık sahipliği ve transferleri
Diem tarzı Move kodunu gerçekten derlenecek ve çalışacak bir geçici çözümle genişletelim. Bunu yapmanın uyumlu yolu “wraper pattern”dir: Bob, Alice’in adresine bir CoolAsset’i doğrudan move_to yapamayacağı için, Alice’ten önce içinde bir koleksiyon türü (Table) olan bir wrapper türü CoolAssetStoreyayınlayarak CoolAsset’leri almayı “tercih etmesini” isteriz. Alice bunu opt_in fonksiyonunu çağırarak yapabilir. Daha sonra Bob’un CoolAssetStore’undaki bir CoolAsset’i Alice’in CoolAssetStore’una taşımasını sağlayan kodu ekliyoruz.
Bu koda bir başka ayrıntı daha ekleyelim: CoolAsset’lerin aktarılmasına, oluşturulmasının üzerinden en az 30 gün geçmişse izin vereceğiz. Bu tür bir politika, (örneğin) fırsatçıların etkinlik biletlerini satın almasını / karaborsaya düşürmesini engellemek isteyen içerik oluşturucular için önemlidir, böylece gerçek hayranların bunları makul bir fiyata almaları daha kolay olur.
struct CoolAssetStore has key {
assets: Table<TokenId, CoolAsset>
}public fun opt_in(addr: &signer) {
move_to(addr, CoolAssetHolder { assets: table::new() }
}public entry fun cool_transfer(
addr: &signer, recipient: address, id: TokenId
) acquires CoolAssetStore {
// withdraw
let sender = signer::address_of(addr);
assert!(exists<CoolAssetStore>(sender), ETokenStoreNotPublished);
let sender_assets = &mut borrow_global_mut<CoolAssetStore>(sender).assets;
assert!(table::contains(sender_assets, id), ETokenNotFound);
let asset = table::remove(&sender_assets, id);
// check that 30 days have elapsed
assert!(time::today() > asset.creation_date + 30, ECantTransferYet)
// deposit
assert!(exists<CoolAssetStore>(recipient), ETokenStoreNotPublished);
let recipient_assets = &mut borrow_global_mut<CoolAssetStore>(recipient).assets;
assert!(table::contains(recipient_assets, id), ETokenIdAlreadyUsed);
table::add(recipient_assets, asset)
}
Bu kod çalışıyor. Ancak bir varlığı Alice’ten Bob’a aktarmak gibi basit bir amacı gerçekleştirmek için oldukça karmaşık bir yol! Tekrar Sui Move varyantına bakalım:
public entry fun cool_transfer(
asset: CoolAsset, recipient: address, ctx: &mut TxContext
) {
assert!(tx_context::epoch(ctx) > asset.creation_date + 30, ECantTransferYet);
transfer(asset, recipient)
}
Bu kod çok daha kısadır. Burada dikkat edilmesi gereken en önemli şey cool_transfer’in bir entryfonksiyonu olması (yani bir işlem aracılığıyla doğrudan Sui çalışma zamanı tarafından çağrılabilir), ancak girdi olarak CoolAssettüründe bir parametreye sahip olmasıdır. Sui çalışma zamanı yine iş başında! Bir işlem, üzerinde çalışmak istediği nesne kimlikleri kümesini içerir ve Sui çalışma zamanı:
Kimliklerin nesne değerlerine dönüştürür (yukarıdaki Diem tarzı kodda borrow_global_mut ve table_remove kısımlarına olan ihtiyacı ortadan kaldırır)
Nesnenin işlemin göndericisine ait olup olmadığını kontrol eder (signer::address_of kısmına + yukarıdaki ilgili koda olan ihtiyacı ortadan kaldırır). Birazdan açıklayacağımız gibi bu kısım özellikle ilginçtir: Sui’de, güvenli nesne sahipliği sahiplik kontrolleri çalışma zamanının bir parçasıdır!
Nesne değerlerinin türlerini, çağrılan cool_transfer işlevinin parametre türlerine göre kontrol eder
Nesne değerlerini ve diğer bağımsız değişkenleri cool_transfer parametrelerine bağlar ve işlevi çağırır
Bu, Sui Move programcısının mantığın “para çekme” kısmının zahmetli kısmını atlamasına ve doğrudan ilginç kısma geçmesine olanak tanır: 30 günlük sona erme politikasını kontrol etmek. Benzer şekilde, “para yatırma” kısmı da yukarıda açıklanan Sui Move transferyapısı ile büyük ölçüde basitleştirilmiştir. Ve son olarak, CoolAssetStoregibi dahili bir koleksiyona sahip bir wrapper türü tanıtmaya gerek yoktur — kimlik endeksli Sui global depolama, bir adresin belirli bir türde rastgele sayıda değer depolamasına olanak tanır.
Belirtilmesi gereken bir diğer fark ise Diem tarzı cool_transfer’in 5 farklı şekilde iptal edilebilmesine (yani başarısız olup transferi tamamlamadan kullanıcıdan gaz ücreti almasına) karşın Sui Move cool_transfer’in sadece 1 şekilde iptal edilebilmesidir: 30 günlük politika ihlal edildiğinde.
Nesne sahipliği kontrollerinin çalışma zamanına yüklenmesi sadece kolaylık açısından değil, güvenlik açısından da büyük bir kazançtır. Bunun çalışma zamanı seviyesinde güvenli bir şekilde uygulanması, bu kontrollerin yapım aşamasında uygulanması (ya da tamamen unutulması!) hatalarını önler.
Son olarak, Sui Move giriş noktası işlev imzasının cool_transfer( asset: CoolAsset, ...) bize işlevin ne yapacağı hakkında ne kadar fazla bilgi verdiğine dikkat edin (daha opak olan Diem tarzı işlev imzasının aksine). Bu fonksiyonun CoolAsset’i transfer etme izni istediğini düşünebiliriz, oysa farklı bir fonksiyon olan f(asset: &mut CoolAsset, ...)* CoolAsset’i yazma (ancak transfer etmeme) izni ister ve g(asset: &CoolAsset, ...) *sadece okuma izni ister.
Bu bilgi doğrudan fonksiyon imzasında mevcut olduğundan (yürütme veya statik analiz gerekmez!), doğrudan cüzdan ve diğer istemci araçları tarafından kullanılabilir. Sui cüzdanında, kullanıcıya iOS/Android tarzı bir izin istemi sağlamak için bu yapılandırılmış işlev imzalarından yararlanan ve aynı zamanda** insan tarafından okunabilir imzalama istekleri **üzerinde çalışıyoruz. Cüzdan şöyle bir şey söyleyebilir: “Bu işlem CoolAsset’inizi okuma, AssetCollection’ınızı yazma ve ConcertTicket’ınızı aktarma izni istiyor. Devam edelim mi?”.
İnsan tarafından okunabilir imzalama talepleri, cüzdan kullanıcılarının etkilerinin ne olabileceğini anlamadan işlemleri körü körüne imzalamak zorunda kaldığı mevcut birçok platformda (Diem tarzı Move! kullananlar da dahil) bulunan büyük bir saldırı vektörünü ele almaktadır. Cüzdan deneyimini daha az tehlikeli hale getirmenin, kripto cüzdanlarının yaygın olarak benimsenmesini teşvik etmek için önemli bir adım olduğunu düşünüyoruz ve Sui Move’u, insan tarafından okunabilir imzalama istekleri gibi özellikleri etkinleştirerek bu hedefi destekleyecek şekilde tasarladık.
Heterojen varlıkların gruplandırılması
Son olarak, farklı türlerdeki varlıkların gruplandırılmasına ilişkin bir örnek ele alalım. Bu oldukça yaygın bir kullanım durumudur: bir programcı farklı türlerdeki NFT’leri bir koleksiyonda gruplamak, bir markette birlikte satılacak öğeleri bir araya getirmek veya mevcut bir öğeye aksesuar eklemek isteyebilir. Somut olarak, aşağıdaki senaryoya sahip olduğumuzu varsayalım:
Alice bir oyunda kullanılmak üzere bir Characternesnesi tanımladı
Alice, karakterini daha sonra oluşturulan farklı türlerdeki üçüncü taraf aksesuarlarla süslemek istiyor
Herkes aksesuar yaratabilmelidir, ancak Characterin sahibi aksesuar ekleyip eklemeyeceğine karar vermelidir.
Bir Characteraktarıldığında, tüm aksesuarları otomatik olarak aktarılmalıdır.
Bu sefer, Sui Hareket kodu ile başlayalım. Sui çalışma zamanı yerleşik nesne sahipliği özelliğinin başka bir yönünden yararlanacağız: bir nesne başka bir nesne tarafından sahiplenilebilir. Her nesnenin benzersiz bir sahibi vardır, ancak bir ebeveyn nesnenin isteğe bağlı sayıda çocuk nesnesi olabilir. Ebeveyn/çocuk nesne ilişkileri, yukarıda tanıtılan transfer fonksiyonunun bir kardeşi olan transfer_to_object fonksiyonu kullanılarak oluşturulur.
// in the Character module, created by Alice
struct Character has key {
id: VersionedID,
favorite_color: u8,
strength: u64,
...
}/// The owner of `c` can choose to add `accessory`
public entry fun accessorize<T: key>(c: &mut Character, accessory: T) {
transfer_to_object(c, accessory)
}// ... in a module added later by Bob
struct SpecialShirt has key {
id: VersionedID,
color: u8
}public entry fun dress(c: &mut Character, s: Shirt) {
// a special shirt has to be the character's favorite color
assert!(character::favorite_color(c) == s.color, EBadColor);
character::accessorize(c, shirt)
}// ... in a module added later by Clarissa
struct Sword has key {
id: VersionedID,
power: u64
}public entry fun equip(c: &mut Character, s: Sword) {
// a character must be very strong to use a powerful sword
assert!(character::strength(c) > sword.power * 2, ENotStrongEnough);
character::accessorize(c, s)
}
Bu kodda, Karakter modülü, bir karakterin sahibinin alt nesne olarak rastgele bir türe sahip bir accessorizenesnesi eklemesine olanak tanıyan bir aksesuar işlevi içerir. Bu, Bob ve Clarissa’nın Alice tarafından öngörülmeyen farklı niteliklere ve işlevselliğe sahip kendi aksesuar türlerini oluşturmalarına, ancak Alice’in hâlihazırda yaptıklarının üzerine ekleme yapmalarına olanak tanır. Örneğin, Bob’un gömleği yalnızca karakterin en sevdiği renkse giyilebilir ve Clarissa’nın kılıcı yalnızca karakter onu kullanabilecek kadar güçlüyse kullanılabilir.
Diem tarzı Move’da bu tür bir senaryoyu uygulamak mümkün değildir. İşte yetersiz kalan birkaç uygulama stratejisi denemesi:
// attempt 1
struct Character {
// won't work because every Accessory would need to be the same type + have
// the same fields. There is no subtyping in Move.
// Bob's shirt needs a color, and Clarissa's sword needs power--no standard
// representation of Accessory can anticipate everything devs will want to
// create
accessories: vector<Accessory>
}// attempt 2
struct Character {
// perhaps Alice anticipates the need for a Sword and a Shirt up front...
sword: Option<Sword>,
shirt: Option<Shirt>
// ...but what happens when Daniel comes along later and wants to add Pants?
}// attempt 3
// Does not support accessory compositions. For example: how do we represent a
// Character with Pants and a Shirt, but no Sword?
struct Shirt { c: Character }
struct Sword { s: Shirt }
struct Pants { s: Sword }
Diem tarzı Move’da temel sorunlar vardır:
Sadece homojen koleksiyonlar desteklenir (ilk denemenin gösterdiği gibi), ancak aksesuarlar temelde heterojendir
Nesneler arasındaki ilişkiler yalnızca “wrapping” (yani bir nesneyi başka bir nesnenin içinde saklama) yoluyla oluşturulabilir; ancak wraplanacak nesneler kümesi önceden tanımlanmalı (ikinci girişimde olduğu gibi) veya aksesuar bileşimini desteklemeyen geçici bir şekilde eklenmelidir (üçüncü girişimde olduğu gibi)
Sonuç
Sui, Move’u kullanma biçimiyle orijinal Diem tasarımından önemli ölçüde farklılaşan ilk platformdur. Move’dan ve platformun benzersiz özelliklerinden tam olarak yararlanan yerleştirmeler tasarlamak, hem Move dilini hem de altta yatan blok zincirinin yeteneklerini derinlemesine anlamayı gerektiren hem bir sanat hem de bir bilimdir. Sui Move’un kaydettiği ilerlemeler ve sağlayacağı yeni kullanım alanları konusunda gerçekten heyecanlıyız!
[1] Diem tarzı Move’un “belirli bir türden bir varlık almak için kabul edip onaylamanız gerekir” politikası lehine yapılan bir diğer argüman da bunun spam önleme için iyi bir mekanizma olduğudur. Ancak biz spam önlemenin uygulama katmanına ait olduğunu düşünüyoruz. Kullanıcılardan bir varlığı almayı kabul etmeleri için gerçek paraya mal olan işlemler göndermelerini istemek yerine, spam, zengin kullanıcı tanımlı politikalar ve otomatik spam filtrelerinin yardımıyla (örneğin) cüzdan düzeyinde kolayca ele alınabilir.
Sui Website — https://sui.io/
Sui Twitter — https://twitter.com/SuiNetwork
Sui Discord — https://discord.gg/sui
Bu makalede ele alınan önemli noktalar:
Sui Move, güvenli ve verimli akıllı sözleşmeler yazmanız için eksiksiz ve hazırdır
Sui, Move’u nasıl entegre ettiği konusunda orijinal Diem tasarımını geliştiren ilk blok zinciridir ve bu iyileştirmelerin somut örneklerini paylaşıyoruz.
Giriş
Move, 2018 yılında Libra projesinin ilk günlerinde doğdu — iki Mysten kurucusu (Evan ve ben) aynı zamanda Libra’nın kurucu ekibindeydik. Yeni bir dil yaratmaya karar vermeden önce, Libra’nın ilk ekibi, geliştiricilerin ne yapmak istediğini ve mevcut dillerin nerede yetersiz kaldığını anlamak için mevcut akıllı sözleşme kullanım durumlarını ve dillerini yoğun bir şekilde inceledi. Tespit ettiğimiz temel sorun, akıllı sözleşmelerin tamamen varlıklar ve erişim kontrolü ile ilgili olması, ancak ilk akıllı sözleşme dillerinin her ikisinin de tür/değer temsilinden yoksun olmasıdır. Move hipotezine göre, bu temel kavramlar için birinci sınıf soyutlamalar sağlarsak, hem akıllı sözleşmelerin güvenliğini hem de akıllı sözleşme programcılarının üretkenliğini önemli ölçüde artırabiliriz — eldeki görev için doğru kelime dağarcığına sahip olmak her şeyi değiştirir. Yıllar boyunca Move’un tasarımına ve uygulanmasına çok sayıda kişi katkıda bulundu ve dil, “web3'ün JavaScript’i” olma gibi cesur bir fikri temel alma hedefiyle platformdan bağımsız bir akıllı sözleşme diline dönüştü.
Bugün, Move’un Sui’ye entegrasyonundaki kilometre taşını duyurmaktan heyecan duyuyoruz: Sui Move, gelişmiş araçlarla desteklenen eksiksiz bir özelliktir ve aşağıdakiler de dahil olmak üzere kapsamlı dokümanlara ve örneklere sahiptir:
Sui Move objeleri ile programlama üzerine bir eğitim serisi
Sui Move temelleri, tasarım modelleri ve örneklerinden oluşan bir tarif kitabı
Mysten Move ekibi tarafından oluşturulan kod anlama ve hata tanılama desteği ile geliştirilmiş VSCode eklentisi!
Move derlemeleri, testleri, paket yönetimi, dokümantasyon oluşturma ve Move Prover’ın sui* *CLI ile entegrasyonu
Fungible** **tokenlar, NFT’ler, DeFi ve oyunları içeren bir dizi örnek.
2021'in sonlarına doğru Sui üzerinde çalışmaya başladığımızda, Move’a yeni bir bakış attık ve hangi erken tasarım kararlarının yeterince iyi oturmadığını hem de Move’un Sui’nin benzersiz özelliklerinden yararlanmak için nasıl geliştirilebileceğini düşündük. Daha önce Sui Move’da dil seviyesinde nelerin yeni olduğu hakkında bir yazı yazmıştık, ancak bu farklılıkları ortaya koyma motivasyonunu derinlemesine incelememiştik. Bu yazının geri kalanında, bu sorgulama örnek odaklı bir şekilde ayrıntılı olarak ele alınmaktadır.
Bir Dakika, farklı Move’lar da mı var?
Move platformlar arası, gömülü bir dildir. Çekirdek dilin kendisi çok basittir: yapılar, tamsayılar ve adresler gibi genel kavramlara sahiptir, ancak hesaplar, işlemler, zaman, kriptografi vb. gibi blok zincirine özgü kavramlara sahip değildir. Bu özellikler Move’a entegre blok zinciri platformu tarafından sağlanmalıdır. Daha da önemlisi, bu blok zincirlerinin kendi Move çatallarına ihtiyacı yoktur — her platform aynı Move VM, bytecode doğrulayıcı, derleyici, prover, paket yöneticisi ve CLI’yi kullanır, ancak bu temel bileşenlerin üzerine inşa edilen kod aracılığıyla blok zincirine özgü özellikler ekler.
Diem, Move’a gömülen ilk blok zinciriydi ve sonraki Move tabanlı zincirler (0L, StarCoin ve Aptos dahil) büyük ölçüde Diem tarzı yaklaşımı kullandı. Diem tarzı Move bazı başarılı niteliklere sahip olsa da, hem Diem’in izinli yapısı hem de Diem blok zincirinin uygulama ayrıntıları (özellikle depolama modeli) bazı temel akıllı sözleşme kullanım durumlarının uygulanmasını zorlaştırmaktadır. Özellikle Move ve Diem’in orijinal tasarımları, NFT’lerin popülerlik patlamasından önce ortaya çıkmıştır ve NFT ile ilgili kullanım durumlarının uygulanmasını özellikle zorlaştıran bazı uygunsuzluklara sahiptir.
Bu yazıda, orijinal Diem tarzı Move gömme ile ilgili bir sorunu sergileyen ve Sui Move’da bu sorunu nasıl ele aldığımızı gösteren üç tür örnek üzerinden yürüyeceğiz. Move hakkında temel bir anlayışa sahip olduğunuzu varsayıyoruz, ancak asıl kilit noktaların programlama geçmişi olan herkes tarafından anlaşılabilir olacağını umuyoruz.
Kolay toplu varlık oluşturma
Varlıkları toplu olarak oluşturma ve dağıtma becerisi, hem web3 kullanıcılarını takıma katmak hem de kullanıcıların ilgisini çekmek için son derece önemlidir. Belki de bir Twitch yayıncısı hatıra NFT’leri dağıtmak, bir içerik üreticisi özel bir etkinlik için bilet göndermek veya bir oyun geliştiricisi tüm oyuncularına yeni öğeler dağıtmak istiyordur.
İşte Diem tarzı Move’da bir varlığın toplu basımı için kod yazmaya yönelik (başarısız) bir girişim. Bu kod girdi olarak alıcı adreslerinin bir vektörünü alır, her biri için bir varlık oluşturur ve varlığı aktarmaya çalışır.
struct CoolAsset { id: GUID, creation_date: u64 } has key, storepublic entry fun mass_mint(creator: &signer, recipients: vector<address>) {
assert!(signer::address_of(creator) == CREATOR, EAuthFail);
let i = 0;
while (!vector::is_empty(recipients)) {
let recipient = vector::pop_back(&mut recipients);
assert!(exists<Account>(recipient), ENoAccountAtAddress);
let id = guid::create(creator);
let creation_date = timestamp::today();
// error! recipient must be `&signer`, not `address`
move_to(recipient, CoolAsset { id, creation_date })
}
Diem tarzı Move’da, global depolama (adres, tür adı) çiftleri ile şifrelenir — yani, her adres belirli bir türden en fazla bir varlık depolayabilir. Böylece, move_to(recipient, CoolAsset { ... }* *satırı CoolAsset’i recipientadresi altında depolayarak aktarmaya çalışır.
Ancak, bu kod move_to(recipient, …) satırında derlenemeyecektir. Temel sorun, Diem tarzı Move’da CoolAssettüründe bir değeri A adresine gönderemezsiniz:
1- A olmayan bir adres A’da bir hesap oluşturmak için bir işlem gönderir
2- A’nın sahibi, CoolAsset türündeki nesneleri almayı açıkça tercih etmek için bir işlem gönderir
Bu sadece bir varlık almak için iki işlem demek! İşleri bu şekilde yapma kararı, hesap oluşturmayı dikkatlice kısıtlaması ve depolama sistemindeki sınırlamalar nedeniyle hesapların yüksek miktarda varlık tutmasını önlemesi gereken izinli bir sistem olan Diem için mantıklıydı. Ancak bu, varlık dağıtımını ilk katılım mekanizması olarak kullanmak isteyen ya da genel olarak varlıkların Ethereum ve benzeri blok zincirlerinde olduğu gibi kullanıcılar arasında serbestçe gönderilmesine izin veren açık bir sistem için son derece kısıtlayıcıdır [1].
Şimdi aynı kodu Sui Move’da inceleyelim:
struct CoolAsset { id: VersionedID, creation_date: u64 } has keypublic entry fun mass_mint(recipients: vector<address>, ctx: &mut TxContext) {
assert!(tx_context::sender(ctx) == CREATOR, EAuthFail);
let i = 0;
while (!vector::is_empty(recipients)) {
let recipient = vector::pop_back(&mut recipients);
let id = tx_context::new_id(ctx);
let creation_date = tx_context::epoch(); // Sui epochs are 24 hours
transfer(CoolAsset { id, creation_date }, recipient)
}
}
Sui Move’un küresel depolaması nesne kimlikleri tarafından şifrelenir. keyyeteneğine sahip her yapı, küresel olarak benzersiz bir idalanına sahip olması gereken bir “Sui nesnesidir”. Sui Move, kısıtlı move_to yapısını kullanmak yerine, herhangi bir Sui nesnesi üzerinde kullanılabilecek bir transfer ilkeli sunar. Bu ilkel, id’yi global depodaki CoolAsset ile eşleştirir ve değerin recipient’a ait olduğunu belirtmek için meta veri ekler.
mass_mint’in Sui versiyonunun ilginç bir özelliği, diğer tüm işlemlerle (mass_mint’i çağıran diğerleri dahil!) döngü oluşturmasıdır. Sui çalışma zamanı bunu fark edecek ve bu işlevi çağıran işlemleri, konsensusa ihtiyaç duymayan Byzantine “hızlı yolu” üzerinden gönderecektir. Bu tür işlemler hem işlenebilir hem de paralel olarak yürütülebilir! Bu, programcıya ekstra bir is çıkarmaz — sadece yukarıdaki kodu yazar ve çalışma zamanı gerisini halleder.
Bu kodun Diem varyantı için bu durum geçerli değildir — yukarıdaki kod çalışsa bile, hem exists<Account> hem de guid::create çağrıları GUID üreten veya Accountkaynağına dokunan diğer işlemlerle uyuşmazlık yaratacaktır. Bazı durumlarda, uyumsuzluklardan kaçınmak için Diem tarzı Move kodunu yeniden yazmak mümkündür, ancak Diem tarzı Move yazmanın uyumlu yollarının çoğu, paralel yürütmeye ket vuran darboğazlar sorunlarını ortaya çıkarır.
Yerel varlık sahipliği ve transferleri
Diem tarzı Move kodunu gerçekten derlenecek ve çalışacak bir geçici çözümle genişletelim. Bunu yapmanın uyumlu yolu “wraper pattern”dir: Bob, Alice’in adresine bir CoolAsset’i doğrudan move_to yapamayacağı için, Alice’ten önce içinde bir koleksiyon türü (Table) olan bir wrapper türü CoolAssetStoreyayınlayarak CoolAsset’leri almayı “tercih etmesini” isteriz. Alice bunu opt_in fonksiyonunu çağırarak yapabilir. Daha sonra Bob’un CoolAssetStore’undaki bir CoolAsset’i Alice’in CoolAssetStore’una taşımasını sağlayan kodu ekliyoruz.
Bu koda bir başka ayrıntı daha ekleyelim: CoolAsset’lerin aktarılmasına, oluşturulmasının üzerinden en az 30 gün geçmişse izin vereceğiz. Bu tür bir politika, (örneğin) fırsatçıların etkinlik biletlerini satın almasını / karaborsaya düşürmesini engellemek isteyen içerik oluşturucular için önemlidir, böylece gerçek hayranların bunları makul bir fiyata almaları daha kolay olur.
struct CoolAssetStore has key {
assets: Table<TokenId, CoolAsset>
}public fun opt_in(addr: &signer) {
move_to(addr, CoolAssetHolder { assets: table::new() }
}public entry fun cool_transfer(
addr: &signer, recipient: address, id: TokenId
) acquires CoolAssetStore {
// withdraw
let sender = signer::address_of(addr);
assert!(exists<CoolAssetStore>(sender), ETokenStoreNotPublished);
let sender_assets = &mut borrow_global_mut<CoolAssetStore>(sender).assets;
assert!(table::contains(sender_assets, id), ETokenNotFound);
let asset = table::remove(&sender_assets, id);
// check that 30 days have elapsed
assert!(time::today() > asset.creation_date + 30, ECantTransferYet)
// deposit
assert!(exists<CoolAssetStore>(recipient), ETokenStoreNotPublished);
let recipient_assets = &mut borrow_global_mut<CoolAssetStore>(recipient).assets;
assert!(table::contains(recipient_assets, id), ETokenIdAlreadyUsed);
table::add(recipient_assets, asset)
}
Bu kod çalışıyor. Ancak bir varlığı Alice’ten Bob’a aktarmak gibi basit bir amacı gerçekleştirmek için oldukça karmaşık bir yol! Tekrar Sui Move varyantına bakalım:
public entry fun cool_transfer(
asset: CoolAsset, recipient: address, ctx: &mut TxContext
) {
assert!(tx_context::epoch(ctx) > asset.creation_date + 30, ECantTransferYet);
transfer(asset, recipient)
}
Bu kod çok daha kısadır. Burada dikkat edilmesi gereken en önemli şey cool_transfer’in bir entryfonksiyonu olması (yani bir işlem aracılığıyla doğrudan Sui çalışma zamanı tarafından çağrılabilir), ancak girdi olarak CoolAssettüründe bir parametreye sahip olmasıdır. Sui çalışma zamanı yine iş başında! Bir işlem, üzerinde çalışmak istediği nesne kimlikleri kümesini içerir ve Sui çalışma zamanı:
Kimliklerin nesne değerlerine dönüştürür (yukarıdaki Diem tarzı kodda borrow_global_mut ve table_remove kısımlarına olan ihtiyacı ortadan kaldırır)
Nesnenin işlemin göndericisine ait olup olmadığını kontrol eder (signer::address_of kısmına + yukarıdaki ilgili koda olan ihtiyacı ortadan kaldırır). Birazdan açıklayacağımız gibi bu kısım özellikle ilginçtir: Sui’de, güvenli nesne sahipliği sahiplik kontrolleri çalışma zamanının bir parçasıdır!
Nesne değerlerinin türlerini, çağrılan cool_transfer işlevinin parametre türlerine göre kontrol eder
Nesne değerlerini ve diğer bağımsız değişkenleri cool_transfer parametrelerine bağlar ve işlevi çağırır
Bu, Sui Move programcısının mantığın “para çekme” kısmının zahmetli kısmını atlamasına ve doğrudan ilginç kısma geçmesine olanak tanır: 30 günlük sona erme politikasını kontrol etmek. Benzer şekilde, “para yatırma” kısmı da yukarıda açıklanan Sui Move transferyapısı ile büyük ölçüde basitleştirilmiştir. Ve son olarak, CoolAssetStoregibi dahili bir koleksiyona sahip bir wrapper türü tanıtmaya gerek yoktur — kimlik endeksli Sui global depolama, bir adresin belirli bir türde rastgele sayıda değer depolamasına olanak tanır.
Belirtilmesi gereken bir diğer fark ise Diem tarzı cool_transfer’in 5 farklı şekilde iptal edilebilmesine (yani başarısız olup transferi tamamlamadan kullanıcıdan gaz ücreti almasına) karşın Sui Move cool_transfer’in sadece 1 şekilde iptal edilebilmesidir: 30 günlük politika ihlal edildiğinde.
Nesne sahipliği kontrollerinin çalışma zamanına yüklenmesi sadece kolaylık açısından değil, güvenlik açısından da büyük bir kazançtır. Bunun çalışma zamanı seviyesinde güvenli bir şekilde uygulanması, bu kontrollerin yapım aşamasında uygulanması (ya da tamamen unutulması!) hatalarını önler.
Son olarak, Sui Move giriş noktası işlev imzasının cool_transfer( asset: CoolAsset, ...) bize işlevin ne yapacağı hakkında ne kadar fazla bilgi verdiğine dikkat edin (daha opak olan Diem tarzı işlev imzasının aksine). Bu fonksiyonun CoolAsset’i transfer etme izni istediğini düşünebiliriz, oysa farklı bir fonksiyon olan f(asset: &mut CoolAsset, ...)* CoolAsset’i yazma (ancak transfer etmeme) izni ister ve g(asset: &CoolAsset, ...) *sadece okuma izni ister.
Bu bilgi doğrudan fonksiyon imzasında mevcut olduğundan (yürütme veya statik analiz gerekmez!), doğrudan cüzdan ve diğer istemci araçları tarafından kullanılabilir. Sui cüzdanında, kullanıcıya iOS/Android tarzı bir izin istemi sağlamak için bu yapılandırılmış işlev imzalarından yararlanan ve aynı zamanda** insan tarafından okunabilir imzalama istekleri **üzerinde çalışıyoruz. Cüzdan şöyle bir şey söyleyebilir: “Bu işlem CoolAsset’inizi okuma, AssetCollection’ınızı yazma ve ConcertTicket’ınızı aktarma izni istiyor. Devam edelim mi?”.
İnsan tarafından okunabilir imzalama talepleri, cüzdan kullanıcılarının etkilerinin ne olabileceğini anlamadan işlemleri körü körüne imzalamak zorunda kaldığı mevcut birçok platformda (Diem tarzı Move! kullananlar da dahil) bulunan büyük bir saldırı vektörünü ele almaktadır. Cüzdan deneyimini daha az tehlikeli hale getirmenin, kripto cüzdanlarının yaygın olarak benimsenmesini teşvik etmek için önemli bir adım olduğunu düşünüyoruz ve Sui Move’u, insan tarafından okunabilir imzalama istekleri gibi özellikleri etkinleştirerek bu hedefi destekleyecek şekilde tasarladık.
Heterojen varlıkların gruplandırılması
Son olarak, farklı türlerdeki varlıkların gruplandırılmasına ilişkin bir örnek ele alalım. Bu oldukça yaygın bir kullanım durumudur: bir programcı farklı türlerdeki NFT’leri bir koleksiyonda gruplamak, bir markette birlikte satılacak öğeleri bir araya getirmek veya mevcut bir öğeye aksesuar eklemek isteyebilir. Somut olarak, aşağıdaki senaryoya sahip olduğumuzu varsayalım:
Alice bir oyunda kullanılmak üzere bir Characternesnesi tanımladı
Alice, karakterini daha sonra oluşturulan farklı türlerdeki üçüncü taraf aksesuarlarla süslemek istiyor
Herkes aksesuar yaratabilmelidir, ancak Characterin sahibi aksesuar ekleyip eklemeyeceğine karar vermelidir.
Bir Characteraktarıldığında, tüm aksesuarları otomatik olarak aktarılmalıdır.
Bu sefer, Sui Hareket kodu ile başlayalım. Sui çalışma zamanı yerleşik nesne sahipliği özelliğinin başka bir yönünden yararlanacağız: bir nesne başka bir nesne tarafından sahiplenilebilir. Her nesnenin benzersiz bir sahibi vardır, ancak bir ebeveyn nesnenin isteğe bağlı sayıda çocuk nesnesi olabilir. Ebeveyn/çocuk nesne ilişkileri, yukarıda tanıtılan transfer fonksiyonunun bir kardeşi olan transfer_to_object fonksiyonu kullanılarak oluşturulur.
// in the Character module, created by Alice
struct Character has key {
id: VersionedID,
favorite_color: u8,
strength: u64,
...
}/// The owner of `c` can choose to add `accessory`
public entry fun accessorize<T: key>(c: &mut Character, accessory: T) {
transfer_to_object(c, accessory)
}// ... in a module added later by Bob
struct SpecialShirt has key {
id: VersionedID,
color: u8
}public entry fun dress(c: &mut Character, s: Shirt) {
// a special shirt has to be the character's favorite color
assert!(character::favorite_color(c) == s.color, EBadColor);
character::accessorize(c, shirt)
}// ... in a module added later by Clarissa
struct Sword has key {
id: VersionedID,
power: u64
}public entry fun equip(c: &mut Character, s: Sword) {
// a character must be very strong to use a powerful sword
assert!(character::strength(c) > sword.power * 2, ENotStrongEnough);
character::accessorize(c, s)
}
Bu kodda, Karakter modülü, bir karakterin sahibinin alt nesne olarak rastgele bir türe sahip bir accessorizenesnesi eklemesine olanak tanıyan bir aksesuar işlevi içerir. Bu, Bob ve Clarissa’nın Alice tarafından öngörülmeyen farklı niteliklere ve işlevselliğe sahip kendi aksesuar türlerini oluşturmalarına, ancak Alice’in hâlihazırda yaptıklarının üzerine ekleme yapmalarına olanak tanır. Örneğin, Bob’un gömleği yalnızca karakterin en sevdiği renkse giyilebilir ve Clarissa’nın kılıcı yalnızca karakter onu kullanabilecek kadar güçlüyse kullanılabilir.
Diem tarzı Move’da bu tür bir senaryoyu uygulamak mümkün değildir. İşte yetersiz kalan birkaç uygulama stratejisi denemesi:
// attempt 1
struct Character {
// won't work because every Accessory would need to be the same type + have
// the same fields. There is no subtyping in Move.
// Bob's shirt needs a color, and Clarissa's sword needs power--no standard
// representation of Accessory can anticipate everything devs will want to
// create
accessories: vector<Accessory>
}// attempt 2
struct Character {
// perhaps Alice anticipates the need for a Sword and a Shirt up front...
sword: Option<Sword>,
shirt: Option<Shirt>
// ...but what happens when Daniel comes along later and wants to add Pants?
}// attempt 3
// Does not support accessory compositions. For example: how do we represent a
// Character with Pants and a Shirt, but no Sword?
struct Shirt { c: Character }
struct Sword { s: Shirt }
struct Pants { s: Sword }
Diem tarzı Move’da temel sorunlar vardır:
Sadece homojen koleksiyonlar desteklenir (ilk denemenin gösterdiği gibi), ancak aksesuarlar temelde heterojendir
Nesneler arasındaki ilişkiler yalnızca “wrapping” (yani bir nesneyi başka bir nesnenin içinde saklama) yoluyla oluşturulabilir; ancak wraplanacak nesneler kümesi önceden tanımlanmalı (ikinci girişimde olduğu gibi) veya aksesuar bileşimini desteklemeyen geçici bir şekilde eklenmelidir (üçüncü girişimde olduğu gibi)
Sonuç
Sui, Move’u kullanma biçimiyle orijinal Diem tasarımından önemli ölçüde farklılaşan ilk platformdur. Move’dan ve platformun benzersiz özelliklerinden tam olarak yararlanan yerleştirmeler tasarlamak, hem Move dilini hem de altta yatan blok zincirinin yeteneklerini derinlemesine anlamayı gerektiren hem bir sanat hem de bir bilimdir. Sui Move’un kaydettiği ilerlemeler ve sağlayacağı yeni kullanım alanları konusunda gerçekten heyecanlıyız!
[1] Diem tarzı Move’un “belirli bir türden bir varlık almak için kabul edip onaylamanız gerekir” politikası lehine yapılan bir diğer argüman da bunun spam önleme için iyi bir mekanizma olduğudur. Ancak biz spam önlemenin uygulama katmanına ait olduğunu düşünüyoruz. Kullanıcılardan bir varlığı almayı kabul etmeleri için gerçek paraya mal olan işlemler göndermelerini istemek yerine, spam, zengin kullanıcı tanımlı politikalar ve otomatik spam filtrelerinin yardımıyla (örneğin) cüzdan düzeyinde kolayca ele alınabilir.
Sui Website — https://sui.io/
Sui Twitter — https://twitter.com/SuiNetwork
Sui Discord — https://discord.gg/sui
No activity yet