
2025년 5월 7일에 이루어진 이더리움 펙트라 업그레이드는, 이더리움의 execution layer와 consensus layer의 주요 업그레이드이다.
이번 업그레이드는 그동안에 업그레이드에 비해서 가장 많은 EIP를 포함한다.
관련 업그레이드
2022 더 머지(The Merge) 업그레이드 (지분증명 전환)
2024 덴쿤(Dencun) 업그레이드 (Blob 도입)
더 머지를 통해 지분 증명 방식으로 전환했고 덴쿤 업그레이드로 확정성을 개선했다. 하지만 여전히 몇가지 문제가 존재했다.
따라서 이번 업그레이드는 이러한 문제에 대한 개선을 목표로 삼고 있다.
특히 스토리지 효율성을 높이고 검증인 참여를 원활하게 만드는 변화가 포함되어 있다.
| 항목 | 설명 |
|---|---|
| EIP-7702 | EOA의 스마트 계약화로 계정 추상화 지원 |
| EIP-7251 | 밸리데이터 스테이킹 한도 2,048 ETH로 증가 |
| EIP-6110 | 밸리데이터 입금 처리 시간 13분으로 단축 |
| EIP-7002 | 실행 계층에서 출금 트리거 가능 |
| EIP-7691 | 블롭 처리량 증가로 레이어 2 확장성 향상 |
| EIP-2537 | BLS 연산 최적화로 성능 및 보안 강화 |
| EIP-2935 | 과거 블록 해시 저장으로 스마트 계약 기능 향상 |
| EIP-7840 | 동적 블롭 스케일링 |
| EIP-7623 | 콜데이터 비용 관리 |
| EIP-7549 | 최적화된 증명 |
| EIP-7685 | 합의 계층 최적화 / 블록 검증 효율 및 네트워크 최적화 |
주요 개선 사항 11가지를 특징에에 따라 카테고리화 하여 설명한다.
| EIP | 제목 | 설명 |
|---|---|---|
| EIP-7251 | 스테이킹 상한 상향 (2048ETH) | 단일 밸리데이터가 2048 ETH까지 스테이킹 가능. 대규모 밸리데이터 운영 간소화 및 네트워크 부하 감소 |
| EIP-7549 | Attestation 구조 단순화 | Attestation 구조 간소화(committee index를 서명 밖으로 이동), ZK 검증 및 클라이언트 효율 향상 |
| EIP-6110 | 입금 전달 방식 개선 (EL → CL) | Deposit 정보를 실행 계층에서 직접 합의 계층으로 전달하는 방식 도입. 더 빠르고 안전한 입금 처리 |
| EIP-7002 | EL 기반 밸리데이터 exit 지원 | 실행 계층에서 밸리데이터 exit을 트리거할 수 있도록 하는 특별 컨트랙트 도입. 밸리데이터 키 분실 이슈 해결 |
| EIP | 제목 | 설명 |
|---|---|---|
| EIP-7685 | EL ↔︎ CL 메시지 전송 표준화 | 실행 계층에서 합의 계층으로 요청을 안전하게 전달할 수 있는 표준 프레임워크 정의. EL ↔︎ CL 연결 강화 |
| EIP-2935 | 블록 해시를 상태로 저장 | 최근 8192개 블록 해시를 상태(state)에서 제공하는 시스템 컨트랙트 도입. Stateless execution 지원 |
| EIP | 제목 | 설명 |
|---|---|---|
| EIP-7702 | EOA에 스마트 컨트랙트 코드 허용 | EOA에서 임시 스마트 컨트랙트 코드를 부여하여 EOA를 확장할 수 있도록 하는 새로운 트랜잭션 타입 도입 |
| EIP-2537 | BLS12-381 연산 프리컴파일 도입 | BLS12-381 커브 연산을 위한 프리컴파일 추가로, AA 및 ZK/스테이킹 등에서 효율적인 서명 검증 가능 |
| EIP | 제목 | 설명 |
|---|---|---|
| EIP-7691 | 블롭 수 증가 | 블롭 수 조정(평균 3개 → 6개, 최대 6개 → 9개), 롤업 확장성 향상 |
| EIP-7623 | calldata 비용 차등화 | calldata 가격을 데이터 무거운 트랜잭션만 인상 → 블롭 사용 유도 및 블록 크기 제한 |
| EIP-7840 | 블롭 스케줄을 EL 설정에 포함 | 실행 계층 설정 파일에 블롭 스케줄(타겟/최대 수 등) 직접 설정 가능하도록 구조 개선 |
consensus layer의 밸리데이터의 스테이킹 한도를 높였다.

이더리움 비컨체인에서 합의는 밸리데이터들이 블록과 epoch에 대해 투표(Attestation)를 하는 방식이다.
하나의 투표는 아래와 같이 구성되어 있었다.
type AttestationData struct {
Slot Slot // 투표가 발생한 슬롯 번호 (투표 시점)
Index CommitteeIndex // 검증자가 속한 위원회 인덱스
BeaconBlockRoot Root // 투표 대상 블록의 루트 해시
Source Checkpoint // 이전 epoch의 체크포인트
Target Checkpoint // 현재 epoch의 체크포인트
}
// 예시
AttestationData{
Slot: 12345,
Index: 3, // 이 슬롯에서 4번째 위원회
BeaconBlockRoot: 0xabc123..., // 블록 root
Source: Checkpoint{
Epoch: 385,
Root: 0xdeadbeef...,
},
Target: Checkpoint{
Epoch: 386,
Root: 0xbeefcafe...,
},
}
committeeIndex (어떤 위원회에서 나왔는지)가 포함되어 있었기 때문에, 동일한 투표 내용이라도 서로 다른 committee에서 발생한 경우 서명 해시가 달라져서 집계가 어려운 문제가 발생type AttestationData struct {
Slot Slot
Index CommitteeIndex // 항상 0으로 설정
BeaconBlockRoot Root
Source Checkpoint
Target Checkpoint
}
type Attestation struct {
AggregationBits Bitlist
Data AttestationData
Signature BLSSignature
CommitteeBits Bitvector // 새로운 필드로, 각 위원회에 대한 집계 정보를 포함
}
//최종 포함 예시
ExecutionPayload:
...
attestations: List[Attestation] # 여러 개 포함될 수 있음
Attestation들을 보다 쉽게 집계할 수 있어, 검증자들의 서명 검증 비용이 감소Attestation 수가 증가하여, 블록 공간 활용도가 향상이전에 밸리더이터의 입금은 eth1data poll 이라는 기능을 통해 Beacon 체인에서 처리되었다. 이는 Merge 이전 Beacon Chain과 실행 계층가 분리되어 있었을 때 되입된 구조이다.
이전 deposit life cycle
Beacon Chain이 실행 계층의 데이터를 주기적으로 polling 하면서 예치금(입금)정보, 블록 해시 등을 수동으로 가져와야 했고, PoW 체인의 리오그(re-org)를 고려해야 했기 때문에 복잡하고 유연하지 않았다.
보충 설명
PoW 체인에서는 블록이 뒤집힐 가능성이 있음.
예를 들어, A 블록에 입금 기록이 있었는데 리오그로 A가 무효가 되면 입금도 무효
→ PoW 블록의 finality를 확신하기 어려우므로 여러 블록을 확인해야 하고, 이중 처리 방지도 신경 써야 함
리오그(re-org)
PoW 체인은 여러 채굴자가 동시에 블록을 만들 수 있기 때문에, 동시 생성된 유효한 블록 2개 라고 한다면 네트워크는 잠시 동안 두 체인을 갖게 됨. 나중에 더 긴 체인이 생기면 블록체인은 더 긴 체인을 정식 체인으로 선택하고, 그 결과 더 짧았던 체인은 버려지고 무효 처리됨. 이 과정을 리오그(re-org)라고 함.
그래서 PoW에서는 finality가 중요한데, 블록이 생성된 것이 끝이 아니라, 보통 6개 이상 블록이 쌓여야 안전하다고 보는것도 이것 때문
Q. 머지 이후에는?
PoS에서는 finality 개념이 훨씬 강력함. 블록이 체크포인트를 통해 합의되면 절대 되돌릴 수 없음
→ 리오그 위험이 사실상 사라짐
체크포인트
Beacon Chain에서 특정 슬롯마다 설정되는 특별한 블록을 의미
→ 이 블록들은 합의 투표(Attestation)의 대상이 되며, 그 중 일부는 finalized될 수 있음
이더리움은 1슬롯 = 12초, 1 에포크 = 32 슬롯인데, 각 에폭의 첫 블록이 하나의 체크포인트
Q. 체크 포인트가 중요한 이유?
체크포인트는 밸리데이터들이 “이 시점까지는 다 동의 했어”라고 합의하는 기준점이 됨
밸리데이터는 직전 체크 포인트와 새로운 체크포인트를 연결하는 블록 체인 구간에 대해 투표를 함
이 투표가 일정 조건 이상 만족되면, 그 체크포인트는 justified 상태가 되고, 그 직전 justified 체크포인트가 finalized가 됨
- Justified 체크포인트: 2/3 이상 밸리데이터가 투표하면 정당화됨
- Finalized 체크포인트: 다음 에포크에서 또 2/3 이상 투표되면 최종화됨
DepositContract를 통해 처리DepositContract를 풀링하여 새로운 입금 이벤트를 감지eth1data라는 구조체에 요약되어 담기고, 이를 통해 새로운 밸리데이터의 입금이 처리됨. (입금 처리시 지연 발생)eth1data: {
deposit_root: bytes32, # 실행 계층에서 보고된 Merkle 루트
deposit_count: uint64, # 누적 입금 수
block_hash: bytes32 # 해당 데이터를 포함한 eth1 블록의 헤더 hash
}
EIP-6110은 밸리데이터 입금 정보를 실행 계층에서 직접 처리하도록 변경하여, 블록 생성 시 입금 데이터를 명시적으로 포함시키는 방법을 도입했다. 기존의 합의 계층에서의 입금 처리 방식보다 효율적이고 결정적인 처리를 가능하게 한다.
DepositContract에서 발생한 이벤트 로그를 파싱하여 입금 정보를 수집하고, 이를 ExecutionPayload의 deposits 필드에 명시적으로 포함def event_data_to_deposit_request(deposit_event_data) -> bytes:
deposit_data = parse_deposit_data(deposit_event_data)
pubkey = Bytes48(deposit_data[0])
withdrawal_credentials = Bytes32(deposit_data[1])
amount = deposit_data[2] # 8 bytes uint64 LE
signature = Bytes96(deposit_data[3])
index = deposit_data[4] # 8 bytes uint64 LE
return pubkey + withdrawal_credentials + amount + signature + index
class ExecutionPayload(Container):
parent_hash: Hash32
fee_recipient: ExecutionAddress
state_root: Root
receipts_root: Root
logs_bloom: Bytes[LOGS_BLOOM_BYTES]
prev_randao: Bytes32
block_number: uint64
gas_limit: uint64
gas_used: uint64
timestamp: uint64
extra_data: Bytes[MAX_EXTRA_DATA_BYTES]
base_fee_per_gas: uint256
block_hash: Hash32
transactions: List[Bytes[MAX_BYTES_PER_TRANSACTION], MAX_TRANSACTIONS_PER_PAYLOAD]
withdrawals: List[Withdrawal, MAX_WITHDRAWALS_PER_PAYLOAD] # EIP-4895
blob_gas_used: uint64 # EIP-4844
excess_blob_gas: uint64 # EIP-4844
blob_kzg_commitments: List[KZGCommitment, MAX_BLOBS_PER_BLOCK] # EIP-4844
deposits: List[Bytes[DEPOSIT_DATA_SIZE], MAX_DEPOSITS_PER_PAYLOAD] # EIP-6110
requests: List[Bytes[MAX_REQUEST_SIZE], MAX_REQUESTS_PER_PAYLOAD] # EIP-7002, EIP-7251
DepositContract : 0x00000000219ab540356cBB839Cbe05303d7705Fa
[참고] beacon-chain.md
합의 계층의 단순화: 합의 계층은 더 이상 입금 로그를 직접 파싱하지 않고, 실행 계층에서 전달된 deposits 필드를 통해 입금 정보를 처리
- 합의 계층은 Go Ethereum(클라이언트) 코드베이스가 아니라 Prysm, Lighthouse, Teku 등 별도의 클라이언트에서 처리
- 즉, Geth는 실행 계층 역할이고, EIP-6110에서 합의 계층의 입금 처리 코드는 Geth가 아닌 별도 Beacon 클라이언트(Go 언어로 작성된 Prysm 등)에서 구현
- 합의 계층 클라이언트는 로그를 파싱해 `DepositData` 생성
- 이를 Beacon state에 반영하여 밸리데이터 등록 또는 balance 업데이트
결정성 및 효율성 향상: 입금 데이터가 블록에 명시적으로 포함되므로, 입금 처리의 결정성이 보장되며, 입금 처리 지연이 감소
DepositEvent라는 이벤트 로그를 발생engine_getPayloadV4 api를 호출해서 블록을 수신: Beacon Node(합의 계층 클라이언트)는 새로운 블록을 실행 계층으로부터 받을 때, 이 request들을 함께 전달받음func (api *ConsensusAPI) getPayload(payloadID engine.PayloadID, full bool) (*engine.ExecutionPayloadEnvelope, error) {
log.Trace("Engine API request received", "method", "GetPayload", "id", payloadID)
data := api.localBlocks.get(payloadID, full)
if data == nil {
return nil, engine.UnknownPayload
}
return data, nil
}
type ExecutionPayloadEnvelope struct {
ExecutionPayload *ExecutableData `json:"executionPayload" gencodec:"required"`
BlockValue *big.Int `json:"blockValue" gencodec:"required"`
BlobsBundle *BlobsBundleV1 `json:"blobsBundle"`
Requests [][]byte `json:"executionRequests"`
Override bool `json:"shouldOverrideBuilder"`
Witness *hexutil.Bytes `json:"witness,omitempty"`
}
[참고] engine_getPayloadV4
합의 계층 클라이언트는 이 request들 중 DepositEvent 로그를 찾아서,
로그 안의 pubkey, withdrawal_credentials, amount, signature, index 등을 파싱(해석)하고 이 정보를 가지고 DepositData 구조체(또는 객체)를 생성
[참고] DepositRequest
생성한 DepositData는 Beacon Chain 상태(state)에 반영되어, 새로운 밸리데이터 등록 혹은 기존 밸리데이터의 잔액 업데이트 등에 사용
“실행 계층의 히스토리가 만료된다”는 것은, 이더리움의 미래 로드맵에서 상태 및 기록 데이터를 오래 보관하지 않고 제거하겠다는 계획을 의미. 그 예시가 Verkle 트리와 state expiry
Verkle 트리 요약
Verkle 트리는 현재 이더리움에서 사용하는 Merkle Patricia Trie를 더 압축되고 효율적인 데이터 구조로 대체하기 위한 기술
- 더 적은 증명 데이터로 상태 증명을 가능하게 함 (light client 친화적)
- 상태 데이터를 압축 → 스토리지 사용 줄이기
- 상태 만료(state expiry) 기능을 도입할 기반 제공
State expiry 요약
state expiry는 이더리움 실행 계층에 있는 계정, 컨트랙트, 스토리지 등의 상태 데이터(state)를 일정 시간이 지나면 만료시켜 더 이상 보관하지 않도록 하는 기능
예시)
- 어떤 계정이 1년 동안 트랜잭션을 안 하면 상태에서 제거됨
- 과거의 logs, receipts, calldata, storage 등도 일정 시간이 지나면 지워짐
- 새로운 노드를 부팅해도 과거의 상태를 전부 보존할 필요 없음
이전에는 밸리데이터가 밸리데이터 탈퇴를 위해 출금(exit)을 하려면 BLS 서명 키(활성 키)가 반드시 필요했다.( → 반드시 BLS 활성 키를 이용해 서명한 exit message를 보내야 했음) 반면, 출금 수신 구조는 콜드키(비활성 키)로, 출금시 수령만 가능하고 직접 요청은 불가했다. 즉, 출금 트리거는 오직 활성 키로만 가능했기 때문에 키 분리 운영, 키 분실 또는 제 3자가 운영하는 구조에서 유연성이 부족했다.
출금 요청(Exit)은 합의 계층에서만 가능
Exit message)를 만들어 비컨 체인에 제출비컨 체인에서해당 출금 정보가 상태(state)에 반영되고, 비컨 체인이 process_withdrawals를 통해 withdrawal 부분을 채우고 engine_NewPayloadV4 api를 호출하여 실행 계층으로 ExecutableData 데이터 전달
[참고] process_withdrawals
[참고] prepare_execution_payload
실행계층은 ExecutableData와 다른 데이터를 조합하여 새로운 블록에 들어갈 데이터를 만들고, 프로듀서는 tx를 실행하면서 요청 처리(withdrawal처리) 후 블록을 생성
생성된 블록은 비컨체인에서 다시 합의를 거쳐 확정
[참고] body.Withdrawals
반면, EIP-7002는 Withdrawal credential(콜드 키)로 실행 계층의 시스템 컨트랙트를 호출해서 validator exit를 트리거 할 수 있게 되었다.
Validator Exit Utility : 0x0000000000000000000000000000000000005001//Withdrawal credential 예시
0x010000000000000000000000b794f5ea0ba39494ce839613fffba74279579268
0x01 + 11바이트 0-padding + 20바이트 주소 (즉, 0x01로 시작 → 실행 계층 주소임을 의미)withdrawal credential을 확인하여 일치하면 exit이 요청을 deposit-request 처리방식으로 처리하고 비컨 체인에 전달withdrawal credential로 등록한 주소로 시스템 컨트랙트에 add_withdrawal_request(pubkey, amount) 같은 함수 tx를 전송//https://eips.ethereum.org/EIPS/eip-7002#add-withdrawal-request
def add_withdrawal_request(Bytes48: validator_pubkey, uint64: amount):
"""
Add withdrawal request adds new request to the withdrawal request queue, so long as a sufficient fee is provided.
"""
# Verify sufficient fee was provided.
fee = get_fee()
require(msg.value >= fee, 'Insufficient value for fee')
# Increment withdrawal request count.
count = sload(WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS, WITHDRAWAL_REQUEST_COUNT_STORAGE_SLOT)
sstore(WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS, WITHDRAWAL_REQUEST_COUNT_STORAGE_SLOT, count + 1)
# Insert into queue.
queue_tail_index = sload(WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS, WITHDRAWAL_REQUEST_QUEUE_TAIL_STORAGE_SLOT)
queue_storage_slot = WITHDRAWAL_REQUEST_QUEUE_STORAGE_OFFSET + queue_tail_index * 3
sstore(WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS, queue_storage_slot, msg.sender)
sstore(WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS, queue_storage_slot + 1, validator_pubkey[0:32])
sstore(WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS, queue_storage_slot + 2, validator_pubkey[32:48] ++ uint64_to_little_endian(amount))
sstore(WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS, WITHDRAWAL_REQUEST_QUEUE_TAIL_STORAGE_SLOT, queue_tail_index + 1)
engine_getPayloadV4 api를 통해 beacon에 전달 : Beacon Node(합의 계층 클라이언트)는 새로운 블록을 실행 계층으로부터 받을 때, 이 request들을 함께 전달받고 deposit-request처럼 처리하고 다시 Withdrawal이 실행계층에서 처리[참고] Withdrawal
EIP-6110(검증자 예치금 온체인 처리) 및 EIP-7002(실행 계층에서의 검증자 출금 트리거)와 같은 제안들과 함께 작동하여 실행 계층과 합의 계층 간의 통신을 개선한다.
EIP-7685는 실행 계층에서 발생한 다양한 요청을 합의 계층으로 효율적으로 전달하고 처리할 수 있는 프레임워크를 제공하여 이더리움의 모듈화 및 확장성을 향상시킨다.
실행 계층의 블록 헤더에 새로운 32바이트 필드인 requests_hash가 추가되었다. 이 필드는 블록 내의 모든 요청을 해시화하여 생성된 값으로, 합의 계층이 해당 요청들을 검증하고 처리하는 데 사용한다.
합의 계층은 requests_hash를 통해 실행 계층에서 전달된 요청들을 인식하고, 각 요청의 request_type에 따라 적절한 처리를 수행한다. (예 : 밸리데이터의 exit 요청, deposit 요청)
//go-ethereum/miner/worke.go
func (miner *Miner) generateWork(params *generateParams, witness bool) *newPayloadResult {
//블록 헤더, 상태 트리 등 초기 작업 환경 구성
work, err := miner.prepareWork(params, witness)
if err != nil {
return &newPayloadResult{err: err}
}
// 블록에 포함될 트랜잭션을 메모리풀에서 가져와 정렬 및 필터링하여 포함
if !params.noTxs {
interrupt := new(atomic.Int32)
timer := time.AfterFunc(miner.config.Recommit, func() {
interrupt.Store(commitInterruptTimeout)
})
defer timer.Stop()
err := miner.fillTransactions(interrupt, work)
if errors.Is(err, errBlockInterruptedByTimeout) {
log.Warn("Block building is interrupted", "allowance", common.PrettyDuration(miner.config.Recommit))
}
}
// 실행 결과로 발생한 Withdrawals 포함 및 로그 수집
body := types.Body{Transactions: work.txs, Withdrawals: params.withdrawals}
allLogs := make([]*types.Log, 0)
for _, r := range work.receipts {
allLogs = append(allLogs, r.Logs...)
}
// Collect consensus-layer requests if Prague is enabled.
// (Prague 하드포크 이후) 합의 계층 요청 수집
var requests [][]byte
if miner.chainConfig.IsPrague(work.header.Number, work.header.Time) {
requests = [][]byte{}
// Deposit 로그를 수집하는 부분
// EIP-6110 : DepositContract 로그를 파싱하여, 입금 요청을 requests 배열에 추가
if err := core.ParseDepositLogs(&requests, allLogs, miner.chainConfig); err != nil {
return &newPayloadResult{err: err}
}
// EIP-7002 : EL withdrawal credentials 기반으로 validator exit을 요청
if err := core.ProcessWithdrawalQueue(&requests, work.evm); err != nil {
return &newPayloadResult{err: err}
}
// EIP-7251 : 여러 validator를 합치는 consolidation 요청 처리
if err := core.ProcessConsolidationQueue(&requests, work.evm); err != nil {
return &newPayloadResult{err: err}
}
// -> 이 요청들은 합의 계층이 처리해야 할 작업 목록으로, requests로 담아서 블록에 포함
}
// 요청 목록을 해시화 하여 블록 헤더에 포함
if requests != nil {
reqHash := types.CalcRequestsHash(requests)
work.header.RequestsHash = &reqHash
}
//최종 블록 조립
block, err := miner.engine.FinalizeAndAssemble(miner.chain, work.header, work.state, &body, work.receipts)
if err != nil {
return &newPayloadResult{err: err}
}
//결과 반환
return &newPayloadResult{
block: block,
fees: totalFees(block, work.receipts),
sidecars: work.sidecars,
stateDB: work.state,
receipts: work.receipts,
requests: requests,
witness: work.witness,
}
}
[참고] worker.go
[참고] processRequestsSystemCall
[참고] SystemContracts
request_type(1바이트)과 request_data로 구성되며, request_data는 해당 요청의 세부 정보를 포함하는 불투명한 바이트 배열requests_hash 필드를 추가하여, 블록 내 모든 요청의 커밋먼트를 저장. 이 해시는 요청들을 해시한 값들의 리스트를 다시 해시하여 계산requests_hash를 통해 합의 계층은 실행 계층에서 발생한 요청들을 확인하고 적절한 시점에 처리
1. (CL → EL) engine_forkchoiceUpdatedV3 호출 : 새로운 블록 상태와 타임 정보를 EL에 전달
2. (EL) 내부적으로 preparePayloadV3 호출 : ExecutionPayload 준비 (deposit, requests, withdrawals, transactions 등 조립)
3. (CL-> EL) engine_getPayloadV4(payloadID) 호출 : 생성된 ExecutionPayload를 요청
4. (EL → CL) returns ExecutionPayload : transactions, withdrawals, deposits, requests, blobsBundle, parentBeaconBlockRoot 등을 포함한 완전한 Payload 반환.
5. (CL) Beacon Block 구성 : 이 ExecutionPayload를 Beacon block에 포함하여 최종 블록 구성
6. (CL → EL) engine_newPayloadV4(payload) 호출 : 해당 ExecutionPayload를 실제로 실행하여 블록을 적용하도록 요청. 유효성 검증 후 결과 반환
[참고] api.go
기존 EVM에서는 BLOCKHASHopcode를 통해 스마트 컨트랙트에서 블록 해시를 조회할 수 있었다. 하지만 최신 256개 블록까지만 조회가 가능(BLOCKHASH(n))했고, 이는 앞으로 stateless client(상태를 저장하지 않는 클라이언트) 환경에서는 불편하거나 불가능해질 수 있다.
8192개인 이유?
1. 롤업(rollup) & light client 사용 시나리오 최적화
- 롤업 시스템은 종종 몇 천 블록 전 데이터를 증명으로 제출함.
- 예: 12초 블록 * 8192 = 약 1.1일 분량의 블록 해시 보존.
- 즉, 약 1~1.2일 분량의 롤업 증명 처리에 필요한 블록 해시 커버 가능.
2. 트레이드오프 고려- 블록마다 32바이트 해시 × 8192개 = 약 256 KiB 추가 데이터.
- 클라이언트가 관리하기 부담 없는 크기로 설계 (RAM/디스크 부담 고려).
- 특히 컨센서스 레이어가 아닌 실행 레이어에 저장되는 것이므로 효율성 중요.
3. 2의 거듭제곱 값 사용- 8192 = 2¹³
- 내부 처리 (슬라이딩 윈도우, 링버퍼 구조 등)에서 bitmask 및 index 연산 최적화가 가능.
- 가변 크기 배열보다 고정된 슬라이딩 윈도우 구조가 성능과 단순성 측면에서 유리.
| Parameter | Value |
|---|---|
| BLOCKHASH_SERVE_WINDOW | 256 |
| HISTORY_SERVE_WINDOW | 8191 |
| SYSTEM_ADDRESS | 0xfffffffffffffffffffffffffffffffffffffffe |
| HISTORY_STORAGE_ADDRESS | 0x0000F90827F1C53a10cb7A02335B175320002935 |
BLOCKHASH_SERVE_WINDOW : BLOCKHASH오피 코드로 조회 가능한 과거 블록 해시의 최대 개수HISTORY_SERVE_WINDOW : 시스템 컨트랙트를 통해 저장되는 과거 블록 해시의 개수SYSTEM_ADDRESS : 시스템 컨트랙트가 트리거 되는 특수 주소. 이 주소에서 실행되는 코드나 호출은 “시스템 수준”으로 간주HISTORY_STORAGE_ADDRESS : 과거 블록 해시를 저장하고 조회하는 시스템 컨트랙트 주소. 일반 트랜잭션 및 컨트랙트에서 CALL 가능 set(bytes32 blockHash) : SYSTEM_ADDRESS만 호출 가능. 블록 해시 저장get(uint256 blockNumber) → bytes32 :누구나 호출 가능. 저장된 과거 해시 반환[블록 처리 중]
SYSTEM_ADDRESS (자동 실행)HISTORY_STORAGE_ADDRESS.set(block.parentHash)HISTORY_STORAGE_ADDRESS.get(n)을 통해 과거 블록 해시를 조회[참고] execution_hash
이더리움 상태(state)란?
계정의 잔액, 스마트 컨트랙트의 스토리지, nonce 등 모든 온체인 데이터
현재 이더리움 노드는 전체 상태를 메모리에 보관하여 트랜잭션을 처리
Stateless Ethereum이란?
노드가 전체 상태를 저장하지 않고도 트랜잭션을 처리할 수 있게 하려는 연구 방향
- 트랜잭션이 “필요한 상태 데이터”를 함께 포함
트랜잭션에, 그것이 접근하거나 변경할 계정/스토리지에 대한 상태 증명(예: Merkle proof등)을 같이 포함시킴
따라서, 검증자나 노드가 별도로 상태를 보유하지 않아도 해당 트랜잭션이 유효한지 검증 가능- 상태 저장 노드와 stateless 노드를 분리
일부 노드는 상태를 저장(archival/full nodes)
다른 노드는 상태 없이 검증만 수행(stateless clients)
Stateless 필요성
- 노드 운영 비용 감소
전체 상태를 저장하지 않으면, 하드웨어 요구사항이 낮아짐 → 더 많은 노드 참여 유도- 동기화 문제 완화
신규 노드가 전체 상태를 받지 않아도 바로 네트워크에 참여 가능- 더 빠른 트랜잭션 처리
일부 상황에선 경량화된 검증으로 처리 속도 증가
Stateless 클라이언트가 제대로 동작하려면?- 현재 상태에 대한 효율적인 증명이 필요하고 ( → Verkle Tree)
- 과거 블록 해시에 대한 접근 및 검증이 필요 ( → EIP-2935)
관련 기술
- witness (증인 데이터) : 트랜잭션 실행에 필요한 상태 데이터와 그 Merkle proof
- Verkle Tree : 기존 Merkle Patricia Tree 보다 작은 proof로 상태 검증을 가능하게 하는 새로운 상태 트리 구조(EIP-4844 이후 적용 예상)
- EIP-4444 : 오래된 상태(history)를 보존할 필요가 없도록 해주는 프로토콜
EIP-7702는 AA를 구현하기 위한 핵심 제안 중 하나로, EOA를 일시적으로 스마트 컨트랙트처럼 동작하게 만드는 기능을 도입한다. 이는 기존의 EOA와 스마트 컨트랙트의 경계를 허물고 유연성과 확장성을 높이기 위한 기반을 제공한다.
새로운 트랜잭션 필드 도입
EIP-7702는 새로운 트랜잭션 타입(0x04)에 다음 필드를 추가한다.
authorizationList : [[chain_id, address, nonce, y_parity, r, s], ...]EIP-7702 작성 초기에는 delegated될 컨트랙트의 코드를 직접 authorization list에 추가했으나 최종적으로는 tx 크기를 줄이기 위해 컨트랙트의 address를 추가하기로 결정 (자세한 이유는 섹션 참고)
Creation by template
The cost analysis makes the answer clear. The smallest proxy would be around 50 bytes and an address is 20 bytes. The 30 byte difference provides no useful additional functionality and will be inefficiently replicated billions of times on the chain.
이 트랜잭션을 실행할 때만 이 코드를 임시로 계정에 붙여서 동작한다. 실행이 끝나면 코드는 사라지고 원래 EOA로 돌아간다.
[구조체 예시]
[참고] transaction.go
// Transaction types.
const (
LegacyTxType = 0x00
AccessListTxType = 0x01
DynamicFeeTxType = 0x02
BlobTxType = 0x03
SetCodeTxType = 0x04
)
[참고] tx_setcode.go
type SetCodeTx struct {
ChainID *uint256.Int
Nonce uint64
GasTipCap *uint256.Int // a.k.a. maxPriorityFeePerGas
GasFeeCap *uint256.Int // a.k.a. maxFeePerGas
Gas uint64
To common.Address
Value *uint256.Int
Data []byte
AccessList AccessList
AuthList []SetCodeAuthorization
// Signature values
V *uint256.Int
R *uint256.Int
S *uint256.Int
}
type SetCodeAuthorization struct {
ChainID uint256.Int `json:"chainId" gencodec:"required"`
Address common.Address `json:"address" gencodec:"required"`
Nonce uint64 `json:"nonce" gencodec:"required"`
V uint8 `json:"yParity" gencodec:"required"`
R uint256.Int `json:"r" gencodec:"required"`
S uint256.Int `json:"s" gencodec:"required"`
}
| 필드 | 설명 | ||
|---|---|---|---|
| chainId | 이 서명이 유효한 체인 ID | ||
| Address | 위임된 실행 대상 주소 (컨트랙트 주소 등), 예: `0xef0100 | address` | |
| nonce | 서명자의 nonce (재사용 방지) | ||
| V | BLS/ECDSA 서명값 Y-parity | ||
| R | 서명값 R | ||
| S | 서명값 S |
[참고] statedb.go
setCode를 사용하여 새로운 코드를 설정. 이때 코드의 해시도 함께 계산하여 저장
func (s *StateDB) SetCode(addr common.Address, code []byte) (prev []byte) {
stateObject := s.getOrNewStateObject(addr)
if stateObject != nil {
return stateObject.SetCode(crypto.Keccak256Hash(code), code)
}
return nil
}
이때, address 기준으로 코드가 저장되기때문에 같은 address에 대하여 다른 코드가 들어가 있다면 덮어써질 수 있음
SetCodeAuthorization[] = [
{ account: 0xAlice, code: CODE_A, signature: SIG_A },
{ account: 0xAlice, code: CODE_B, signature: SIG_B }
]
이 경우 실행 흐름
1. 첫 번째 setCode(0xAlice, CODE_A) 적용
2. 두 번째 setCode(0xAlice, CODE_B)로 덮어씀
3. 실행 시에는 CODE_B만 적용됨
이 트랜잭션은 EOA가 직접 임시로 특정 스마트 컨트랙트 코드를 실행할 권한을 AuthList를 통해 부여받아 트랜잭션을 수행할 수 있도록 한다.
AuthList는 일종의 위임 서명 모음이며, 검증된 서명이 있으면 해당 주소의 코드를 실행하는 것이 허용된다.
즉, 기존 EOA는 여전히 단순 서명 기반으로 작동되지만, 이 새로운 트랜잭션 유형을 사용하면 EOA가 마치 컨트랙트처럼 확장된 기능을 실행할 수 있는 것이다.
SetCodeTx 구조체로 디코딩authorizationList 필드를 포함하여, EOA가 위임한 스마트 계약 주소와 서명 정보를 포함SetCodeAuthorization 항목에 대해 다음을 수행ecrecover를 사용하여 서명자가 실제 EOA인지 확인합니다.0xef0100 || address 형식으로 설정하여, 지정된 스마트 컨트랙트 주소로의 위임임을 표시[트랜잭션 예시]
Etherscan에서 set code tx 조회 가능https://etherscan.io/txnauthlist
트랜잭션 실행 흐름
authorizationList에 서명된 임시 코드를 포함시켜 트랜잭션을 보낸다.accountCode가 해당 트랜잭션 동안 임시 코드로 계정에 로드된다.즉, 트랜잭션 중에만 스마트 계약처럼 동작하는 EOA를 만들 수 있는 것이다.
tx 호출 시 sender 뿐만 아니라 authority의 nonce도 1 증가함
따라서 sender 주소, to 주소, authority 주소가 모두 같고 authorization_list 길이가 1이면 nonce가 최종적으로 2 증가함 (tx 예시: tx의 nonce는 1, authorization_list 안의 nonce는 2)
✔️ EOA vs EIP-4337 vs EIP-7702
| 항목 | EOA | EIP-4337 | EIP-7702 |
|---|---|---|---|
| 서명 방식 | 단일 개인 키 서명 | 커스텀 검증 함수 | 트랜잭션 내 임시코드로 검증 |
| 계정 유형 | 외부 소유 계정(EOA) | 스마트 컨트랙트 계정(CA) | EOA + 임시 컨트랙트 기능 |
| 컨트랙트 사용 | 없음 | 필수 | 트랜잭션 중에만 임시 사용 |
| 멀티시그 지갑 | 불가능 | 가능 | 가능 |
| 소셜 복구 | 불가능 | 가능 | 가능 |
| 가스 수수료 커스터마이징 | 불가능 | 수수료 대납 가능 | 유연한 처리 가능 |
| Layer 2 호환성 | 기본 지원 | 추가 구현 필요 | 유연한 지원 |
| 도입 난이도 | 없음 | 높음(entry point 필요) | 낮음 (EOA 확장) |
| 배포 필요 여부 | 없음 | 컨트랙트 배포 필요 | 없음 |
| 트랜잭션 타입 | Legacy / EIP-1559 | UserOperation | EIP-7702(새로운 타입) |
| 사용자 지갑 변화 | 단순 지갑 | 완전히 새로운 컨트랙트 지갑 필요 | 기존 지갑 그대로 사용하며 확장 가능 |
| 목표 및 철학 | 단순성과 직접 서명 | 완전한 계정 추상화 구조 구현 | 기존 EOA를 확장해 추상화에 근접 |
| 요약 | 단순하고 보편적이지만 기능이 제한적 | 강력하지만 복잡하고 별도 인프라 필요 | EOA를 그대로 유지하면서 컨트랙트 기능을 임시로 부여 |
상황
0xAlice)를 사용하지만, 계정 키 분실 방지를 위해 다중 서명으로 보호받고 싶어 함해결 방법 (EIP-7702)
1. Alice는 트랜잭션을 보낼 때 다음을 포함함:
authList에 자신의 계정(0xAlice)에 다음 코드 지정:
function validateUserOp(bytes calldata userOp) public view returns (bool) {
return verifyMultisig(userOp); // 3-of-5 방식 등
}
장점
상황
해결 방법
authList를 통해 다음 코드를 지정:function validatePaymaster(bytes calldata userOp) external returns (bool) {
return signatureCheck(userOp);
}
기존 방식
해결 방법
authList에 담아서 실행:function executeBatch(bytes[] calldata calls) external {
for (uint i = 0; i < calls.length; i++) {
(bool success, ) = address(this).call(calls[i]);
require(success);
}
}
BLS12-381은 이더리움 PoS 합의 계층에서 밸리데이터 서명에 사용되는 곡선 연산이다.
EIP-2537 도입 전에는 이더리움에서 BLS12-381 연산을 직접적으로 지원하지 않았기 때문에, 개발자나 프로토콜 레벨에서 해당 연산을 사용할 때 직접 구현하거나 외부 라이브러리에 의존해야 했다.
특히, 연산 자체가 복잡하고 무겁기 때문에 Solidity 등에서 직접 구현할 수 없어 클라이언트(Go, Rust 등)에서 구현했다.
프리 컴파일
- EVM에 내장된 특수 스마트 컨트랙트
- 사용자가 배포하는게 아니라, 클라이언트(Geth 등)에 직접 구현됨
- 일반 스마트 컨트랙트처럼 호출 가능하지만, 성능과 보안이 뛰어남
- 주로 암호 연산과 같은 고성능 작업을 위해 사용
bn256 처럼 시스템 컨트랙트 주소로 호출 가능Blob
EIP-4844은 이더리움에 새로운 트랜잭션 유형을 추가하여 Blob 데이터를 담을 수 있도록 함
이 블롭은 execution layer에서 직접 읽히지 않으며, 대신 data availability layer를 통해 Rollup 확장성을 높이는데 쓰임
// BlobTx represents an EIP-4844 transaction.
type BlobTx struct {
ChainID *uint256.Int
Nonce uint64
GasTipCap *uint256.Int // a.k.a. maxPriorityFeePerGas
GasFeeCap *uint256.Int // a.k.a. maxFeePerGas
Gas uint64
To common.Address
Value *uint256.Int
Data []byte
AccessList AccessList
BlobFeeCap *uint256.Int // a.k.a. maxFeePerBlobGas
BlobHashes []common.Hash
// A blob transaction can optionally contain blobs. This field must be set when BlobTx
// is used to create a transaction for signing.
Sidecar *BlobTxSidecar `rlp:"-"`
// Signature values
V *uint256.Int
R *uint256.Int
S *uint256.Int
}
// BlobTxSidecar contains the blobs of a blob transaction.
type BlobTxSidecar struct {
Blobs []kzg4844.Blob // Blobs needed by the blob pool
Commitments []kzg4844.Commitment // Commitments needed by the blob pool
Proofs []kzg4844.Proof // Proofs needed by the blob pool
}
흐름 간단 요약
1. blobHash / blob / proof 등을 blobTx에 담아서 tx를 만들어서 txPool로 전송
2. EL에서 블록으로 생성
3. CL에서 getPayload를 통해서 blob을 가져감
4. CL에서 blobHash와 proof를 통해 블롭의 유효성을 검증하고 블록을 합의 ( → 이때 합의를 위해 blob은 randao로 결정되는 P2P subnet을 통해서 CL 노드간 blob 전파)
5. EL에서는 CL에서 받은 blobHash와 유저가 tx를 만들때 계산 했던 blobHash와 같은건지 확인하고 저장
6. layer2에서는 이 블롭 데이터를 활용하여 트랜잭션 데이터를 게시하거나 증명을 제공
Blob은 레이어2를 위한 데이터 가용성을 제공하는 구조로, 이전 덴쿤(Dencun) 업그레이드에서 도입되었다.
| 항목 | 내용 |
|---|---|
| 고정 목표 블롭 수 | 블록당 target = 3 blobs (최대 6 blobs) |
| 수수료 조절 한계 | blob\_base\_fee는 블롭 수 증가에 따라 선형 조절되지만 경직된 구조 |
| 과도한 수요 발생 시 | 수수료 급등 → 블롭을 넣기 어려움 → 데이터 포스팅 비용이 치솟음 |
| 유연성 부족 | L2나 Rollup 수요 급증 시 적응이 느림 |
이 EIP-7691의 도입은, EIP7594(PeerDAS)가 도입되어 더 높은 블롭 처리량을 지원하기 전까지 과도기적 확장 솔루션 역할을 수행
target_blobs_per_block : 3 → 6max_blobs_per_block : 6 → 9// DefaultCancunBlobConfig is the default blob configuration for the Cancun fork.
DefaultCancunBlobConfig = &BlobConfig{
Target: 3,
Max: 6,
UpdateFraction: 3338477,
}
// DefaultPragueBlobConfig is the default blob configuration for the Prague fork.
DefaultPragueBlobConfig = &BlobConfig{
Target: 6,
Max: 9,
UpdateFraction: 5007716,
}
블롭 수수료(blob_base_fee) 곡선 개선
- 기존 EIP-1559 스타일은 수수료 급등이 너무 빠름
- EIP-7691은 수수료 증가를 완만하게 조정
- EIP-7691은 수수료 증가를 완만하게 조정
- 더 많은 사용자가 감당 가능
- Rollup/L2 들이 더 자주 blob을 포스팅 가능
UpdateFraction
- 블롭 수수료를 조정할 때, 이전 블록의 볼롭 사용량과 목표 블롭 수(target_blobs_per_block)의 차이를 기반으로 수수료를 증가시키거나 감소시키는데, 이때UpdateFraction은 수수료 조정의 속도를 결정하는 분모로 사용
- 블롭이 가득 찬 경우 수수료가 약 8.2% 증가, 블롭이 비어있는 경우 수수료가 약 14.5% 감소하도록 조정
Rollup 친화적 구조
- Rollup의 데이터 수요는 갑작스럽게 증가할 수 있음
- 이를 위해 L2-friendly fee tuning을 도입
- L2가 안정적으로 Data availability를 확보할 수 있게 함
수수료 조정 공식 (EIP-4844 & 7691 동일)
new_base_fee = old_base_fee × (1 + Δ / update_fraction)
- Δ =
excess_blob_gas-target_blob_gasupdate_fraction= 5007716 (EIP-7691의 UpdateFraction)target_blob_gas=target_blobs×BLOB_GAS_PER_BLOB
(하나의 blob gas는 131072)
블롭이 가득 찬 경우 (9개 → full usage)
- Target: 6개 × 131072 = 786432
- Actual: 9개 × 131072 = 1179648
- Δ = 1179648 - 786432 = 393216
base_fee_new = base_fee_old × (1 + Δ / update_fraction) = base_fee_old × (1 + 393216 / 5007716) ≈ base_fee_old × 1.0785 → 약 7.85%✔ 공식 문서에서는 근사값으로 약 8.2%라고 표현
블롭이 비어 있는 경우 (0개)
- Actual = 0, Target = 786432
- Δ = 0 - 786432 = -786432
base_fee_new = base_fee_old × (1 + Δ / update_fraction) = base_fee_old × (1 - 786432 / 5007716) ≈ base_fee_old × 0.8439 → 약 -15.6%✔ EIP-7691 문서에서는 근사값으로 -14.5% 정도로 표현.
(정확도 차이는rounding이나 blob가스 단위 단절에서 발생)
덴쿤 업그레이드 이전, L2 롤업들은 데이터를 콜데이터로 이더리움에 저장했다. 이후 블롭이 도입되어 L2는 더 효율적인 방식으로 데이터를 저장하게 되었다.
그러나 블롭과 콜데이터 모두 이더리움 대역폭을 사용하기 때문에, 블롭이 많은 블록에서 콜데이터까지 많을 경우 P2P 네트워크에 과부하를 유발했다.
calldata는 상태(state)가 아니라 체인 히스토리에 저장됨calldata가 커지면:calldata가 크면:calldata는 EVM에서 가스 비용만 들고 실행은 안됨 (ex. 그냥 데이터)0x00으로 가득찬 calldata를 반복적으로 보내서calldata 가스 비용: 0바이트당 4 가스, 1바이트당 16 가스즉, 네트워크 안정성을 위해 데이터 많은 콜데이터 트랜잭션만 비용을 높여 L2가 블롭을 사용하도록 유도하는 개선안이다.
// IntrinsicGas computes the 'intrinsic gas' for a message with the given data.
func IntrinsicGas(data []byte, accessList types.AccessList, authList []types.SetCodeAuthorization, isContractCreation, isHomestead, isEIP2028, isEIP3860 bool) (uint64, error) {
//1.기본 가스 설정
var gas uint64
if isContractCreation && isHomestead {
gas = params.TxGasContractCreation
} else {
gas = params.TxGas
}
//2. 가스 비용 계산
dataLen := uint64(len(data))
// Bump the required gas by the amount of transactional data
if dataLen > 0 {
// Zero and non-zero bytes are priced differently
//2-1. zero / non-zero 바이트 수 계산
z := uint64(bytes.Count(data, []byte{0}))
nz := dataLen - z
// Make sure we don't exceed uint64 for all data combinations
// 2-2. EIP-2028 조건에 따른 가스 비용 적용
nonZeroGas := params.TxDataNonZeroGasFrontier // 일반적으로 68
if isEIP2028 {
nonZeroGas = params.TxDataNonZeroGasEIP2028 // EIP-2028 적용시 16
}
if (math.MaxUint64-gas)/nonZeroGas < nz {
return 0, ErrGasUintOverflow
}
// 2-3. overflow 체크 + 가스 누적
gas += nz * nonZeroGas
if (math.MaxUint64-gas)/params.TxDataZeroGas < z {
return 0, ErrGasUintOverflow
}
gas += z * params.TxDataZeroGas
// 3. EIP-3860: Init code 길이에 따른 가스 추가
if isContractCreation && isEIP3860 {
lenWords := toWordSize(dataLen)
if (math.MaxUint64-gas)/params.InitCodeWordGas < lenWords {
return 0, ErrGasUintOverflow
}
gas += lenWords * params.InitCodeWordGas
}
}
// 4. EIP-2930: Access List 관련 가스
if accessList != nil {
gas += uint64(len(accessList)) * params.TxAccessListAddressGas
gas += uint64(accessList.StorageKeys()) * params.TxAccessListStorageKeyGas
}
// 5. EIP-7702: SetCodeAuthorization 관련 가스
if authList != nil {
gas += uint64(len(authList)) * params.CallNewAccountGas
}
//6. 최종 반환
return gas, nil
}
| 필드 | 설명 |
|---|---|
| data | 트랜잭션의 calldata (입력 데이터) |
| accessList | EIP-2930의 액세스 리스트 |
| authList | EIP-7702 관련 authorization 목록 |
| isContractCreation | 컨트랙트 생성 여부 |
| isHomestead, isEIP2028, isEIP3860 | 포크 여부에 따른 가스 정책 제어 |
// params/protocol_params.go
const (
TxDataZeroGas uint64 = 4
TxDataNonZeroGasFrontier uint64 = 68
TxDataNonZeroGasEIP2028 uint64 = 16
TxDataZeroGasEIP7623 uint64 = 12
TxDataNonZeroGasEIP7623 uint64 = 48
)
// core/state_transition.go
func IntrinsicGas(
...
dataLen := uint64(len(data))
if dataLen > 0 {
z := uint64(bytes.Count(data, []byte{0}))
nz := dataLen - z
zeroByteGas := params.TxDataZeroGas
nonZeroGas := params.TxDataNonZeroGasFrontier
if isEIP2028 {
nonZeroGas = params.TxDataNonZeroGasEIP2028
}
// EIP-7623
if isEIP7623 {
zeroByteGas = params.TxDataZeroGasEIP7623
nonZeroGas = params.TxDataNonZeroGasEIP7623
}
if (math.MaxUint64-gas)/nonZeroGas < nz {
return 0, ErrGasUintOverflow
}
gas += nz * nonZeroGas
if (math.MaxUint64-gas)/zeroByteGas < z {
return 0, ErrGasUintOverflow
}
gas += z * zeroByteGas
if isContractCreation && isEIP3860 {
lenWords := toWordSize(dataLen)
if (math.MaxUint64-gas)/params.InitCodeWordGas < lenWords {
return 0, ErrGasUintOverflow
}
gas += lenWords * params.InitCodeWordGas
}
}
...
}
[참고] state_transition.go
이더리움은 블롭을 통해 L2 데이터를 더 효율적으로 저장한다.
이 블롭의 목표 수(target)와 최대 수(max)는 현재 네트워크 업그레이드에 따라 고정되거나, 클라이언트 간 Engine API를 통해 교환되어 설정된다.
var (
// DefaultCancunBlobConfig is the default blob configuration for the Cancun fork.
DefaultCancunBlobConfig = &BlobConfig{
Target: 3,
Max: 6,
UpdateFraction: 3338477,
}
// DefaultPragueBlobConfig is the default blob configuration for the Prague fork.
DefaultPragueBlobConfig = &BlobConfig{
Target: 6,
Max: 9,
UpdateFraction: 5007716,
}
// DefaultOsakaBlobConfig is the default blob configuration for the Osaka fork.
DefaultOsakaBlobConfig = &BlobConfig{
Target: 6,
Max: 9,
UpdateFraction: 5007716,
}
// DefaultBlobSchedule is the latest configured blob schedule for Ethereum mainnet.
DefaultBlobSchedule = &BlobScheduleConfig{
Cancun: DefaultCancunBlobConfig,
Prague: DefaultPragueBlobConfig,
Osaka: DefaultOsakaBlobConfig,
}
)
blob을 실제로 다루는 합의 계층에서 target blobs per block 와 max blobs per block 등 정보를 가지고 있다. 하지만 현실적으로 이 값들을 실행 계층에서도 필요하다.
예를 들어, eth_feeHistory RPC 호출 결과에는 blobGasUsedRatio라는 필드가 있다. 이 필드는 최대 블롭 개수(max blobs)를 필요로 한다.
하지만 그때마다 실행 계층에서 매 블록마다 max blob값을 엔진 API로 받아오는것이 너무 과하다고 판단하여(maxBlobsPerBlock은 보통 프로토콜 수준에서 거의 고정되거나 드물게 변경되는 값) 실행 계층에 설정값으로 고정해 두는 것이다.
[참고] config.go
engine_forkchoiceUpdated)에 blobGasTarget, maxBlobsPerBlock 같은 값도 함께 전달[references]
https://ethereum.org/ko/roadmap/pectra/
https://www.coinex.land/ko/academy/detail/2575-the-ethereum-pectra-upgrade-your-ultimate-guide-to-the-future-of-eth?pId=2&sId=3