EIP-2718: Typed Transaction Envelope
새로운 트랜잭션 유형을 추가할 때 복잡성을 낮추고 하위 호환성을 보장하기 위해 인벨로프(Envelope) 트랜잭션 유형을 도입하는 EIP입니다.
Berlin
포크와 함께 적용되었습니다.
적용일시: Apr 15, 2021, 10:07:03 AM +UTC
초록
TransactionType || TransactionPayload
는 유효한 트랜잭션이며 TransactionType || ReceiptPayload
는 유효한 영수증입니다.
TransactionType
은 트랜잭션의 형식을 식별하며, *Payload
는 향후 EIP에 의해 정의될 수 있는 트랜잭션/영수증의 콘텐츠입니다.
등장 배경
과거의 문제
이더리움에서 새로운 트랜잭션 유형을 추가하려고 할 때, 기존의 모든 트랜잭션과의 하위 호환성을 보장해야 했습니다. 즉, 새로운 트랜잭션을 다른 트랜잭션과 구분할 수 있어야 했는데, 이는 인코딩된 페이로드(payload)만을 기준으로 하는 것이었습니다. EIP-155에서 이와 관련된 문제가 발생했는데, 새로운 값을 인코딩된 필드 중 하나에 비트 패킹하여 해결하려고 했습니다.
다양한 제안
현재 여러 EIP에서는 새로운 트랜잭션 유형을 정의하려는 논의가 진행 중입니다. 예를 들면, EOA(Externally Owned Account) 계정이 자신의 컨텍스트에서 직접 코드를 실행할 수 있는 트랜잭션, 메시지 전송자(msg.sender) 이외의 누군가가 가스를 지불할 수 있는 트랜잭션, Layer 1에서의 다중 서명(multi-sig) 트랜잭션과 관련된 제안 등이 있습니다.
문제 해결 방안
EIP-2718은 이러한 다양한 트랜잭션 유형을 상호 호환성을 유지하면서 정의해야 한다는 복잡한 문제를 해결하기 위해 제안되었습니다. 인벨로프 트랜잭션 유형을 도입함으로써, 새로운 트랜잭션은 기존 트랜잭션과의 하위 호환성만을 고려하면 됩니다. 그 후로는 단순히 TransactionType 간의 번호 충돌이 없도록 하는 간단한 문제만 해결하면 됩니다.
세부 사항
정의
||
은 바이트/바이트열을 연결하는 연산자입니다.
파라미터
FORK_BLOCK_NUMBER
: 하드 포크 블록 번호,12244000
트랜잭션
FORK_BLOCK_NUMBER
를 기준으로, 블록 헤더의 트랜잭션 루트는 MUST patriciaTrie(rlp(Index) => Transaction)
의 루트 해시여야 합니다.
Index
는 블록에 포함된 트랜잭션의 인덱스입니다.Transaction
은TransactionType || TransactionPayload
또는LegacyTransaction
입니다.TransactionType
은0
과0x7f
사이의 부호가 없는 8비트 정수입니다.TransactionPayload
는TransactionType
에 따라 다른 구조를 가지는 바이트열입니다. 이는 향후 EIP에 의해 정의될 수 있습니다.LegacyTransaction
은rlp([nonce, gasPrice, gasLimit, to, value, data, v, r, s])
= rlp 인코딩된 레거시 트랜잭션입니다.
향후 트랜잭션 유형에 대한 모든 서명은 TransactionType
을 서명된 데이터의 첫 바이트로 SHOULD 사용해야 합니다. 그럼으로써 하나의 트랜잭션 유형에 대한 서명을 다른 트랜잭션 유형에 재사용되는 것을 방지할 수 있습니다.
영수증
FORK_BLOCK_NUMBER
를 기준으로, 블록 헤더의 영수증 루트는 MUST patriciaTrie(rlp(Index) => Receipt)
의 루트 해시여야 합니다.
Index
는 영수증이 참조하는 블록에 포함된 트랜잭션의 인덱스입니다.Receipt
는TransactionType || ReceiptPayload
또는LegacyReceipt
입니다.TransactionType
은0
과0x7f
사이의 부호가 없는 8비트 정수입니다.ReceiptPayload
는TransactionType
에 따라 다른 구조를 가지는 바이트열입니다. 이는 향후 EIP에 의해 정의될 수 있습니다.LegacyReceipt
는rlp([status, cumulativeGasUsed, logsBloom, logs])
= rlp 인코딩된 레거시 영수증입니다.
영수증의 TransactionType
은 MUST 해당 트랜잭션의 TransactionType
과 일치해야 합니다.
논리적 근거
TransactionType은 최대 0x7f까지만 사용합니다.
미래의 안정성을 위해, TransactionType
은 최대 0x7f까지만 사용합니다. 이는 충분히 많은 트랜잭션 유형을 지원할 수 있습니다. 최상위 비트가 남아있기 때문에 향후 확장이 가능합니다. 또한 항상 첫 바이트가 0xc0
이상인 레거시 트랜잭션과 충돌하지 않습니다.
TransactionType을 서명된 데이터의 첫 바이트로 사용하는데 있어 MUST가 아닌 SHOULD인 이유
권장되는 사항이기는 하지만, 모든 경우에 대해 이 방법을 적용하기 어려운 경우가 있습니다. 예를 들어, 레거시 서명 스키마가 적용된 레거시 트랜잭션의 래핑 트랜잭션인 경우 TransactionType을 서명된 데이터의 첫 바이트로 사용할 수 없습니다. 또 다른 예로, 트랜잭션이 서명을 가지고 있지 않고 다른 메커니즘으로 유효성을 검증하는 경우도 있습니다.
TransactionType 선택 알고리즘
표준화된 알고리즘은 없습니다. 이와 관련해서는 향후 EIP에서 도입될 수 있습니다.
RLP 배열이 아닌 불투명 바이트열(Opaque Byte Array)을 사용하는 이유
향후 트랜잭션 페이로드에 대해 SSZ, LEB128 또는 고정 너비 형식과 같은 다양한 인코딩을 지원하기 위해 RLP 배열 대신 불투명 바이트열을 사용합니다.
ORIGIN과 CALLER
ORIGIN
과 CALLER
opcode가 트랜잭션 유형에 의존하도록 하여, 각 트랜잭션 유형이 이 opcode들이 반환하는 값을 정의하도록 하자는 논의가 있었습니다. 그러나 컨트랙트가 트랜잭션 유형을 식별하지 못하게 하여 컨트랙트가 트랜잭션 유형에 따라 다른 로직을 실행는 것이 바람직하지 않다는 의견이 지배적이었습니다. 또한, 이미 존재하는 컨트랙트에 대한 하위 호한성 문제도 제기되었습니다.
따라서 ORIGIN
과 CALLER
opcode는 트랜잭션 유형에 의존하지 않도록 하였습니다. 만약 트랜잭션 유형이 추가적인 정보를 컨트랙트에 전달핟록 하려면, 새로운 opcode를 도입하는 것이 더 바람직합니다.
하위 호환성(Backwards Compatibility)
첫 번째 바이트를 살펴봄으로써, 레거시 트랜잭션과 인벨로프 트랜잭션을 구분할 수 있습니다. 인벨로프 트랜잭션은 [0, 0x7f]
사이의 TransactionType
을 사용하고, 레거시 트랜잭션은 [0xc0, 0xfe]
사이의 값을 가집니다. 0xff
는 현 시점에서 예약되어 있지 않습니다. 향후 확장을 위해 남겨두었습니다.
보안 고려사항(Security Considerations)
새로운 트랜잭션 유형을 추가할 때, 서명된 페이로드의 첫 바이트를 TransactionType
으로 사용할 것을 STRONGLY 권장합니다. 그렇게 하지 않으면 다른 트랜잭션과 상호호환되는 서명을 사용한 트랜잭션을 사용해야 할텐데, 이는 사용자의 보안적 취약점을 노출시킬 수 있습니다.