Havuzlar 18
Madenciler yok
İşçiler yok

< Tüm yazılar

Ravencoin KAWPOW konsensüs hatası

4.487.776 yüksekliğinde kazılan bir Ravencoin bloğu, kendi başlığına daha düşük bir yükseklik yazdı. Bu sayı 2.383.550'deki son kontrol noktasının altında kaldığı için doğrulama, tam memory-hard kontrolü çalıştırmak yerine başlıkta zaten bulunan mix_hash'e güvendi.

Ravencoin, ağustosun ikinci haftasını zincirini kabaca üç gün geri sararak geçirdi. Sebep bir madencilik karteli, bir cüzdan açığı ya da bozuk bir hash fonksiyonu değildi. Sebep, düğümün iki proof of work kontrolünden hangisini çalıştıracağını söylemesi için güvendiği, blok başlığındaki tek bir alandı. Aşağıda alıntılanan her kod parçası bir duyurudan değil, Ravencoin'in kendi konsensüs kaynağından okunmuştur; duyuruların söylediği ama kaynağın göstermediği bir şey olduğunda, bu yazı tahmin yürütmek yerine bunu açıkça belirtir.

Bir KAWPOW başlığı neler taşır

KAWPOW, Ravencoin'in proof of work algoritmasıdır; zincirin coin'i sıradan GPU'larda tutmak için Mayıs 2020'de geçtiği bir ProgPoW varyantıdır. Bu geçiş blok başlığını değiştirdi. Aktivasyon zamanından önce başlık, Bitcoin'den türeyen her zincirin kullandığı sıradan 32 bitlik nonce ile biter. Sonrasında serileştirici bu yuvaya sırasıyla üç alan yazar: nHeight, ardından nNonce64, ardından mix_hash.

Bunlardan ikisi sıradandır. nNonce64 yalnızca daha geniş bir nonce'tur. mix_hash, algoritmanın memory-hard kısmının ara sonucudur; doğrulayıcının sıfırdan tekrarlayacağı bir hesaplama yerine karşılaştırabileceği bir değeri olsun diye başlıkta taşınır. İlginç olan üçüncüsüdür. nHeight, bloğun kendi başlığının içinde hangi yükseklikte olduğuna inandığını söylemesidir.

Bir bloğun zincirdeki gerçek konumu bir görüş meselesi değildir. Bunu hashPrevBlock belirler: hangi bloğun üzerine inşa ederseniz oraya düşersiniz ve bu konuda yalan söyleyip yine de zincirin ucuna bağlanamazsınız. nHeight aynı gerçeğin ikinci, yazılabilir bir kopyasıdır ve bir gerçeğin kopyası, onu asıl değerle karşılaştıran kontrol kadar değerlidir.

İki hash fonksiyonu, biri hiçbir şeyi doğrulamıyor

Ravencoin'in hash.cpp dosyası iki KAWPOW giriş noktası tanımlar ve imzaları, gövdelerinden tek satır okumadan hikâyenin çoğunu anlatır. Birincisi uint256 KAWPOWHash(const CBlockHeader& blockHeader, uint256& mix_hash). İkincisi uint256 KAWPOWHash_OnlyMix(const CBlockHeader& blockHeader).

Birincisi mix_hash'i bir çıktı parametresi olarak alır, çünkü onu üretir. Veri setini ethash::get_epoch_number(blockHeader.nHeight) ile seçer, bu epoch bağlamı elinde yoksa onu kurar ve ardından bunun üzerinde progpow::hash çalıştırır. Döndürdüğü mix, işi yapmış olmanın bir yan ürünüdür. Bu memory-hard yoldur ve memory-hard olması, düz bir hash yerine KAWPOW'un var olmasının tek nedenidir.

İkincisi böyle bir parametre almaz, çünkü hiçbir şey üretmez. Başlıkta zaten duran mix'i progpow::hash_no_verify(blockHeader.nHeight, header_hash, to_hash256(blockHeader.mix_hash.GetHex()), blockHeader.nNonce64) üzerinden tüketir. Kaynak kütüphane bu fonksiyona hash_no_verify adını vermiştir ve ad tam olarak doğrudur. Kendisine verilen bir değer üzerinde son karıştırma adımını yapar ve sonucu döndürür. Veri setini hiç kurmaz, dolayısıyla kendisine verilen değerin yalnızca iş yapılarak elde edilebileceğini hiçbir zaman kanıtlamaz.

İki fonksiyon da hata değildir. Ucuz olanın var olmasının iyi bir nedeni vardır: sabit kodlanmış bir kontrol noktasının zaten kesinleştirdiği milyonlarca eski bloğu senkronize eden bir düğüm, kimsenin itiraz etmediği geçmişi yeniden onaylamak için her epoch'ta birkaç gigabaytlık bir veri setini yeniden kurmak zorunda kalmamalıdır. Böyle bir tasarımın cevaplaması gereken tek soru, bu yolu hangi blokların kullanabileceğidir.

Kapı ve güvendiği şey

Bu soruyu validation.cpp içindeki CheckBlockHeader cevaplar ve cevabın hemen üstündeki yorum niyeti açıkça belirtir: "If we are checking a KAWPOW block below a know checkpoint height. We can validate the proof of work using the mix_hash". Niyet doğrudur. Uygulama ise kapıyı block.nHeight <= (uint32_t)pcheckpoint->nHeight koşuluna bağlar ve block.nHeight, bloğun kendisi hakkında yazdığı sayıdır.

Altındaki satır genel CheckProofOfWork(uint256 hash, unsigned int nBits, const Consensus::Params&) fonksiyonunu çağırır. Bu imzanın neyi içermediğine bakın. Mix yok, nonce yok, başlık yok. Bitmiş bir hash ve bir hedef alır, ikisini karşılaştırır ve kendisine verilen sayıyı iki fonksiyondan hangisinin ürettiğini bilmesinin hiçbir yolu yoktur. Bir KAWPOW bloğu için CBlockHeader::GetHash() KAWPOWHash_OnlyMix(*this) döndürür, yani kendisine verilen sayı ucuz olanıdır.

Uç uca eklendiğinde, yeterince düşük bir nHeight beyan eden bir blok, kendi mix hash'inden türetilen değerin hedefin altında olup olmadığına bakılarak doğrulanıyordu. Bu yolun hiçbir yerinde mix'in nereden geldiği sorulmadı ve bu yolun hiçbir yerinde beyan edilen yükseklik, bloğun gerçekte bulunduğu konumla karşılaştırılmadı.

Ne kadar düşüğün yeterince düşük olduğu ise hiç de ince bir mesele değil. Yayımlanan mainnet zincir parametrelerindeki en yüksek kontrol noktası 2.383.550'dir. İlk sahte blok 4.487.776'daydı. Başlığının, gerçekte bulunduğu yerin kabaca 2,1 milyon blok gerisinde bir konum iddia etmesi gerekiyordu ve bu şekilde seçtiği dal aradaki farka hiç bakmadı.

Kestirmenin kazandırdıkları ve kazandırmadıkları

Burada kesin olmak önemli, çünkü haberlerin çoğu bu konuda özensiz davrandı ve özensiz anlatım hatayı olduğundan hem daha basit hem de daha tuhaf gösteriyor.

Kestirme zorluğu düşürmedi. nBits, gerçek önceki bloğun gerektirdiğine göre doğrulanır ve gerçek önceki blok, saldırganın tahrif edip yine de zincirin ucuna düşemeyeceği hashPrevBlock ile seçilir. Sahte bir blok, aynı dakikada kazılan dürüst bir bloğun geçtiği hedefin tıpatıp aynısını geçmek zorundaydı.

Değişen, tek bir denemenin bedeliydi. Dürüst bir madenci her denemenin bedelini gigabaytlarla ölçülen bir veri seti üzerinden tam bir geçişle öder; memory-hard bir algoritmanın bütün amacı budur ve KAWPOW'un altı yıldır ASIC'leri Ravencoin'den uzak tutmasının nedeni de budur. Kestirmenin bedeli ise başlığın bir hash'i artı saldırganın serbestçe seçtiği bir değer üzerinde son bir karıştırma adımıdır. Aynı piyango, her bilette aynı olasılık, biletler ise kat kat ucuz. 2Miners sonuçları neredeyse sıfır işli bloklar olarak tanımladı ve KAWPOW'un maliyetinin ne olması gerektiğiyle kıyaslandığında bu adil bir tanım.

Ağda ne oldu

Bilinen ilk geçersiz blok, zaman damgası 2026-08-07 15:44:01 UTC olan 4.487.776 yüksekliğindeki bloktur. İstismar, 10 Ağustos'taki acil sürüme kadar sürdü. İncelenen 2.089 blokluk bir aralıkta 2Miners 96 etkilenmiş blok saydı; yani sahte bloklar tek bir dizide bir arada durmak yerine dalın içine dağılmış durumda ve çözümün cerrahi bir çıkarma yerine bir yeniden düzenleme (reorg) olmak zorunda kalmasının nedeni de bu.

Düğüm operatörleri için görünen belirti, geçersiz bir bloğun sessizce kabul edilmesinden daha kötüydü. Düğümler bu bloklarda çöktü ve yeniden başlatıldıktan sonra senkronizasyonu durdurdu. Tron Black bu mekanizmayı sonradan anlattı ve bu, derli toplu bir yan hasar örneği. Blok dizini, bir bloğun başlığında beyan ettiği yüksekliği değil, gerçekte bulunduğu yüksekliği saklar. Yeniden başlatmada düğüm her başlığı dizinden yeniden kurar ve hash'ini yeniden hesaplar; böylece sahte bir blok için, başlığın yanlış bir yükseklik taşıdığı yerde gerçek yüksekliği hash'ler. Hash artık eşleşmez, proof of work kontrolü başarısız olur ve LoadBlockIndexGuts işlemi iptal eder. Blok bir kez kabul edilmiş, sonra yüklenemez hale gelmişti; ikinci belirtinin birinciden daha uzun sürmesinin nedeni de buydu.

Dal çözüme kavuşana kadar birkaç borsa RVN yatırma ve çekme işlemlerini askıya aldı. Yakın geçmişi yeniden yazılmak üzere olan bir zincire verilecek olağan ve doğru tepki budur ve bunu coin hakkında bir hüküm olarak okumak yerine beklemek gerekir.

Yama ve nereden geldiği

İlk düzeltme 10 Ağustos'ta 4.6.1.1-hf1 sürümü olarak yayımlandı ve Ravencoin projesinden değil, bir madencilik havuzu olan 2Miners'tan geldi. Onu kuran herkes için kanamayı durdurdu. Bir gün sonra yerini yeni sürüme bıraktı.

11 Ağustos'ta Ravencoin deposu üzerinden yayımlanan 4.8.0 sürümü, çalıştırılması gereken sürümdür. Daha eskisiyle yetinmeyin. Bu sürüm 4.487.776'dan itibaren beyan edilen başlık yüksekliği gerçek konumuyla eşleşmeyen blokları reddeder, son temiz blok olarak 4.487.775'i kontrol noktası yapar, bozulmuş zincir durumunu işlemi iptal etmek yerine otomatik olarak yeniden kurar ve acil düzeltmenin içermediği bir varlık transferi taşma düzeltmesini, kaynaktaki başka çalışmalarla birlikte içerir.

Bunlardan yalnızca ilki onarımın kendisidir. Beyan edilen yüksekliği gerçekte bulunduğu yerle eşleşmeyen bir bloğu reddedin, kapı kapanır. Kontrol noktası ve otomatik yeniden kurma ise kurtarmadır ve özellikle bir kontrol noktası kaba bir araçtır: belirli bir bloğun doğru blok olduğuna dair sabit kodlanmış bir iddiadır ve bir zincir bunlardan olabildiğince azıyla idare etmek ister. Burada olmasının nedeni, dürüst dalın yalnızca hashrate'in sağlayacağından daha hızlı kalıcı hale getirilmesi gerekmesiydi.

Kurtarma noktasının kendisi de bariz bir seçim değil, bir takdir meselesiydi. Tron Black, yeniden düzenlemeyi daha sığ tutmak ve sahte blokları istisna olarak ele almak için havuzlardan daha yakın bir kesim noktasını değerlendirmelerini istediğini yazdı. Havuzlar bunu reddetti ve mümkün olan en temiz çizgiyi seçti. Bir proof of work sisteminde bu tartışma kendiliğinden çözülür: dürüst hashrate'in çoğunluğu bir geçmişi reddettiğinde o geçmiş kaybeder ve en büyük iki havuz birlikte çoğunluğa sahipti.

Üç günlük bir reorg gerçekte ne anlama gelir

Savunmasız düğümler sahte blokları kabul ettiği için, bunları içeren dalın üzerinde gerçek zincir birikti. Onu atmak bir yeniden düzenleme demektir ve Ravencoin'in bir dakikalık blok hedefinde 4.487.776'dan yamaya kadar olan aralık kabaca üç günlük bloğa denk gelir.

RVN tutuyorsanız pratik anlamı şudur: atılan dalda onaylanmış işlemler artık onaylı değildir. Çoğu mempool'a döner ve hayatta kalan zincirde yeniden kazılır, ama bu garanti değildir ve bir grup işlem hiç geri gelemez. Atılan bir bloğun coinbase'ini harcayan bir işlem, hayatta kalan zincirde artık var olmayan coin'leri harcıyordur ve onun üzerine inşa edilen her şey de öyle. Bunlar beklemede değil, düpedüz geçersizdir. Borsaların askıya almalarının aşırı bir tepki değil doğru karar olmasının ve 4.487.775 sonrasındaki hareketlerin tek bir zincir ucu kesinleşene kadar dikkat gerektirmesinin nedeni budur.

O hafta bir düğümün ilerlemeyi reddettiğini gördüyseniz bilmeye değer bir ayrıntı var. Ravencoin düğümleri, sürpriz bir çoğunluk saldırısının derin geçmişi yeniden yazmasını önlemek için var olan bir uzun reorg yasağı kuralı taşır. Sürekli çalışan eski düğümlerin çoğu sonunda bu kuralın dayandığı eş sayısının altına düştü ve ardından daha uzun zinciri tasarlandığı gibi kabul etti. Birkaçı etmedi, çünkü hiçbir zaman yeterince eş kaybetmediler ve yazıldığı haliyle kurallara göre hiçbir şey ihlal edilmemişti. Bu, hiç tasarlanmadığı bir sorun karşısında tam olarak tanımlandığı gibi çalışan bir korumadır. Bölünme çoğunluğa yönelik bir saldırıdan değil bir doğrulama kusurundan kaynaklandı ve kural bu ikisini birbirinden ayıramaz.

RVN kazıyorsanız ne yapmalısınız

Kendi Ravencoin düğümünüzü çalıştırıyorsanız 4.8.0'ı çalıştırın; özellikle bu sorunun yalnızca bir kısmını gideren eski sürümlerden birini değil. Düğümü durdurun, ikili dosyayı değiştirin, yeniden başlatın. Çalıştırmanız gereken bir reindex ya da elle yapılacak bir adım yok: yazılım ihtiyaç duyduğunu kendisi yeniden kurar. Acil düzeltmeden gelen bir düğüm hızla yeniden başlar. Yamasız yazılımdan gelen bir düğüm ise yeniden oynatıp yeniden kurmak zorundadır; bu, sıradan donanımda birkaç saat sürer ve bu süre boyunca görülen bad-blk-height hataları yamanın bir kusuru değil, çalıştığının göstergesidir.

Bir havuz üzerinden kazıyorsanız sizin tarafınızda değiştirilecek bir şey yok. Yeni adres yok, yeni port yok, madencilik yazılımı güncellemesi yok. Buradaki sorun hiçbir zaman KAWPOW'un kendisi değildi: algoritma tam olarak yapmak üzere tasarlandığı şeyi yaptı, kusur ise onun ne zaman çalıştırılacağına karar veren koddaydı. Eski dala yönelttiğiniz hashrate gerçek bir işti. Yalnızca, aralarına dağılmış sahte bloklarla birlikte atılan bloklara denk geldi.

Doğru zincirde olduğunuzu tek satırda doğrulamanın bir yolu var ve varsaymak yerine bunu yapmaya değer. 4.494.143 numaralı bloğun hash'ine bakın. Kanonik zincirde bu hash 4c67d6a4 ile biter ve proje ağın toparlandığını duyurduğunda bağımsız Ravencoin explorer'larının hepsi bu aynı zinciri gösteriyordu.

Hatanın biçimi

Ayrıntıları bir kenara bıraktığınızda geriye, zincirlerde ve onların çok dışında sürekli tekrar eden bir biçim kalır. Dışarıdan gelen bir değer, aynı değerin ne kadar dikkatle inceleneceğine karar vermek için kullanıldı. Başlık kendi yüksekliğini beyan etti ve düğüm bu beyanı, başlığın ardından hangi proof of work kontrolünden geçmesi gerektiğini seçmek için kullandı.

Doğru hali karmaşık değil ve yama esasen bunun tek satırı: beyan edilen yüksekliğin herhangi bir şeye karar vermesine izin vermeden önce onu bloğun gerçekte bulunduğu konumla karşılaştırın. Bunun neden eksik olduğu ise işin daha ilginç yarısı. Kestirme zaten güvenilen bloklar için yazılmıştı ve bu varsayımın içinde kapı, kimsenin dürüst olmayan bir şekilde cevaplayabileceği bir soru olarak değil, bir gerçeğin ifadesi olarak okunuyordu. Güvenilmeyen bir bloğun bu alanı doldurmasına izin verildiği anda öyle okunmayı bırakır ve kodda bunun değiştiği anı işaretleyen hiçbir şey yoktu.

Nasıl bulunduğuna dair kayda değer iki şey var. Ravencoin'in kodu profesyonel güvenlik denetimlerinden geçmişti ve bu denetimler ne bu hatayı ne de onunla birlikte düzeltilen varlık taşmasını yakaladı. Tron Black, ikisinin de öncü bir yapay zeka modeliyle yapılan bir güvenlik incelemesiyle ortaya çıktığından kuvvetle şüphelendiğini yazdı. Araç ne olursa olsun, bu biçime uyuyor: kusur, kestirmeyi kendi mantığı içinde okurken görünmezdir ve ancak kestirmenin güvendiği alanı kimin doldurduğunu sorduğunuzda ortaya çıkar.

Dikkat çekmeye değer son şey, kimin ve hangi sırayla harekete geçtiği. Bir madencilik havuzu doğrulama yolunu okudu, anladı ve üç gün içinde acil bir ikili dosya yayımladı. Ardından en büyük iki havuz, uygun olan kesimi seçmek yerine bütün saldırı penceresini atan bir zinciri kazımayı kabul etti; daha sığ olanı seçmeleri istendiğinde de reddettiler. Proje bir gün sonra acil düzeltmenin yerini alan ve ondan fazlasını düzelten sürümle geldi. Hangisinin önce davrandığı, ikisinin de gerçekleşmesinden ve meseleyi gerçekte çözen şeyin iki duyurudan hiçbiri değil, bölünmenin bir tarafında biriken gerçek işin ağırlığı olmasından daha az önemli. Ademi merkeziyet, bir whitepaper'da durmak yerine yük taşıması gereken bir günde böyle görünür.

Bu sayılar nereden geliyor

Sözümüze güvenmek yerine herkes kontrol edebilsin diye. Başlık düzeni, iki hash fonksiyonu ve imzaları, epoch seçimi ve yorumuyla birlikte doğrulama dalı Ravencoin'in kendi kaynağından, src/primitives/block.h, src/primitives/block.cpp, src/hash.cpp, src/hash.h, src/pow.h ve src/validation.cpp dosyalarından okunmuştur. Mainnet'teki en yüksek kontrol noktası olan 2.383.550 dahil kontrol noktası listesi src/chainparams.cpp dosyasından okunmuştur. İlk geçersiz yükseklik ve zaman damgası, Ravencoin projesinin kendi ağ duyurusundan gelir. Blok dizini çökme yolu, geri dönemeyen coinbase'e bağlı işlemler, uzun reorg yasağı davranışı, reddedilen daha sığ kurtarma noktası, denetim ve yapay zeka incelemesi yorumları, 4.8.0'ın içeriği ve 4.494.143 kontrolü, Tron Black'in Ravencoin projesi adına yaptığı anlatımdan gelir. Acil düzeltme sürümü ve 2.089 blok içindeki 96 etkilenmiş blok sayısı, 2Miners'ın sürüm notundan ve onunla ilgili haberlerden gelir. Kaynakların belirtmediği hiçbir şey bu yazıda doldurulmamış, dışarıda bırakılmıştır.