Cover photo

Sovereign SDK Nasıl Çalışır

Duyuru içim yazdığımız blog yazımızda, bir zk-rollup oluşturmanın bir dapp yaratmak kadar kolay olduğu bir gelecek vizyonu çizdik. Bu blog yazısı, bu vizyonu nasıl gerçekleştireceğimizi açıklamayı amaçlıyor. Konuya girmeden önce kısa bir uyarıda bulunalım - bu yazı biraz teknik olacak ancak tamamen teknik konular üzerine olmayacak. Sovereign SDK'ya teknik olmayan bir giriş yapmak istiyorsanız buraya bakabilirsiniz. Tamamen teknik bir giriş istiyorsanız, GitHub'da Sovereign SDK'nın taslak özelliklerini ve uygulamasını bulabilir veya Discord'dan ulaşabilirsiniz. Bunu aradan çıkardığımıza göre, başlayalım!

Neden ZK Rolluplar?

Bu bölümde, neden zk-rollup'ların blok zincirleri için "oyunun sonu" olduğunu düşündüğümüzü ilk prensiplerden yola çıkarak açıklamaya çalışacağız.

Atomik Birleştirilebilirliğin Bedeli

Ethereum'un can alıcı özelliğinin EVM'nin kendisi değil, atomik birleştirilebilirlik olduğu tezinden yola çıkıyoruz. Herhangi bir akıllı sözleşme diğer herhangi bir akıllı sözleşme ile etkileşime girebilir ve sistem, çağrı yığınının ilerisinde bir şeyler ters gitse bile, çağıranın bazı ara durumlarda takılıp kalmayacağını garanti eder. Bu, programlama modelini büyük ölçüde basitleştirerek Ethereum'u geliştiriciler için en dostane ortamlardan biri olmasını sağlar. Ancak atomik birleştirilebilirliğin bir bedeli var. Bir çağrıyı atomik olarak yürütmek için, tam düğümlerin(full node) çağrı yığınındaki tüm sözleşmelerin ilgili durumuna gerçek zamanlı olarak erişmesi gerekir. Bu da ilgili tüm durumların yerel olarak depolanması ve çok hızlı bir şekilde yüklenebilmesi gerektiği anlamına gelir. Başka bir deyişle, tam düğümlerin SSD satın almaları ve tüm zincir durumunu depolamak için yeterli sayıda SSD’ye sahip olması gerekir.

Ölçeklenebilirliği en üst düzeye çıkarmak için Near gibi sistemler bu gereksinimleri gevşetmeye çalışır. Near, atomik birleştirilebilirlik yerine yalnızca asenkron çapraz sözleşme çağrılarına izin verir. Her bir sözleşmenin durumu yalnızca tam düğümlerin özel bir alt kümesinde saklanabildiğinden, bu kurulum çok daha fazla ölçeklenebilirlik sağlar. Ancak bunun programcıya büyük bir maliyeti vardır - programcı kod yollarının kombinatoryal patlaması üzerinden mantık yürütmek zorundadır.

Her İki Dünyanın En İyisi

Bizim vizyonumuz bu iki uç noktanın sentezi. Tamamen atomik hiçbir sistemin tüm dünyanın ihtiyaçlarını karşılayamayacağına inanıyoruz. Ancak aynı zamanda, atomikliğin basit ve birleştirilebilir akıllı sözleşmelerin geliştirilmesinde oynadığı ana rolün de farkındayız. Bu nedenle, her iki dünyanın da en iyisini elde etmeye çalışıyoruz: her biri atomik birleştirilebilirlik sağlayan, ekosistem boyunca hızlı (ancak eşzamansız) zincirler arası köprülerle birbirine bağlanan optimize edilmiş zincirlerden oluşan bir ekosistem.

Tek bir zincir içinde, geliştiriciler atomik birleştirilebilirliği korurlar. Sözleşme tasarımları nispeten basit olabilir ve kullanıcılar işlemlerinin sonuçlarını neredeyse gerçek zamanlı olarak öğrenebilirler. Ancak aynı zamanda, hiçbir tam düğüm kümesi tüm dağıtık defterlerin güncel durumunu saklamak ve güncellemekten sorumlu değildir.

Blockchain'ler değil, rollup'lar oluşturun

Bu doğrultuda düşünen ilk insanlar kesinlikle biz değiliz. Bildiğimiz kadarıyla, entegre bir çoklu zincir sistemi oluşturmaya yönelik ilk girişim Cosmos'tu. Bizim vizyonumuza çok benzeyen Cosmos, hızlı ve güvenilir köprüleme ile heterojen zincirlerden oluşan aşağıdan yukarıya bir ekosistem. Cosmos'un yetersiz kaldığı nokta yeni zincirler yaratma becerisi. Bir zincir kullanılabilir hale gelmeden önce, işleyişinin arkasına anlamlı bir ekonomik ağırlık koyacak bir "doğrulayıcı setine" ihtiyaç duyar. Sermaye maliyetleri ne olursa olsun, zincir önemli gelirler yaratmak için yeterli kullanıma sahip olana kadar bir doğrulayıcı setini bir zincire çekmek son derece zordur. Ancak hiçbir kullanıcı parasını güvensiz bir zincir için riske atmak istemez. Bu da tavuk-yumurta ikilemine yol açmakta ve yeni zincirlerin başlatılmasını zorlu bir girişim haline getirmektedir.

Buna ek olarak, Cosmos zincirleri köprülerin uygulanmasında temel zorluklarla karşılaşmaktadır. Hafif istemci köprülerinin (IBC) bakımı pahalıdır çünkü başlıkların zincir üzerinde işlenmesi gerekir. Bu da köprülerin seyrek kurulmasına yol açar ve kullanıcı deneyimi de bundan kötü etkilenir. Daha da kötüsü, bir zincir uzun mesafeli bir yeniden yapılanma saldırısına(long distance reorg attack) maruz kalırsa veya doğrulayıcı seti geçersiz bir durum geçişi gerçekleştirirse fonlar kaybedilebilir.

Bu sorunlara çözümümüz basittir: bir doğrulayıcı setini diğer birçok zincirle paylaşmak. Başka bir deyişle, blok zincirler oluşturmayın; rollup oluşturun.

Bu, nihayet bizi fikrimize götürüyor: Sovereign SDK

Sovereign SDK nedir?

Sovereign SDK, eşzamansız olarak kanıtlanmış, egemen zk-rollup'lar oluşturmak için bir geliştirme kitidir. Bu oldukça uzun bir ifade, bu yüzden parça parça inceleyelim.

İlk olarak, işlemlerin eşzamansız olarak kanıtlanması ne anlama geliyor? Basitçe, ham işlem verilerinin bir sıralayıcı tarafından gerçek zamanlı olarak L1'e gönderildiği ve kanıtların daha sonra oluşturulduğu anlamına gelir. Bu, kanıtların zincire herhangi bir veri gönderilmeden önce zincir dışında oluşturulduğu StarkNet gibi rollupların tersidir. Eşzamansız kanıtlamanın avantajı, işlemlerin gerçek zamanlı olarak sonuçlandırılmasına olanak tanıyarak Optimistic rolluplar gibi bir yanıt verebilirlik kazandırmasıdır.

İkinci olarak, bir rollupın egemen olması ne anlama gelir? Bunu anlamanın en kolay yolu "akıllı sözleşme" rollupları ile karşılaştırmaktır. Bir akıllı sözleşme rollupında, L2 durumu ancak L1'deki bir akıllı sözleşme tarafından kabul edildiğinde nihai hale gelir (hafif istemciler açısından). Günümüzün L1 zincirleri bu kadar sınırlı iş hacmine sahip olduğundan, akıllı sözleşme toparlamaları kanonik zincir içi durumlarını nispeten seyrek olarak güncellemek zorunda kalmaktadır. Öte yandan, egemen rollupların hafif düğümleri, belirli bir bloğun kabul edilip edilmeyeceğine kendileri karar vermekten sorumludur. Bu, egemen rolluplarda işlemlerin çok daha hızlı bir şekilde sonuçlandırılmasına olanak sağlar, çünkü blok alanı sıkışık L1'lere hizmet sunmak için güncelleme sıklıklarını kısmak zorunda kalmazlar.

Nasıl Çalışır?

Sovereign SDK, bir zk-rollup'ın işlevselliğini arayüzler hiyerarşisine soyutlayarak çalışır. Hiyerarşinin her seviyesinde, geliştiriciler SDK tarafından sağlanan önceden paketlenmiş uygulamalardan birini kullanmakta veya kendi işlevlerini sıfırdan oluşturmakta özgürdür. Mantığı iyi tanımlanmış arayüzlerin arkasına hapsederek, esneklikten ödün vermeden basit bir geliştirici deneyimi yaratan, eklenebilir bileşenler sağlayabiliyoruz.

Çekirdek API'ler

En yüksek soyutlama düzeyinde, her Sovereign SDK zinciri üç farklı unsuru bir araya getirir:

  1. DA(Veri uygunluğu) ve mutabakat sağlayan bir L1 blok zinciri

  2. Zincirin "iş mantığını" uygulayan bir durum geçiş fonksiyonu (Rust dilinde yazılmış)

  3. (1) öz yineleme ve (2) Rust'ın bir alt kümesini çalıştırabilen sıfır bilgi ispat sistemi

interface DaLayer {
  // Gets all transactions from a particular da layer block that are relevant to the rollup
  // Used only by full-nodes of the rollup
  function get_relevant_txs(header: DaHeader) -> Array<DaTxWithSender>
  // Gets all transactions from a particular da layer block that are relevant to the rollup,
  // along with a merkle proof (or similar) showing that each transaction really was included in the da layer block.
  // Depending on the DA layer, may need to include auxiliary information to show that no 
  // relevant transactions were omitted.
  // Used by provers
  function get_relevant_txs_with_proof(header: DaHeader) -> (Array<DaTxWithSender>, DaMultiProof, CompletenessProof>
  // Verifies that a list of Da layer transactions provided by an untrusted prover is both 
  // complete and correct.
  // Used by the "verifier" circuit in the zkVM
  function verify_relevant_tx_list(txs: Array<DaTxWithSender>, header: DaHeader, witness: DaMultiProof, completenessproof: CompletenessProof)
}

// The interface to a state transition function, inspired by Tendermint's ABCI
interface StateTransitionFunction {
    // Called once at rollup Genesis to set up the chain
  function init_chain(config: Config) 
  // A slot is a DA layer block, and may contain 0 or more rollup blocks
  function begin_slot(slotnumber: u64) 
  // Applies a batch of transactions to the current rollup state, returning a list of the
  // events from each transaction, or slashing the sequencer if the batch was malformed
  function apply_batch(batch: Array<Transaction>, sequencer: Bytes): Array<Array<Event>> | ConsensusSetUpdate
  // Process a zero-knowledge proof, rewarding (or punishing) the prover
  function apply_proof(proof: RollupProof): Array<ConsensusSetUpdate>
  // Commit changes after processing all rollup messages
  function end_slot(): StateRoot
}

interface ZkVM { 
  // Runs some code, creating a proof of correct execution
  function run(f: Function): Proof
  // Verifies a proof, returning its public outputs on success
  function verify(p: Proof): (Result<Array<byte>>)
}

Genelleştirilmiş Tam Düğüm

Az önce açıkladığımız çekirdek arayüzleri kullanan Sovereign SDK, herhangi bir veri kullanılabilirlik katmanı üzerinde neredeyse tüm durum geçiş işlevlerini çalıştırabilen genelleştirilmiş bir tam düğüm uygulaması(full-node implementation) sağlayacaktır. Genel anlamda, tam düğüm, az önce açıkladığımız çekirdek arayüzlerin arkasındaki rollup işlevselliğini kapsülleyerek ve rollup verilerini depolanabilen ve iletilebilen opak bayt dizeleri şeklinde ele alarak çalışır. Tam düğümün nasıl çalışacağına dair bir fikir vermek için, burada çekirdek olay döngüsünün nasıl görüneceğine dair kabaca bir örnek verilmiştir.

// A pseudocode illustration of block execution. Plays fast and loose
// with types for the sake of brevity.
function run_next_da_block(self, prev_proof: Proof, db: Database) {
  // Each proof commits to the previous state as a public output. Use that state 
  // to initialize the state transition processor
  let prev_state = deserialize(prev_proof.verify());
  let current_da_header = db.get_da_header(prev_state.slot_number + 1);
  let processor = stf::new(db, current_da_header, prev_state);
  
  // Fetch the relevant transactions from the DA layer
  let (da_txs, inclusion_proof, completeness_proof) = da::get_relevant_txs_with_proof(current_header);
  da::verify_relevant_tx_list(da_txs, inclusion_proof, completeness_proof)
  
  // Process the relevant DA transactions 
  processor.begin_slot(prev_state.slot_number + 1)
  for da_tx in da_txs { 
    let msg = parse_msg(da_tx);
    if msg.is_batch() { 
      processor.apply_batch(msg)
    } else {
      processor.apply_proof(msg)
    }
  }
  processor.end_slot();
}

Eklenebilir Bileşenler

Tam düğüm uygulaması tamamen genel olduğundan, geliştiriciler zincirlerinin özelliklerini değiştirmek için modülleri kolayca değiştirebilirler. Örneğin, calldata maliyeti konusunda çok endişeli olan bir geliştirici muhtemelen Celestia blok zinciri ile entegrasyonumuz olan Jupiter'i kullanacaktır. Öte yandan, öncelikle likiditeye erişimi önemseyen bir geliştirici Ethereum veya EigenDA üzerine inşa edebilir (bunun için de takılabilir bir modülümüz olacak, ancak prototip aşamasından sonraya kadar geliştirilmeyecek).

Benzer şekilde, geliştiriciler no_std Rust ve birkaç shim API'sini desteklediği sürece kendi zkVM'lerini seçmekte özgürdür. Standart bir derleyici araç zinciri ve VM arayüzü kullanmayı tercih eden geliştiriciler muhtemelen Risc0'ı seçerken, küçük kanıt boyutlarına değer verenler =nil; foundation gibi başka bir VM seçebilirler.

Varsayılan Modüller

Bir soyutlama katmanını atlayan Sovereign SDK, durum geçiş fonksiyonları oluşturma sürecini de basitleştirmeyi amaçlamaktadır. Bu amaçla, Cosmos SDK'sından esinlenerek esnek bir şekilde şekillendirilebilir bir modül sistemi sağlayacağız. Zaman içinde işlevsellik geliştirmeye devam edecek olsak da, en azından şu modüllerle başlamayı planlıyoruz: depolama, köprüleme, (değiştirilebilir) tokenlar ve kanıtlama / sıralama.

Bu yazı yazıldığı sırada modül sisteminin tasarımı hala ilk aşamalarında - bu nedenle bu bölümde okuduğunuz her şey hızlı bir değişime tabidir. Ancak, yine de üzerinde çalıştığımız bazı tasarımların taslağını çıkarmanın, size neler olacağına dair bir fikir vermesi açısından yararlı olacağını düşünüyoruz.

Köprüleme Modülü

İlk olarak, köprüleme modülüne bakalım. Ayrı zincirler arasında köprü kurmanın aksine (ki bunun güçlü güven varsayımları olmadan imkansız olduğu bilinmektedir), paylaşılan bir DA(veri uygunluğu) katmanındaki rolluplar arasında köprü kurmak temelde zor değildir. Sovereign SDK'da, varsayılan olarak düşük gecikmeli güven minimize edilmiş köprüler sağlamak için bu gerçekten tam olarak faydalanmayı planlıyoruz.

Şöyle çalışıyor: Paylaşılan bir DA(veri uygunluğu) katmanında çalışan 50 rollupınız olduğunu varsayın. Standart paradigmada her biri arasında güven minimize edilmiş köprüler çalıştırmak için, her bir rollupın diğer her rollupın zincir içi hafif istemcisini çalıştırması gerekir. Hesaplama yaparsak, bu 50 * 49 = 2450 zincir üstü hafif istemci anlamına gelir. Bunu gerçekleştirmek oldukça pahalı olacağından, bunun yerine çok atlamalı köprüler kullanmaya geri dönersiniz. Her bir zincirin (diyelim ki) 3 diğer zincire bağlanmasını sağlarsınız ve - ağlarınızın makul bir topolojiye sahip olduğunu varsayarsak - 3 veya 4 atlama kullanarak herhangi bir zincir çifti arasında mesajları yönlendirebilirsiniz.

Zk'nın gücü ile çok daha iyisini yapabiliriz. Herhangi iki sıfır bilgi kanıtının (VM modelinde) üçüncü bir kanıtta toplanabileceğini ve bu yeni kanıtın doğrulanmasının orijinallerinden daha pahalı olmadığını hatırlayın. Bu iç görüden yararlanarak, 50 rollup ispatının tamamını zincir dışında öz yinelemeli olarak toplayabilir ve ardından bu rollup ispatını her bir rollup üzerinde doğrulayabiliriz. Böylece 2450 hafif istemci bağlantısını sürdürmek yerine, bir zincir dışı kanıt toplama ve 50 zincir içi kanıt doğrulaması yapabiliriz. Başka bir deyişle, tümden tüme köprülemenin iletişim karmaşıklığını O(n^2)'den O(n)'ye düşürdük.

Depolama Modülü

Verimli zk-rollupların temel bileşenlerinden biri durum yönetimidir. Kimliği doğrulanmış bir veri deposu tasarlarken, geliştiricilerin birbiriyle yarışan çeşitli kaygıları dengelemesi gerekir. Bir yandan, duruma yapılan her okuma ve yazma işleminin sıfır bilgi kanıtı içinde doğrulanması gerektiğinden, devre içinde temsil edilmesi verimli bir şey isterler. Öte yandan, tam düğümleri ve sıralayıcıları darboğaza sokmaması için yerel yürütme sırasında hızlı ve hafif bir şey istiyorlar.

Sovereign SDK'da, bu iki tasarım hedefini dengeleyen varsayılan bir depolama modülü sağlamayı planlıyoruz. zkVM dışında verimlilik için, kimliği doğrulanmış durum verilerini depolamak üzere orijinal olarak Diem tarafından geliştirilen Jellyfish Merkle Tree'yi (JMT) kullanacağız. Hızlı erişim için, ham durumu tutmak üzere disk üzerinde düz bir veri yapısı da tutacağız. Devre içi verimlilik için, JMT'mizi hash fonksiyonu üzerinde genel hale getireceğiz, böylece geliştiriciler kendi zkVM seçimlerine göre uyarlanmış bir hashi kolayca ekleyebilirler.

Hepsini Bir Araya Getirmek: Blok Yaşam Döngüsü

Bir Sovereign zincirine blok eklemek üç adımda gerçekleşir. İlk olarak, bir sıralayıcı L1 zincirine yeni bir işlem verisi bloğu gönderir. Blok L1'de sonlandırılır sonlandırılmaz, yeni bir mantıksal rollup durumu da sonlandırılır.

BLOB: binary large object.

Her L1 bloğu sonlandırıldığında, rollup tam düğümleri bunu tarar ve rollup durum geçiş işleviyle ilgili blobları belirler. Ardından, toparlamanın STF'sini bu blobların her birine sırayla uygulayarak yeni rollup durum kökünü hesaplarlar. Bu noktada blok, tam düğümlerin bakış açısından öznel olarak sonlandırılır.

Daha sonra, kanıtlayıcı düğümler (bir zkVM içinde çalışan tam düğümler) tam düğümlerle aynı işlemi gerçekleştirir - DA(veri uygunluğu) bloğunu tarar ve tüm veri bloblarını sırayla işler. Ancak tam düğümlerin aksine, kanıtlayıcılar kanıt oluşturmak ve bunları zincirde yayınlamak için birbirleriyle yarışırlar. Geçerli bir kanıt oluşturan ilk uygun düğüm, toparlamanın gaz ücretlerine (bir kısmına) karşılık gelen tokenlarla ödüllendirilir. Belirli bir yığın için bir kanıt zincir üzerinde yayınlandıktan sonra, yığın hafif istemciler için öznel olarak nihaidir.

Son olarak, bir aktarıcı, başka bir zincirdeki bir köprü akıllı sözleşmesine (altta yatan DA katmanı ya da başka bir toparlama) yeni bir toparlama kanıtı göndermeyi seçebilir. Verimlilik açısından, aktarıcılar muhtemelen güncellemeleri göndermeden önce birçok toplamanın geçerlilik kanıtlarını tek bir kanıtta birleştirmek için kanıt toplamayı kullanacaktır.

Sonuç

Buraya kadar geldiyseniz, artık Sovereign SDK hakkında iyi bir üst düzey anlayışa sahip olmalısınız. Daha fazlasını öğrenmek istiyorsanız, github'ımıza dalın (katkılara açığız!), Twitter'dan ulaşın veya Discord'da takılın! Ayrıca, iş ilanlarımıza göz atın!