---
title: 'Yazılım Malzeme Listesi'
source: 'https://academia.sh/tr/kurslar/ci-cd/yazilim-malzeme-listesi'
course: 'Sürekli Tümleştirme ve Teslimat'
language: tr
updated: '2026-08-17T18:09:03+00:00'
license: 'CC BY-SA 4.0'
---

# Yazılım Malzeme Listesi

Yapı çıktısının içindeki bileşenler bir envanter olarak üretilir ve üç sayıyla ölçülür: envanterin gördüğü doğrudan, geçişli ve derleme zamanı bileşenleri, hiç göremediği üç sınıf, ve bir kusur duyurusu geldiğinde yanıtın kaç sorguya indiği.

Önceki ders sırrın depoda şifreli durabildiğini ölçtü: anahtar bir yerde, çözebilen özne sayılı,
döndürme belli sayıda kaydı yeniden yazıyor. Depoya giren yapılandırma artık bildirilmiştir — ama
bildirilen yalnız yapılandırmadır. Hattın ürettiği çıktının **içinde ne olduğu** hâlâ hiçbir yerde
yazmaz.

Bu ders o beyanı üretir. Ölçü üç sayıdır: envanter kaç bileşeni görüyor, kaçını hiç göremiyor,
bir kusur duyurusu geldiğinde yanıt kaç sorguya iniyor. M22/K02 taramanın yakaladığı ve kaçırdığı
kusurları saymıştı; o sayım tekrarlanmaz. Buradaki nesne envanterin kendisidir — **hattın ürettiği
bir çıktı** olarak.

**DD19.** Kurgu bölgesel ölçüm ağı dört servistir ve 64 bileşenlik bir havuzu paylaşır; ağaç `node`
ile diske yazılır ve bir modeldir. **DD20.** Envanter üreteci yalnız beyan dosyalarını okur:
bildirim, çözüm ve derleme. **DD21.** Beyan edilmeyen üç yol vardır — kaynağa kopyalanmış, ikiliye
gömülü, koşum anında inen; üretecin tohumu `20260214` görünürdür.

## Envanteri Üretmek

```js
// olcum-agi/envanter.mjs — kurgu bagimlilik agaci diske yazilir, envanter uretilir
import { mkdirSync, rmSync, writeFileSync, readFileSync, statSync } from "node:fs";
import { join, dirname } from "node:path";

const KOK = join(process.env.TMPDIR ?? "/tmp", "olcum-agi-envanter");
rmSync(KOK, { recursive: true, force: true });
const uretec = (tohum) => () =>
  (tohum = (tohum * 1103515245 + 12345) % 2147483648) / 2147483648;
const r = uretec(20260214);                                   // tohum gorunur
const sec = (d) => d[Math.floor(r() * d.length)];

// Kurgu bolgesel olcum agi: dort servis. Havuz paylasilir, ayni bilesen birden cok serviste gecer.
const HAVUZ = Array.from({ length: 64 }, (_, i) => `bilesen-${String(i + 1).padStart(2, "0")}`);
const SURUM = ["1.4", "2.0", "2.3", "2.6", "3.1", "3.4"];
const SERVIS = [                        // kopya/gomulu/kosum: beyan edilmeyen bilesenler
  { ad: "toplayici", dogrudan: 9, arac: 5, kopya: 2, gomulu: 1, kosum: 1, ickopya: 1 },
  { ad: "dogrulayici", dogrudan: 7, arac: 4, kopya: 1, gomulu: 1, kosum: 0, ickopya: 1 },
  { ad: "faturalama", dogrudan: 12, arac: 6, kopya: 2, gomulu: 2, kosum: 2, ickopya: 1 },
  { ad: "is-emri", dogrudan: 6, arac: 4, kopya: 1, gomulu: 0, kosum: 1, ickopya: 0 },
];
const yaz = (yol, icerik) => {
  mkdirSync(dirname(yol), { recursive: true });
  writeFileSync(yol, icerik);
};

for (const s of SERVIS) {
  const alinan = new Set();
  const yeni = () => { let a; do { a = sec(HAVUZ); } while (alinan.has(a)); alinan.add(a); return a; };
  const dogrudan = {}, cozum = {}, araclar = {};
  for (let i = 0; i < s.dogrudan; i++) {
    const ad = yeni();
    cozum[ad] = { surum: (dogrudan[ad] = sec(SURUM)), gerektiren: "" };
    for (let k = 0, n = Math.floor(r() * 4); k < n; k++)       // gecisli cocuklar
      cozum[yeni()] = { surum: sec(SURUM), gerektiren: ad };
  }
  for (let i = 0; i < s.arac; i++) araclar[yeni()] = sec(SURUM);
  yaz(join(KOK, s.ad, "bildirim.json"), JSON.stringify({ servis: s.ad, dogrudan }, null, 1));
  yaz(join(KOK, s.ad, "cozum.json"), JSON.stringify(cozum, null, 1));
  yaz(join(KOK, s.ad, "derleme.json"), JSON.stringify({ araclar }, null, 1));

  for (let i = 0; i < s.kopya; i++)                            // kaynaga kopyalanmis ucuncu taraf
    yaz(join(KOK, s.ad, "kaynak", `k${i}.js`), `// kopyalanmis: ${yeni()}@${sec(SURUM)}\n`);
  for (let i = 0; i < s.ickopya; i++)                          // kendi kodu, ucuncu taraf degil
    yaz(join(KOK, s.ad, "kaynak", `i${i}.js`), `// kopyalanmis: ic-kitaplik-olcum@1.0\n`);
  for (let i = 0; i < s.gomulu; i++)                           // ikilinin icine gomulu
    yaz(join(KOK, s.ad, "cikti", "gomulu", `g${i}.bin`), Buffer.concat(
      [Buffer.from(`GOMULU:${yeni()}@${sec(SURUM)}\n`), Buffer.alloc(2048, "x")]));
  const inen = Array.from({ length: s.kosum }, () => `${yeni()}@${sec(SURUM)}`);
  yaz(join(KOK, s.ad, "cikti", "indirilenler.json"), JSON.stringify({ inen }, null, 1));
  yaz(join(KOK, s.ad, "cikti", "govde.js"), `// ${s.ad} yapi ciktisi\n`);
}

// Envanter ureteci YALNIZ beyan dosyalarini okur; okunan dosya ve bayt sayilir.
let dosya = 0, bayt = 0;
const oku = (y) => { dosya++; bayt += statSync(y).size; return JSON.parse(readFileSync(y)); };
const envanter = [];
for (const s of SERVIS) {
  const bildirim = oku(join(KOK, s.ad, "bildirim.json")).dogrudan;
  const cozum = oku(join(KOK, s.ad, "cozum.json"));
  for (const [ad, k] of Object.entries(cozum))
    envanter.push({ servis: s.ad, ad, surum: k.surum,
      tur: ad in bildirim ? "dogrudan" : "gecisli", gerektiren: k.gerektiren });
  for (const [ad, sur] of Object.entries(oku(join(KOK, s.ad, "derleme.json")).araclar))
    envanter.push({ servis: s.ad, ad, surum: sur, tur: "derleme", gerektiren: "(derleme)" });
}
const yolEnv = join(KOK, "envanter.json");
yaz(yolEnv, JSON.stringify(envanter, null, 1));
yaz(join(KOK, "maliyet.json"), JSON.stringify({ dosya, bayt }));

const say = (t) => envanter.filter((k) => k.tur === t).length;
console.log(`tohum 20260214; servis ${SERVIS.length}, havuz ${HAVUZ.length} bilesen`);
console.log(`envanteri uretmek icin okunan beyan dosyasi ${dosya}, ${bayt} bayt`);
console.log(`envanter kaydi ${envanter.length} -> dogrudan ${say("dogrudan")}, ` +
  `gecisli ${say("gecisli")}, derleme zamani ${say("derleme")}`);
console.log(`envanter dosyasi ${statSync(yolEnv).size} bayt, ` +
  `benzersiz bilesen ${new Set(envanter.map((k) => k.ad)).size}`);
```

```
tohum 20260214; servis 4, havuz 64 bilesen
envanteri uretmek icin okunan beyan dosyasi 12, 7124 bayt
envanter kaydi 107 -> dogrudan 34, gecisli 54, derleme zamani 19
envanter dosyasi 12659 bayt, benzersiz bilesen 56
```

Dört servis toplam **34 doğrudan bağımlılık** bildirir. Envanter 107 kayıt üretir: o 34'ün yanına
**54 geçişli** bileşen ve **19 derleme zamanı aracı** gelir. Beyan edilenle taşınan arasındaki
oran buradadır — ekip 34 ad yazar, çıktıya 88 bileşen girer. Yalnız doğrudan bağımlılıklara bakan
bir kapı, beyan edilen 88 koşum bileşeninin **34'ünü** (%39) görür.

Üretimin maliyeti ortamdan bağımsız niceliklerle yazılır: **12 beyan dosyası, 7.124 bayt okuma,
107 kayıt, 12.659 baytlık envanter.** Süre basılmaz: süre makineye bağlıdır, okunan dosya sayısı
değildir.

107 kaydın arkasında **56 benzersiz bileşen** vardır: aynı bileşen birden çok serviste geçer.
"Hangi servisler etkileniyor" sorusunun tek bir dosyadan yanıtlanabilmesi buna dayanır.

## Envanterin Görmediği

Üreteç beyan dosyalarını okur. Beyan dosyası **niyeti** anlatır: neyin çekilmesi istendiğini. Çıktı
ağacı ise **sonucu** taşır. İkisi aynı olmak zorunda değildir.

**DD22.** Derleme zamanı araçları üretimde koşmaz; koşan bileşen kümesine girmez. **DD23.** Denetçi
ağacı gezer ve `kopyalanmis:` ya da `GOMULU:` başlığı taşıyan her dosyayı beyan dışı sayar; kendi
kodunun kopyası da aynı başlığı taşır.

```js
// olcum-agi/kapsam.mjs — envanterin gormedigi: cikti agaci gezilir, beyanla karsilastirilir
import { readdirSync, readFileSync, writeFileSync, statSync } from "node:fs";
import { join } from "node:path";

const KOK = join(process.env.TMPDIR ?? "/tmp", "olcum-agi-envanter");
const envanter = JSON.parse(readFileSync(join(KOK, "envanter.json")));
const maliyet = JSON.parse(readFileSync(join(KOK, "maliyet.json")));   // ureticin okudugu
const SERVIS = ["toplayici", "dogrulayici", "faturalama", "is-emri"];

const dolas = (kok, alt = "") => readdirSync(join(kok, alt), { withFileTypes: true })
  .flatMap((g) => g.isDirectory() ? dolas(kok, join(alt, g.name)) : [join(alt, g.name)]);

// Denetci beyan dosyalarini degil, agacin kendisini okur.
let gezilenDosya = 0, gezilenBayt = 0;
const bulgu = [];
for (const s of SERVIS) {
  for (const yol of dolas(join(KOK, s))) {
    if (yol.endsWith(".json")) continue;                       // beyan dosyalari denetcinin girdisi degil
    const tam = join(KOK, s, yol);
    gezilenDosya++; gezilenBayt += statSync(tam).size;
    const bas = readFileSync(tam).subarray(0, 120).toString("latin1");
    const k = bas.match(/kopyalanmis: (\S+)@(\S+)/);
    if (k) bulgu.push({ servis: s, ad: k[1], surum: k[2], nasil: "kaynaga kopyalanmis" });
    const g = bas.match(/GOMULU:(\S+)@(\S+)/);
    if (g) bulgu.push({ servis: s, ad: g[1], surum: g[2], nasil: "ikiliye gomulu" });
  }
  const { inen } = JSON.parse(readFileSync(join(KOK, s, "cikti", "indirilenler.json")));
  for (const kayit of inen) {
    const [ad, surum] = kayit.split("@");
    bulgu.push({ servis: s, ad, surum, nasil: "kosum aninda inen" });
  }
}

// Beyan edilmis kosum bileseni: derleme araclari uretimde kosmaz, kosum kumesine girmez.
const beyanli = new Set(envanter.filter((k) => k.tur !== "derleme").map((k) => `${k.servis}|${k.ad}`));
const ucuncuTaraf = (ad) => ad.startsWith("bilesen-");
const beyansiz = bulgu.filter((b) => ucuncuTaraf(b.ad) && !beyanli.has(`${b.servis}|${b.ad}`));
const yanlisTutulan = bulgu.filter((b) => !ucuncuTaraf(b.ad));

const say = (nasil) => beyansiz.filter((b) => b.nasil === nasil).length;
const kosanBeyanli = envanter.filter((k) => k.tur !== "derleme").length;
const kosanToplam = kosanBeyanli + beyansiz.length;

console.log(`envanterin gordugu kosum bileseni ${kosanBeyanli}, ` +
  `derleme zamani ${envanter.length - kosanBeyanli}`);
console.log(`envanterin gormedigi ${beyansiz.length} -> ` +
  `kaynaga kopyalanmis ${say("kaynaga kopyalanmis")}, ` +
  `ikiliye gomulu ${say("ikiliye gomulu")}, kosum aninda inen ${say("kosum aninda inen")}`);
console.log(`kapsam %${(100 * kosanBeyanli / kosanToplam).toFixed(0)} ` +
  `(${kosanBeyanli}/${kosanToplam} kosan bilesen)`);

const SUTUN = [28, 22, 10, 16, 0];
const satir = (s) => console.log(s.map((v, i) => String(v).padEnd(SUTUN[i])).join(""));
console.log("");
satir(["kapi", "okudugu", "tuttugu", "yalniz kendisi", "yanlis tuttugu"]);
satir(["beyan dosyalarini okur", `${maliyet.dosya} dosya / ${maliyet.bayt} bayt`, 0, 0, 0]);
satir(["cikti agacini gezer", `${gezilenDosya} dosya / ${gezilenBayt} bayt`,
  beyansiz.length, beyansiz.length, yanlisTutulan.length]);

console.log(`yanlis tutulan ${yanlisTutulan.length} kayit: ` +
  `${yanlisTutulan.map((b) => b.servis).join(", ")} -> ${yanlisTutulan[0].ad}`);

// Gercek kosan kume sonraki bloga birakilir: beyanli + beyansiz.
writeFileSync(join(KOK, "gercek.json"), JSON.stringify([
  ...envanter.filter((k) => k.tur !== "derleme")
    .map(({ servis, ad, surum }) => ({ servis, ad, surum, gorunur: true })),
  ...beyansiz.map(({ servis, ad, surum }) => ({ servis, ad, surum, gorunur: false })),
]));
```

```
envanterin gordugu kosum bileseni 88, derleme zamani 19
envanterin gormedigi 14 -> kaynaga kopyalanmis 6, ikiliye gomulu 4, kosum aninda inen 4
kapsam %86 (88/102 kosan bilesen)

kapi                        okudugu               tuttugu   yalniz kendisi  yanlis tuttugu
beyan dosyalarini okur      12 dosya / 7124 bayt  0         0               0
cikti agacini gezer         17 dosya / 8685 bayt  14        14              3
yanlis tutulan 3 kayit: toplayici, dogrulayici, faturalama -> ic-kitaplik-olcum
```

Koşan 102 bileşenin 88'i envanterde görünür; **14'ü görünmez** — 6'sı kaynağa kopyalanmış, 4'ü
ikiliye gömülü, 4'ü koşum anında inen. **Kapsam %86'dır.** Eksik %14 ölçünün en pahalı parçasıdır,
çünkü envanter bu bileşenleri "yok" diye değil, hiç göstermeyerek raporlar.

Tablo iki kapıyı yan yana koyar. Beyan dosyalarını okuyan kapı beyan dışı bileşenlerin
**sıfırını** tutar; tutamaz, çünkü baktığı yer beyanın kendisidir. **Kapı olarak marjinal değeri
sıfırdır** ve kapı olarak durması gerekmez: envanteri üretmesi yeterlidir, geçişe izin verme
yetkisi ona hiçbir şey kazandırmaz. **Kaldırılması daha iyi olan kapı budur.**

Çıktı ağacını gezen kapı 14'ün 14'ünü tutar ve **hepsini yalnız kendisi tutar** — marjinal değeri
tuttuğunun tamamıdır. Okuma bedeli küçüktür: 12 dosya ve 7.124 bayta karşı 17 dosya ve 8.685
bayt, farklı bir okuma, daha büyük bir okuma değil. **Asıl bedel yanlış tutmadır:** 17 bulgunun
3'ü kendi kodunun kopyasıdır. Her altı bulgudan biri boşuna bir incelemedir ve bu oranın ürettiği
davranış bilinir — liste bir kez toptan "bilinen" diye işaretlenir, sonraki gerçek bulgu da aynı
işaretin altında kalır.

## Duyuru Geldiğinde

Envanterin değeri üretildiği anda değil, sorulduğu anda ölçülür. Soru hep aynı biçimdedir: şu
bileşenin şu sürümünün altındakiler açık — bizde var mı, nerede?

**DD24.** Kusur duyurusu bir bileşen adı ve bir eşik sürümüdür; eşiğin altındaki sürümler açık
sayılır. Duyuru çekilişinin tohumu `884301` görünürdür.

```js
// olcum-agi/duyuru.mjs — kusur duyurusu geldiginde yanit: envanterli ve envantersiz yol
import { readFileSync } from "node:fs";
import { join } from "node:path";

const KOK = join(process.env.TMPDIR ?? "/tmp", "olcum-agi-envanter");
const SERVIS = ["toplayici", "dogrulayici", "faturalama", "is-emri"];
const SURUM = ["1.4", "2.0", "2.3", "2.6", "3.1", "3.4"];
const oku = (ad) => JSON.parse(readFileSync(join(KOK, ad)));
const envanter = oku("envanter.json"), maliyet = oku("maliyet.json");
const gercek = oku("gercek.json");                 // onceki blogun denetimi: beyanli + beyansiz

const uretec = (tohum) => () =>
  (tohum = (tohum * 1103515245 + 12345) % 2147483648) / 2147483648;
const r = uretec(884301);                          // duyuru cekilisinin tohumu gorunur
const ADLAR = [...new Set(gercek.map((k) => k.ad))].sort();
const DUYURU = Array.from({ length: 9 }, () => ({
  ad: ADLAR[Math.floor(r() * ADLAR.length)],
  esik: SURUM[1 + Math.floor(r() * (SURUM.length - 1))],       // bu surumun altindakiler acik
}));
const etkilenen = (kume, d) => new Set(kume
  .filter((k) => k.ad === d.ad && SURUM.indexOf(k.surum) < SURUM.indexOf(d.esik))
  .map((k) => k.servis));

console.log(`gercek kosan kayit ${gercek.length} ` +
  `(gorunmeyen ${gercek.filter((k) => !k.gorunur).length}), duyuru ${DUYURU.length}; tohum 884301`);
console.log("");
console.log("duyuru".padEnd(24) + "envanter yaniti".padEnd(17) + "gercek".padEnd(9) + "durum");

let dogru = 0, eksik = 0, etkiliDuyuru = 0;
for (const d of DUYURU) {
  const yanit = etkilenen(gercek.filter((k) => k.gorunur), d), ger = etkilenen(gercek, d);
  if (ger.size) etkiliDuyuru++;
  yanit.size === ger.size ? dogru++ : eksik++;
  console.log(`${d.ad} < ${d.esik}`.padEnd(24) + `${yanit.size} servis`.padEnd(17) +
    `${ger.size} servis`.padEnd(9) + (yanit.size === ger.size ? "tam" : "eksik"));
}

const BEYAN = SERVIS.length * 3;                   // depo basina bildirim, cozum, derleme
console.log("");
console.log("yol".padEnd(30) + "duyuru basina".padEnd(26) + `${DUYURU.length} duyuru icin`);
console.log("envanter dosyasina sorgu".padEnd(30) + `1 sorgu / ${envanter.length} kayit`.padEnd(26) +
  `1 uretim (${maliyet.dosya} dosya) + ${DUYURU.length} sorgu`);
console.log("depolari tek tek acmak".padEnd(30) + `${SERVIS.length} depo / ${BEYAN} dosya`.padEnd(26) +
  `${DUYURU.length * SERVIS.length} depo acilisi, ${DUYURU.length * BEYAN} dosya`);
console.log("");
console.log(`etkilenen servisi olan duyuru ${etkiliDuyuru}/${DUYURU.length}; ` +
  `envanterin tam yanitladigi ${dogru}, eksik yanitladigi ${eksik}`);
```

```
gercek kosan kayit 102 (gorunmeyen 14), duyuru 9; tohum 884301

duyuru                  envanter yaniti  gercek   durum
bilesen-47 < 2.6        0 servis         1 servis eksik
bilesen-49 < 3.4        0 servis         1 servis eksik
bilesen-59 < 2.3        0 servis         0 servis tam
bilesen-41 < 2.0        0 servis         0 servis tam
bilesen-12 < 2.0        0 servis         0 servis tam
bilesen-12 < 2.3        0 servis         0 servis tam
bilesen-05 < 3.1        1 servis         1 servis tam
bilesen-58 < 2.3        0 servis         0 servis tam
bilesen-15 < 2.6        1 servis         2 servis eksik

yol                           duyuru basina             9 duyuru icin
envanter dosyasina sorgu      1 sorgu / 107 kayit       1 uretim (12 dosya) + 9 sorgu
depolari tek tek acmak        4 depo / 12 dosya         36 depo acilisi, 108 dosya

etkilenen servisi olan duyuru 4/9; envanterin tam yanitladigi 6, eksik yanitladigi 3
```

Dokuz duyurunun **beşi hiçbir servisi etkilemez.** En sık verilen yanıt "bizde yok"tur ve
envanterin değeri öncelikle buradadır: o yanıt tek sorguda gelir. Envantersiz yol aynı yanıta
ulaşmak için her duyuruda **dört depoyu açar, on iki beyan dosyası okur** — dokuz duyuru için 36
depo açılışı ve 108 dosya.

Bu, kapının eklediği sürenin yönünü belirler. Envanter üretimi hatta bir aşama ekler ve o aşama
**her değişiklikte** 12 dosya okur; buna karşılık yanıt **duyuru başına** dört depo açılışından bir
sorguya düşer. **Kapının bedeli değişiklik başına, getirisi duyuru başınadır**; oranı duyuru
sıklığı belirler, kapının kendisi değil.

Son satır kapsamın bedelidir. Envanter 9 duyurunun **6'sını tam, 3'ünü eksik** yanıtlar. İkisinde
yanıt "0 servis", gerçek "1 servis"tir; duyuru ilgisiz diye kapatılır. Üçüncüsü daha kötüdür:
`bilesen-15` için envanter "1 servis" der, gerçek "2 servis"tir. Sıfır bir yanıt bazen sorgulanır,
**bir sayı sorgulanmaz** — iş yapılmış görünür. Görünmeyen 14 bileşenin bedeli budur ve çıktı
ağacını gezen kapı kaldırıldığında geri gelir.

## Özet

- Dört servis 34 doğrudan bağımlılık bildirir; envanter 107 kayıt üretir — 34 doğrudan, 54
  geçişli, 19 derleme zamanı aracı. Yalnız doğrudan bağımlılığa bakan bir kapı beyan edilen 88
  koşum bileşeninin 34'ünü görür.
- Üretim maliyeti ortamdan bağımsız yazılır: 12 beyan dosyası, 7.124 bayt okuma, 107 kayıt,
  12.659 baytlık envanter, 56 benzersiz bileşen.
- Koşan 102 bileşenin 88'i envanterde görünür, 14'ü görünmez: 6 kaynağa kopyalanmış, 4 ikiliye
  gömülü, 4 koşum anında inen. Kapsam %86'dır.
- Beyan dosyalarını okuyan kapı beyan dışı bileşenin sıfırını tutar ve kapı olarak kaldırılabilir;
  çıktı ağacını gezen kapı 14'ünü tutar, hepsini yalnız kendisi tutar, bedeli 17 bulgunun 3'ünü
  yanlış tutmasıdır.
- Yanıt duyuru başına 4 depo açılışından 1 sorguya iner: 9 duyuru için 36 depo açılışına karşı
  bir üretim ve 9 sorgu. Envanter 6 duyuruyu tam, 3'ünü eksik yanıtlar; eksiklerden biri "0"
  değil "1" der.

## Sonraki Adım

Envanter artık var, kapsamı sayılı ve sorgulanabilir. Ama 12.659 baytlık bu dosya ile tarif ettiği
çıktı arasında **hiçbir bağ yoktur.** Envanter ayrı bir dosyadır: yanına başka bir envanter
konabilir, içindeki bir kayıt değiştirilebilir, bambaşka bir yapı çıktısı aynı envanterle
eşleştirilebilir. 107 kaydın hiçbiri, tarif ettiği baytların gerçekten o baytlar olduğunu söylemez.

Aynı boşluk zincirin öteki halkalarında da vardır: kaynak işlemesi ile yapı çıktısı arasında, yapı
çıktısı ile dağıtılan şey arasında. Bir sonraki ders bu halkaları gerçek bir anahtar çiftiyle
bağlar ve iki soruyu sayıya çevirir: doğrulama zincirin hangi noktasında yapılırsa kaç sahte çıktı
geçer, ve o anahtarı elinde tutan kaç özne vardır?
