Подача контента
Запись протокола никогда не несёт файл. Она несёт CID — самоописывающий хеш контента, хранящегося где-то ещё, — что держит квитанцию в 10 МБ вне полезной нагрузки gossip, которую каждый узел должен хранить и воспроизводить, при этом всё же позволяя арбитру установить, что изображение, на которое он смотрит, — то, что сторона подписала.
Кто-то всё же должен держать байты. Этот кто-то — ваш узел, и он уже это делает: подача контента включена по умолчанию и не требует конфигурации.
Почему это в узле, а не в демоне
Первая версия этого закрепляла (pin) через отдельный демон
Kubo, достигаемый по --ipfs-api-url. Это
работало и стоило больше, чем казалось: вторая идентичность пира в сети, среда
исполнения Go и её резидентная память рядом с узловой, и неаутентифицированная
поверхность управления /api/v0 на порту 5001, которая позволяет любому, кто её
достигнет, закреплять, откреплять и читать всё, что демон держит, — смягчаемая
лишь привязкой к loopback.
Более глубокая проблема была в умолчании. Запуск демона — это работа, поэтому закрепление было opt-in, поэтому почти никто бы его не включил, — а гарантия долговечности, в которую никто не входит, — не гарантия. Узел теперь говорит bitswap в процессе, на той же идентичности libp2p, с которой он уже делает gossip, что и позволяет поведению быть включённым по умолчанию. Это, в свою очередь, и делает премию вознаграждения измеряющей нечто реальное: когда подают все, множитель отделяет узлы, которые действительно держат и отвечают за контент, от узлов, которые офлайн или обрезали, а не операторов, потрудившихся установить Go, от тех, кто не потрудился.
Что ваш узел держит, и что нет
Он держит контент, на который ссылаются записи вложений, которые он принял, в пределах своего окна хранения. Он не извлекает каждый CID, который видит, — узел, который бы это делал, хранил бы что угодно, на что кто-либо решил бы его направить.
Эта граница не обещание, это арифметика: вложение должно называть расчёт, а расчёт требует реального резервирования против реального эскроу. Потолок того, что запрашивается у вашего диска, — это фактический объём торговли сети, а не терпение незнакомца.
Всё извлечённое проверяется против его CID перед тем, как быть сохранённым, пришло ли оно от пира или от шлюза.
Спросите свой узел, что он держит:
curl -s -X POST http://localhost:7080/rpc -H 'content-type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"getHeldContent","params":{"cid":"bafkrei..."}}'
# {"jsonrpc":"2.0","id":1,"result":{"content":"<base64>"}} ← держится
# {"jsonrpc":"2.0","id":1,"result":{"content":null}} ← не держится
Откуда берётся первая копия
Bitswap двигает блоки между пирами, у которых они уже есть; он не создаёт первый. Контент входит в сеть через тот сервис закрепления, в который интерфейс его загрузил, поэтому первый узел, желающий CID, должен извлечь его из более широкой сети IPFS, проверить байты против CID и подавать его своим пирам с тех пор.
--content-gateway https://ipfs.example.com # по умолчанию: https://ipfs.filebase.io
Шлюз — это недоверенный транспорт, не власть. Он может подать неверные байты, никакие байты или залогировать, кто что запросил; он не может изменить, какой контент называет CID, потому что CID — это хеш того контента. Байты, которые не то, что CID называет, отклоняются, а шлюз, который что-либо подменяет, неотличим от просто лежащего.
Приватность — единственное, что проверка не исправляет: кто бы ни управлял шлюзом, он узнаёт, что ваш узел запросил этот CID. Вот почему флаг существует — направьте его на свой собственный — и почему это запасной вариант, а не первый выбор. Ваш узел предпочитает своих пиров.
Если вы уже запускаете Kubo
--ipfs-api-url http://127.0.0.1:5001 всё ещё работает и теперь значит нечто более
узкое, чем раньше: контент протокола закрепляется в вашем демоне тоже, помещая
копию куда-то, что собственное окно хранения вашего узла не управляет. Это больше
не то, как узел подаёт контент.
Отключение, и что это стоит
openfiat-node --no-content-serving
Этот узел ничего не хранит и не может ответить на вызов извлекаемости, поэтому зарабатывает сниженную долю. Это честный исход — он делает для сети меньше — и это правильный флаг, если диска действительно нет. Узел всё же бросает вызов своим пирам в любом случае: измерение, кто подаёт контент, — сервис, который узел выполняет, хранит ли он что-либо сам или нет.
Как работает вызов
Узел выбирает CID, который сеть знает, просит у другого узла байты и хеширует то, что возвращается. Адрес контента есть хеш его контента, поэтому вернуть правильные байты — не то, что узел может сделать, не имея их. Это единственный сигнал качества узла, который проверяется, а не принимается на веру.
Только некоторые CID могут решить вопрос. Дайджест CID с кодеком raw берётся по самому файлу; CID dag-pb адресует корень нарезанного DAG, поэтому пир мог бы вернуть правильный файл и всё же провалить наивную проверку хеша. Провайдеры переключаются между двумя на 262 144 байтах, поэтому вызовы отбирают файлы на или ниже 256 КиБ — аватары почти всегда, вложения иногда. Этого достаточно, чтобы отделить узел, закрепляющий всё, от того, что не закрепляет ничего, что и нужно множителю; это не доказательство того, что узел держит конкретное большое вложение, и ничто не утверждает, что это так.
Что такое множитель
Узел, который отвечает, сохраняет свою полную долю; узел, который не может,
масштабируется до 0.7, поэтому подающий узел зарабатывает примерно 1.43× того,
что зарабатывает в остальном идентичный узел, который не подаёт. Обе цифры —
[PROPOSED — NEEDS SIGN-OFF] — см. OFS-4100 §9.2 и crates/rewards/src/params.rs.
Премия выражена как штраф по причине, которая не презентационна. Эмиссия за эпоху фиксирована, и эти множители решают, как она делится. Бонус выше 1.0 не заплатил бы закрепляющему узлу из воздуха, он отчеканил бы эмиссию, которой в ведре инфраструктуры нет, — что параметры вознаграждения прямо отвергают. Поэтому закрепляющий узел сохраняет свою полную долю, а незакрепляющий узел уступает часть своей.
0.7 вместо 0.4 у только-gossip, потому что хранение — меньшая услуга сети, чем соединение с цепочкой: узел, который не закрепляет, всё же ретранслирует, проверяет и подаёт всё остальное.
Как долго хранится контент
Не каждый узел должен нести всю историю.
--retention 30 # по умолчанию: скользящее окно в 30 дней
--retention 365 # более длинное окно, всё ещё скользящее
--retention archival # хранить всё, навсегда — явный выбор
30 дней — это также нижний порог, который каждый узел должен сети, поэтому меньшие значения отклоняются, а не тихо поднимаются, — узел, настроенный на семь дней, который тихо проработал бы тридцать, делал бы нечто иное, чем просил его оператор.
Этот порог и есть то, что позволяет вытеснению и вознаграждениям сосуществовать. Вызовы всегда черпаются только из контента внутри него, поэтому скользящий узел, который правильно вытеснил прошлогодние доказательства, никогда о них не спрашивается и никогда не теряет свою долю за то, что поступил правильно. Равным образом ни один узел не может сократить то, о чём его могут спросить, объявив меньшее окно.