Merkle Ağacı NFT Whitelist için Nasıl Kullanılır?

Eray USTA
6 min readMar 4, 2022

--

Merkle ağacını yakın zamanda NFT whitelist(bundan sonra beyaz liste olarak bahsedeceğim) leri için kontratlara eklendiğini görmüşsünüzdür. Bunu çeşitli şekillerde toplanmış cüzdan adreslerinin belirli işlemi yapabilme yetkisini tanımlamak olarak adlandırabiliriz. Örneğin Pepsi NFT lerini dağıtmak için bu yöntemi kullandı. Kontratını şuradan inceleyebilirsiniz.

Merkle Ağaçları, bugün bildiğimiz blok zincirinin varlığından çok önce, hem kriptografi hem de bilgisayar bilimi alanlarında uzun zamandır kullanılmaktadır. Günümüzde, veri doğrulama amacıyla zincir üzerinde daha sık kullanıldığını yavaş yavaş görmeye başlıyoruz. Bu makalede, adreslerin beyaz listeye alınması amacıyla bir NFT (ERC-721) özelinde Merkle Ağaçlarının nasıl uygulanabileceğini, belirteçlerin yalnızca hedeflenen katılımcılar tarafından talep edilebileceğini ve nasıl güvence sağlayabileceklerini yazmaya çalışacağım.

Merkle Ağacı Nedir?

Merkle Ağaçları, ağaçtaki her düğümün bazı kriptografik hash işlevlerinin sonucu olan bir değerle temsil edildiği ağaç benzeri bir yapıdır. Hash fonksiyonları tek yönlüdür, yani bir girdiden çıktı üretmek kolaydır, ancak çıktıdan girdi belirlemek hesaplama açısından mümkün değildir. Merkle Ağaçları 3 tür düğüm içerir, bunlar aşağıdaki gibidir:

Yaprak Düğümleri (Leaf Nodes) — Bu düğümler, ağacın en altında yer alır ve değerleri, belirli bir hash işlevine göre hash edilen orijinal verinin sonucudur. Bir ağaçta, hash işlemi gerektiren orijinal veri parçalarının sayısı kadar yaprak düğümü vardır. Örneğin. 7 adet verinin hash edilmesi gerekiyorsa, 7 yaprak düğüm olacaktır.

Ana Düğümler (Parent Nodes)— Ana düğümler, genel ağaç boyutuna bağlı olarak ağacın çeşitli seviyelerinde oturabilir, ancak her zaman yaprak düğümlerinin üzerinde bulunur. Üst düğümler, yalnızca en az bir düğüm ve en fazla iki düğüm besler. Bir üst düğümün değeri, tipik olarak soldan sağa başlayarak, altındaki düğümlerin birleştirilmiş hashlerinin hash değeriyle belirlenir. Farklı girdiler her zaman farklı hashler üreteceğinden, hash çarpışması dikkate alınmadan, destekleyici düğüm hashlerinin birleştirilme sırası önemlidir. Ana düğümlerin, ağaç boyutuna bağlı olarak diğer ana düğümleri besleyebileceğini belirtmekte fayda var.

Kök Düğüm (Root Node)— Root node, ağacın tepesinde bulunur ve yine soldan sağa başlayarak, altındaki iki ana düğümün birleştirilmiş karmalarının karmasından türetilir. Herhangi bir Merkle Ağacında yalnızca tek bir kök düğüm vardır ve kök düğüm, root hashe sahiptir.

Bunun sindirilmesi gereken çok fazla bilgi olduğunu biliyorum, bu yüzden bu ağaçların nasıl yapılandırıldığını daha iyi görselleştirmek için lütfen aşağıdaki şemaya (Şekil 1) bakın.

Şekil 1. Merkle Ağacı Yapısı

Daha önce bahsedildiği gibi, bir NFT (ERC-721) özelinde bir Merkle Ağacı kullanmak, belirli bir miktarda tokenın belirli bir katılımcı grubu, kendi başına bir beyaz liste için ayrıldığı durumlarda faydalı olacaktır. Merkle Ağaçları önceden hesaplanmalıdır ve bu nedenle üye başına farklı bir tür veri kullanır. Bu bağlamda, tek bir yaprak düğümün beyaz listemizdeki tek bir cüzdan adresini temsil ettiğini varsayalım.

Projenizin, bir yarışma, çekiliş veya başka bir sistem aracılığıyla seçilmiş olabilecek belirli cüzdan adresleri için rastgele sayıda token ayrıldığı bir beyaz liste stratejisi uyguladığını hayal edelim. Beyaz listeye alınan bu adreslere, çeşitli nedenlerle halka açık para basmadan önce belirli bir zamanda ayrılmış tokenlarını talep etme yeteneği verilmiştir. Bunlar, yüksek gaz ücretlerinden kaçınmak, yaratıcılığı ödüllendirmek, erken katılım, topluluk katılımı vb. ile ilgili olabilir.

Bu adresler bilindiği ve sabit olduğu için bu bilgiyi Merkle Ağacı oluşturmak için kullanabiliriz. Bunu göstermek için merkletreejs ve keccak256 JavaScript kitaplıklarını kullanalım. Not: Basitlik adına, ağaç boyutunu kısa tutmak için yalnızca 8 cüzdan adresi kullanacağım.

Kod yazmadan denemek isterseniz cüzdan adreslerinizi veya herhangi bir veri girerek kendi merkletree ağacınızı aşağıdaki linkten oluşturabilirsiniz:

https://lab.miguelmota.com/merkletreejs/example

JavaScript Uygulaması (nodejs)

Yapmak istediğimiz ilk şey, yaprak düğümlerimizi türetmek. Hatırlarsanız, bir ağaçta doğrudan yaprak düğümlerinin üzerinde bulunan her bir üst düğüm, yalnızca en fazla iki yaprak düğümü besleyecektir. Eşit olmayan sayıda yaprak düğüm varsa, bir üst düğüm tek bir yaprak düğümü besleyecektir. Her yaprak düğümü bir tür hash veri olmalıdır, bu nedenle bu örnek için beyaz listemizdeki tüm adresleri karma hale getirmek için keccak256 kitaplığını kullanalım. Daha sonra Solidity akıllı sözleşmemizde kullanılacağı için bu özel hash algoritmasını kullanacağız.

Önceki yazılarımda nodejs kurulumundan bahsetmiştim. Nodejs kurulu ise aşağıdaki kodu çalıştırmanız için bir klasör açıp

npm install merkletreejs
npm install keccak256

terminalde komutlarını verin ve aşağıdaki kodları merkletree.js olarak kayıt edin. Daha sonra terminalden node merkletree.js dediğinizde kendi merkle ağacınızı oluşturmuş olacaksınız.

Beyaz listemizdeki tüm adresleri hash ettikten ve böylece yaprak düğümlerimizi elde ettikten sonra, artık Merkle Tree nesnesini oluşturabiliriz. Bunu merkletreejs kitaplığını kullanarak ve yeni MerkleTree() işlevini çağırarak, ilk argüman olarak yaprak düğümlerimizi, ikinci olarak karma algoritmamızı ve son olarak { sortPairs: true } seçeneğini ileterek yapıyoruz.

new MerkleTree(leafNodes, keccak256, {sortPairs: true}); 
Şekil 3 : Kodun console çıktısı

Artık tam bir Merkle Ağacı türetmiş olduğumuza göre, Merkle Tree nesnemizde getHexRoot() yöntemini (Şekil 3) çağırarak root hashi alabiliriz. Bir Merkle Ağacının root hashi, doğrudan ağaçtaki kök düğümün altındaki önceki iki ana düğümün hash i olduğunu unutmayın. Merkle Ağacımızı toString() yöntemi kullanılarak konsolda ağacımızın nasıl yapılandırıldığına dair güzel bir görselleştirme sağlanabilir.

Bir Merkle Ağacının yaratıcılığı, bir düğümün bizim ağacımıza ait olduğunu doğrulamak için orijinal veri blokları hakkında herhangi bir bilgi gerektirmemesi gerçeğinden kaynaklanmaktadır. Bir yaprak düğümün bizim ağacımıza ait olduğunu doğrulamaya çalışıyorsak, yalnızca doğrudan komşu yaprak düğümlerin hash’inin (varsa) ve komşu üst düğümün doğrudan yaprak düğümlerinin üzerindeki hash’lerinin bilgisi gereklidir. Bunun nasıl çalıştığına dair kısa ve tatlı bir açıklama için Tara Vancil’in bu videosunu izlemenizi tavsiye ederim. Bu bilgiler aksi takdirde kanıt olarak bilinir ve bir arayanın beyaz listemizin dışında olduğunu doğrulamak için Solidity akıllı sözleşmemizde kullanılacaktır.

Burda kullandığımız cüzdan adresi, root hashi ve yaprak için oluşan proof bilgisi doğrulama için yeterli olacaktır.

// proof leaf için merkle treenin türettiği bir hash dizisi, 
// leaf (cüzdan adresinin keccak256 ile hashlenmiş hali)
// rootHash (Merkle ağacı oluşturduktan sonra oluşan kök hash)
console.log(merkleTree.verify(proof, leaf, rootHash));

Buraya kadar olan kısımda hem merkle ağacımızı oluşturduk ve doğrulanma sürecini javascript ile denemiş olduk. Şimdi akıllı sözleşmemizde nasıl kullanacağımıza bakabiliriz.

Akıllı Kontrat Uygulaması

Not: Gösterilen akıllı sözleşme örneği, bir kavram kanıtı göstermek için gereken minimum kod miktarıyla oluşturulmuştur. Bu hiçbir şekilde bir mint fonksiyonunu nasıl yazmanız gerektiğine dair bir örnek değildir.

Merkle Kontratı

Sağlanan kanıtı doğrulamak için yapmamız gereken ilk şey, OpenZeppelin MerkleProof.sol sözleşmesini içe aktarmaktır.(4.satır) Bu, akıllı sözleşme kodumuzda verify() işlevini kullanmamızı sağlayacak. Gerekli olan bir sonraki şey, Merkle root hash i tanımlamaktır. Akıllı sözleşme, beyaz liste sonlandırılmadan önce Ethereum ana ağına dağıtılmışsa, bu değeri daha sonraki bir zamanda güncellemek için kullanılabilecek bir ayarlayıcı işlevi olduğu varsayılır. Bu örnekte, dağıtım sırasında ayarlanması için Merkle karma değerini sabit kodladık. (Satır 13).

Ardından, kanıtı(proof) doğrulamamız gerekiyor. Kanıtın işlemle birlikte gönderildiğini ve bir tür bytes32 değerleri dizisi olduğunu hatırlayın. Teknik olarak string türündeydiler, ancak Solidity bunları ne olursa olsun doğru şekilde yorumlayacaktır. Beyaz listeye alınmış bir adresin keccak256 hash i olan hedef yaprak düğümümüzü (Satır 17- keccak fonksiyonu) oluşturuyoruz. 17.satırda msg.sender değerini hashleyerek hedef yaprak düğümümüzü oluşturuyoruz. Bu değerin değişmez olduğunu ve kötü amaçla değiştirilemeyeceğini unutmayın.

Yaprak düğümlerimizi oluşturmak için yalnızca beyaz listeye alınmış adreslerin kullanılması nedeniyle, beyaz listeye alınmamış bir adres geçerli veya geçersiz bir kanıt(proof) kullanarak bu işlevi çağırmaya çalışırsa, oluşturulan hedef yaprak düğümün bizim üzerimizde olmayacağı varsayılır. Merkle Ağacı ve doğrulama başarısız olacaktır.

Bu uygulamanın son adımı, sağlanan kanıtı ilk argüman olarak, Merkle root hashi ve hedef yaprak düğümünü son olarak ileten proof.verify() işlevini çağırır. Bu fonksiyon false döndürürse, request ifadesi başarısız olur ve işlem basitçe geri alınır, aksi takdirde fonksiyon çalışmaya devam eder ve NFT veya tokenlar basılır.

require(proof.verify(merkleRoot, keccak256(abi.encodePacked(msg.sender))), "Listede degilsin!");

Kaynak:

Alternatif Bet ve Merkle Ağacı uygulaması:

Youtube Videosu:

Beni twitter hesabımdan ve youtube hesabımdan takip edebilirsiniz.

--

--