Marzec 2024 roku na zawsze zmienił ekonomię warstwy drugiej. Przed aktualizacją Dencun rollupy pokroju Arbitrum czy Optimism paliły budżety zespołów na ładowanie calldata w sieci głównej. Smart kontrakty Ethereum zapisywały tę tablicę bajtów w stanie globalnym na zawsze, zmuszając każdego walidatora do przechowywania jej wiecznie. Pochłaniało to nawet dziewięćdziesiąt procent kosztów użytkowników L2.
EIP-4844 rozbił ten schemat, wprowadzając transakcje trzeciego typu i bloby. Jednak za ładnymi cyframi obniżki opłat rzędu 10-50 razy kryje się mnóstwo niuansów inżynieryjnych, o których marketerzy zazwyczaj wolą nie wspominać.
Jak działa blob na poziomie pamięci i konsensusu
Blob to nie zwykły plik wysłany do blockchaina. To izolowana tablica danych o dokładnie 131 072 bajtach (128 KB), która istnieje równolegle z egzekucją EVM. Smart kontrakty w Ethereum nie potrafią odczytać zawartości bloba wewnątrz wywołania CALL lub STATICCALL. Mają dostęp tylko do jednego kodu bajtowego - specjalnego opkodu BLOBHASH, który zwraca wersję oraz hash zobowiązania.
- Rozmiar bloku: docelowa wartość wynosi 3 bloby (~384 KB), a maksimum w szczycie to 6 blobów (~768 KB).
- Czas życia: około 4096 epok (w przybliżeniu 18 dni), po czym węzły konsensusu czyszczą dane ze swoich dysków.
- Izolacja mempoola: bloby nie kręcą się w zwykłym mempoolu wykonawczym. Posiadają własną pulę na poziomie klientów konsensusu (P2P blob pool), co chroni główne kolejki transakcji przed przeciążeniem.
Parametr | Klasyczny calldata | Blob według EIP-4844 (Type 3)
Gdzie leżą dane: W stałym stanie EVM | W tymczasowej puli P2P konsensusu
Czas przechowywania: Na zawsze | ~18 dni
Dostęp z EVM: Bezpośredni (przez odczyt bajtów) | Pośredni (tylko przez hash i KZG)
Wycena: Wspólny execution gas | Niezależny rynek blob gas (EIP-1559)
Nieoczywiste funkcje i pułapki architektoniczne proto-dankshardingu
Mało kto zwraca uwagę na to, jak działa wycena blob gasu. W przeciwieństwie do standardowego gazu, który skacze przy każdym kichnięciu w DeFi, rynek blobów jest sterowany przez niezależny algorytm korekty. Jeśli rollupy zapychają sloty powyżej docelowych trzech blobów, cena zaczyna rosnąć wykładniczo. Jeśli sloty świecą pustkami, spada do minimalnego progu. W praktyce prowadzi to do sytuacji, w której wysłanie bloba w spokojnych godzinach kosztuje mikroskopijne ułamki centa, ale w momencie nagłego szturmu na sieć cena może podskoczyć setki razy szybciej niż w warstwie wykonawczej.
Kolejna delikatna kwestia to weryfikacja kryptograficzna poprzez zobowiązania KZG (Kate-Zaverucha-Goldberg). Walidatorzy nie muszą pobierać całych 128 KB, aby upewnić się co do poprawności danych. Wystarczy, że zweryfikują 48-bajtowy wielomian. Obniża to wymagania wobec procesorów walidatorów, ale przerzuca całe obciążenie na łącze sieciowe.
Gdzie zrzucać "niewygodne" info w praktyce
Tymczasowa natura blobów czyni z nich idealny kontener dla danych, które nie potrzebują wiecznej księgowości, lecz wymagają kryptograficznej niezmienności w krótkim interwale walidacji:
- Zrzuty wyroczni i surowe metryki: pośrednie tablice danych, które są podpisywane przez dostawców przed agregacją do smart kontraktu.
- Migawki stanu L3 i światów gier: zmiany stanu typu delta, którym wystarczy potwierdzić poprawność za ostatni tydzień.
- Logi audytowe backendu: pakiety transakcji lub rachunki, które muszą być weryfikowalne publicznie, lecz po miesiącu tracą wartość operacyjną.
Wrzucanie ładunku do bloba poprzez skrypty Pythona w web3.py wymaga osobnego pakowania bajtów w wielomiany i generowania hasha wersjonowanego z prefiksem 0x01. Błędne obliczenie maxFeePerBlobGas w momencie wysyłki gwarantuje zawieszenie transakcji w mempoolu.
Teoria i ekonomia są jasne, ale jak wygląda praca z blobami na poziomie kodu? Użycie standardowego wywołania send_transaction zakończy się błędem, ponieważ transakcje trzeciego typu wymagają ręcznego przygotowania zobowiązań KZG, wygenerowania dowodów (proofs) oraz obliczenia osobnego limitu gazu.
Wysyłanie transakcji Type-3 w Pythonie
Poniżej zaprezentowano działający wzorzec wysyłania bloba przy użyciu aktualnych bibliotek do obsługi węzła (Geth lub Reth), który wspiera aktualizację Dencun:
import os
from eth_account import Account
from web3 import Web3
w3 = Web3(Web3.HTTPProvider("http://127.0.0.1:8545"))
key = os.environ["OPERATOR_KEY"]
acct = Account.from_key(key)
FE_COUNT, FE_PAYLOAD = 4096, 31 # 31 bajtów danych na element pola
def encode_blob(data: bytes) -> bytes:
data = len(data).to_bytes(4, "big") + data
if len(data) > FE_COUNT * FE_PAYLOAD:
raise ValueError("payload nie mieszcz się w jednym blobie")
data = data.ljust(FE_COUNT * FE_PAYLOAD, b"\x00")
# starszy bajt każdego elementu = 0, aby wartość < modułu BLS
return b"".join(b"\x00" + data[i:i + FE_PAYLOAD]
for i in range(0, len(data), FE_PAYLOAD))
blob = encode_blob(b"EXMON Infrastructure DA Payload: batch #98412")
# Aktualna cena blob gasu z zapasem na wzrost
blob_base_fee = int(w3.provider.make_request("eth_blobBaseFee", [])["result"], 16)
base_fee = w3.eth.get_block("latest")["baseFeePerGas"]
tx = {
"type": 3,
"chainId": w3.eth.chain_id,
"nonce": w3.eth.get_transaction_count(acct.address),
"gas": 21000,
"maxPriorityFeePerGas": w3.to_wei(1, "gwei"),
"maxFeePerGas": base_fee * 2 + w3.to_wei(1, "gwei"),
"maxFeePerBlobGas": max(blob_base_fee * 2, w3.to_wei(1, "gwei")),
"to": acct.address, # nie tworzenie kontraktu: dla blob-tx `to` jest obowiązkowe
"value": 0,
"data": b"",
}
# ckzg liczy zobowiązanie, dowody i versioned hash; sidecar jest wstrzykiwany do raw tx
signed = Account.sign_transaction(tx, key, blobs=[blob])
tx_hash = w3.eth.send_raw_transaction(signed.raw_transaction)
print(f"Blob-tx wysłana: {tx_hash.to_0x_hex()}")Jeśli nie uwzględnisz zmienności maxFeePerBlobGas, w momentach szczytowego obciążenia rollupów transakcja zawiesi się w mempoolu konsensusu, dopóki rynek nie ostygnie.
Problem węzłów archiwalnych: gdzie trafiają dane po 18 dniach
Główna pułapka przy projektowaniu architektury opartej na blobach wiąże się z ich usuwaniem. Klienci konsensusu przechowują ciała blobów przez co najmniej 4096 epok (około 18 dni), po czym je kasują. Sama transakcja oraz zobowiązanie KZG pozostają w blockchainie na zawsze, ale odzyskanie zawartości bloba na ich podstawie jest niemożliwe. Jeśli po miesiącu zajdzie potrzeba pobrania danych bloba, standardowy węzeł zwróci błąd.
Zespoły budujące produkty w oparciu o EIP-4844 muszą rozwiązać ten problem na jeden z trzech sposobów:
- Własne skrippery archiwalne: wdrażanie usług w tle, które pobierają obiekty blob z sieci P2P w momencie ich pojawienia się i zapisują w chłodnym magazynie (bucketach kompatybilnych z S3).
- Archiwa zdecentralizowane: korzystanie z sieci takich jak Celestia, Avail czy The Graph do długoterminowego przechowywania Data Availability.
- Rezygnacja z głębokiej historii: przechowywanie lokalnych indeksów tylko dla aktywnego dwutygodniowego okna, zrzucając starsze agregaty w formie skompresowanej.
Praktyka EXMON: optymalizacja przechowywania
W infrastrukturze EXMON wykorzystujemy tymczasowe przestrzenie blobów do wynoszenia ciężkich, pośrednich logów transakcyjnych, zrzutów serwisowych oraz migawek weryfikacyjnych.
Pozwala to na rozwiązanie dwóch zadań jednocześnie:
- Oszczędność budżetu: koszty publikacji danych spadły o rząd wielkości w porównaniu do starszych metod opartych na calldata.
- Czystość blockchaina: globalny stan sieci nie jest zapychany śmieciami, które tracą na aktualności już po tygodniu, przy jednoczesnym zachowaniu stumprocentowej weryfikowalności kryptograficznej w przedziale cyklu życia danych.