Hedef, imzalanan şeyin içindedir; bu yüzden bir imza başka bir zincirde yeniden kullanılamaz
Solidus Bridge bir kimlik belgesinin durumunu başka zincirlere kopyalar ve her zincirdeki bir sözleşme, bir durumu yalnızca yeterli sayıda doğrulayıcı imzaladıysa kabul eder. Kimlik belgesi durumu köprüsü nedir sayfası yolun tamamını kapsar. Veriyi görmeden bir koşulu denetleyin sayfası, uygulanmış bir durumla bir sözleşmenin ne yaptığını kapsar. Bu sayfa yoldaki tek bir kuralı kapsar: bir imza bir hedef için geçerlidir, başka hiçbiri için değil.
Tekrar saldırısı, tek bir örnekle
Tekrar (replay), gerçek ve geçerli bir mesajın sayılmaması gereken bir yerde yeniden gönderilmesidir. İmza gerçektir. Yer ya da an yanlıştır.
Diyelim ki doğrulayıcılar Sepolia için 41 numaralı mesajı imzaladı. Biri onu imzalarıyla birlikte, yine 41 numaralı bir mesaj bekleyen bir Fuji sözleşmesine kopyalıyor. İmza yalnızca mesaj baytlarını kapsasaydı Fuji, güvendiği imzalayanlardan gelen geçerli imzalar görür ve kopyayı kabul ederdi. Hiçbir şey sahte değildi. İmzalayanlar hiçbir zaman Fuji'yi kastetmedi.
Savunma, hedefi doğrulayıcıların imzaladığı şeyin içine koymaktır.
İmzalanan baytların içinde ne var
Doğrulayıcılar mesajın kendisini imzalamaz. Sabit bir etiketin ve beş değerin özetini imzalar. Bu tablo AttestationLib.digest ve SolidusCredentialMirror.submit kodundan okunmuştur.
| Alan | Genişlik | Ayna bunu nereden alır |
|---|---|---|
Etiket SOLIDUS_BRIDGE_ATTESTATION_V1 |
29 bayt | Kütüphanedeki bir sabit |
| Solidus zincir kimliği | 8 bayt | Dağıtım sırasında aynada saklanır |
| Hedef alan | 4 bayt | Dağıtım sırasında aynada localDomain olarak saklanır |
| Sıra numarası | 8 bayt | Mesaj başlığı |
| Mesaj kimliği | 32 bayt | Mesajın tamamının keccak256 hash'i |
| Solidus yüksekliği | 8 bayt | Mesaj başlığı |
Değerler art arda paketlenir, toplam 89 bayt, ve keccak256 ile hash'lenir. Sonuç standart Ethereum imzalı mesaj önekiyle sarılır, yeniden hash'lenir ve imzalanır. Her imza 65 bayttır.
Mesaj kimliği gövdenin tamamını hash'ler; bu yüzden gövde imzaların altından değiştirilemez. 18 baytlık mesaj başlığında (sürüm, tür, sıra, yükseklik) hedef alanı yoktur. Hedef mesajın içinde değildir. Ayna kendi hedefini getirir.
Hedef neden önemli
Biri submit(message, signatures) çağırdığında ayna özeti kendi zincirinde, kendi localDomain değeriyle yeniden kurar. Sonra her imza üzerinde ecrecover çalıştırır. Bu işlev bir hash ve bir imza alır ve imzayı yapan adresi döndürür. Hash'i değiştirin, aynı imza farklı bir adres döndürür. Ayna, geri kazanılan her adresi kendi doğrulayıcı listesiyle karşılaştırır.
Dolayısıyla Fuji aynasına gösterilen bir Sepolia imzası bir Fuji özetine karşı denetlenir. Hiçbir doğrulayıcının sahip olmadığı bir adres çıkar ve çağrı UnknownSigner ile geri alınır.
2026-10-08 tarihinde, dört imzasıyla tek bir Sepolia mesajında (bir kalp atışı, sıra 1274) ölçüldü: Sepolia alanıyla yeniden kurulduğunda geri kazanılan dört adresin hepsi listelenen imzalayanlarla eşleşti. Fuji alanıyla ve bir kez de Arc alanıyla yeniden kurulduğunda, geri kazanılan sekiz adresin hiçbiri listelenen bir imzalayanla eşleşmedi.
Bağ gerçek bir iş yapar. Aynı gün Sepolia ve Fuji aynaları aynı dört doğrulayıcı adresini listeledi ve /v1/routes Sepolia, Fuji ve Arc için tek bir ayna adresi gösteriyor. Ne imzalayanlar ne sözleşme adresi zincirleri birbirinden ayırır. Ayıran, özetteki alandır.
Ayna, sırayı herhangi bir imzadan önce denetler. 2026-10-08 tarihinde saat 16:34 UTC'de Sepolia 1278'i, Fuji 1016'yı bekliyordu; bu yüzden kopyalanmış bir mesaj Fuji'de çoğunlukla önce OutOfSequence hatasında dururdu. Alan bağı, sayıların tesadüfen tuttuğu durumda koruyan şeydir.
Sıra ve yinelemeler
Sıkı sıra. Her ayna nextDomainSeq değerini tutar. Bir mesajı yalnızca mesajın sırası bu sayıya tam olarak eşitse uygular. Başka her şey OutOfSequence(expected, got) ile geri alınır. Bir mesaj uygulandıktan sonra sayaç bir artar. Bir mesaj iki kez uygulanamaz ve 42 numaralı mesaj 41 numaralıdan önce uygulanamaz. 41 bir iptal taşıyorsa hiçbir şey onu atlayamaz. Bir geri alma her yazmayı geri alır, bu yüzden reddedilen bir mesaj bir yuvayı tüketmez. Her hedef kendi sayısını tutar.
Artan imzalayan sırası. İmzalar tek bir bayt dizisi olarak gelir. Ayna, geri kazanılan her adresin bir öncekinden kesin olarak büyük olmasını ister, yoksa SignersOutOfOrder ile geri alır. Bir tekrar kendinden büyük olamaz; bu yüzden bir doğrulayıcı iki kez sayılamaz. Kod yorumu nedeni veriyor: tek bir anahtar aynı özet üzerinde birçok farklı geçerli imza dizisi üretebilir ve sıra kuralı bunların birçok imzalayan gibi geçmesini engeller; kurulacak bir liste ya da küme de gerekmez. Ayna ayrıca bir imzanın üst yarı biçimini (HighS), yani aynı imzanın ikinci bir yazılışını da reddeder. Daha fazla denetim güvenlik sayfasında.
Tek komutla görün
Mesaj listesinde zincire göre süzgeç yok. Birkaç tane isteyin ve bir Sepolia satırı seçin.
curl -sL 'https://bridge.solidus.network/v1/messages?limit=20' \
| jq -c '.messages[] | select(.chainKey=="sepolia") | {domain, domainSeq}'
Sonra o mesajın imzalarını isteyin. Rota Bridge API başvurusunda.
curl -sL https://bridge.solidus.network/v1/attestations/11155111/1274
2026-10-08 tarihli yanıttan kısaltılmıştır; bir imzalayan ve bir imza kısaltıldı:
{
"domain": 11155111,
"domainSeq": 1274,
"threshold": 3,
"signatures": [
{ "signer": "0x99a8...c272", "signature": "0x99bf...1c" }
]
}
Gerçek yanıt, imzalayan adresine göre artan sırada dört imza taşır. Şimdi özeti Foundry'nin cast aracıyla, bir kez Sepolia için bir kez Fuji için yeniden kurun ve ilk imzalayanın onu imzalayıp imzalamadığını sorun. 50002 sayısı, üç aynanın da solidusChainId() ile döndürdüğü Solidus test ağı zincir kimliğidir.
A=$(curl -sL https://bridge.solidus.network/v1/attestations/11155111/1274)
ID=$(echo "$A" | jq -r '.messageId[2:]')
HT=$(echo "$A" | jq -r '.solidusHeight')
SIGNER=$(echo "$A" | jq -r '.signatures[0].signer')
SIG=$(echo "$A" | jq -r '.signatures[0].signature')
TAG=$(printf SOLIDUS_BRIDGE_ATTESTATION_V1 | xxd -p -c 100)
for DEST in 11155111 43113; do
PRE=0x$TAG$(printf '%016x%08x%016x' 50002 $DEST 1274)$ID$(printf '%016x' $HT)
echo "destination $DEST"
cast wallet verify --address $SIGNER $(cast keccak $PRE) $SIG
done
Çıktı, adresler kısaltılmış olarak:
destination 11155111
Validation succeeded. Address 0x99a8...c272 signed this message.
destination 43113
Error: Validation failed. Address 0x99a8...c272 did not sign this message.
Aynı baytlar, aynı imza ve aynı imzalayan. Yalnızca hedef değişti.
Okumaya devam edin
- Kimlik belgesi durumu köprüsü nedir ve başka bir zincirdeki sözleşmenin neden ona ihtiyacı olur
- Bir akıllı sözleşme işlevini, arkasındaki veriyi görmeden bir kimlik belgesine nasıl bağlarsınız
- Doğrulayıcıların bir durum mesajını nasıl imzaladığı ve bir ağ geçidinin onu başka bir zincire nasıl ilettiği
- Ayna sözleşmesini Solidity, TypeScript ve komut satırından okuyun