ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • EIP-2718 : 이더리움의 트랜잭션 유형별 Type & Payload
    evm 2024. 2. 20. 17:37

    Author : 최원혁

     

    본 게시글은 EIP-2718 & EIP-1559 & EIP-2930 & EIP-2929에 대한 내용을 다루고 있습니다.

     

    Intro


    이더리움의 블록체인은 우리가 보내는 트랜잭션에 작성된 내용을 참고하여 우리가 원하는 동작을 수행한다. 해당 내용은 정해진 표준이 존재하며, 여러 가지 유형이 존재한다. 블록체인 노드는 표준에서 벗어난 트랜잭션은 받아들이지 않는다. 때문에 올바른 트랜잭션을 전송하기 위해서는 이더리움에서 인정하는 표준 양식에 대해 숙지하고 있어야 하는 것이 중요하다. 이번 포스트를 통해 이더리움이 어떤 트랜잭션 표준을 따르는지에 대해 알아보자.

     

     

    Transaction Type & Payload


    transaction structure

     

    이더리움의 Transaction은 크게 Transaction Type(이하 Type) 과Transaction Payload(이하 Payload)로 두가지 필드(field)로 구성되어 있으며, type과 일렬로 나열된 payload 필드를 RLP Encode한 데이터를 합치면 최종적인 Transaction이 된다.

     

    EIP-2718(opens in a new tab) is what allows for this behavior. Transactions are interpreted as: TransactionType || TransactionPayload

    EIP-2718은 이 동작을 허용합니다. 트랜잭션은 다음과 같이 해석됩니다: TransactionType || TransactionPayload
    출처 이더리움 공식 org | https://ethereum.org/developers/docs/transactions#typed-transaction-envelop

     

    이더리움 공식 문서에 의하면 Type과 Payload는 EIP-2718에 정의되어 있으며, 표준은 베를린 하드포크를 통해 메인넷에 적용됬다. 각각 어떤 의미를 갖고 있는지 알아보자.

     

    TransactionType is a positive unsigned 8-bit number between 0 and 0x7f that represents the type of the transaction

    TransactionPayload is an opaque byte array whose interpretation is dependent on the TransactionType and defined in future EIPs

    Transaction Type - 0에서 0x7f 사이의 숫자로, 총 128개의 가능한 트랜잭션 유형이 있습니다. Transaction Payload - 트랜잭션 유형에 의해 정의된 임의의 바이트 배열입니다.

    출처 eip-2718 Specification | https://github.com/ethereum/EIPs/blob/master/EIPS/eip-2718.md#definitions

     

    eip-2718 표준 문서에 정의되어 있는 내용을 보면, Type는 단순히 0 ~ 0x7f(10진수로 127) 사이의 숫자이며, 트랜잭션의 유형을 분류한다. 현재 이더리움은 총 3종류의 트랜잭션 유형이 존재하기에 0x00 ~ 0x02까지 사용하고 있으며, 앞으로 추가되는 트랜잭션의 유형이 다음 Type 숫자를 순차적으로 사용될 예정이다. 그리고 Type은 트랜잭션 데이터 맨 앞에 들어가야하는 규칙을 갖고 있다.

    Payload는 트랜잭션으로 동작하고자 하는데 필요한 데이터와 전송자(from)의 서명값이 포함된다. 여기에 해당하는 필드의 종류에 따라 트랜잭션 유형이 나뉘어 진다.

     

     

    name type
    Legacy Transaction 0x00
    Access List Transaction 0x01
    EIP-1559 Transaction
    0x02

     

    샤펠라 업그레이드까지 진행된 시점의 이더리움은 Legacy Transaction, EIP-1559 Transaction, Access List Transaction 총 3가지 유형의 트랜잭션이 존재한다. 각각의 트랜잭션은 어떤 필드를 갖는지 알아보자.

     

    Legacy Transaction


    {
      nonce: 0,
      gasPrice: BigNumber { _hex: '0x6fc23ac0', _isBigNumber: true }, //1875000000
      gasLimit: 40000,
      to: '0x5FbDB2315678afecb367f032d93F642f64180aa3',
      value:1000000000000000000, // 1 ether
      data: '0x',
      v: < chainId * 2 + 35 >,
      r: '0xbf42bd7ca5e8744269bbf11f4d7a283e04fa8e28aecbd1e0a89cf8e43a248773',
      s: '0x4cfcc93eace6496b09502474b3cfa981c77a8e1baf69ed1582dfe68e3bfa616f',
    }
    

     

    Transaction Type과 Payload는 EIP-2718이 등장한 이더리움 베를린 하드포크(2021-04-15) 이후부터 적용된 개념이다. 때문에 이더리움은 원래 하나의 트랜잭션 형식만 존재 했이며, 현재는 이를 레거시 트랜잭션(Legacy Transaction) 라고 부른다. 또한 Type 개념이 없었기에, Legacy Transaction에는 Payload만 존재한다.

    Ethereum originally had one format for transactions. Each transaction contained a nonce, gas price, gas limit, to address, value, data, v, r, and s. These fields are RLP-encoded, to look something like this: RLP([nonce, gasPrice, gasLimit, to, value, data, v, r, s])

    이더리움에는 원래 하나의 트랜잭션 형식이 있었습니다. 각 트랜잭션에는 nonce, gas price, gas limit, to address, value, data, v, r, s가 포함되며, 이러한 필드는 RLP로 인코딩되어 다음과 같은 형태로 표시됩니다: RLP([nonce, gasPrice, gasLimit, to, value, data, v, r, s])

    출처 이더리움 공식 org : https://ethereum.org/developers/docs/transactions#typed-transaction-envelope

     

    이더리움 공식문서에 나와있는 내용대로 Legacy Transaction는 nonce, gasPrice, gasLimit, to, value, data, v, r, s 총9개의 필드로 구성되어 있다.

     

    transaction = RLP([nonce, gasPrice, gasLimit, to, value, data, v, r, s])

     

    각 필드의 데이터를 RLP Encode하게 되면 이더리움이 수용하는 트랜잭션이 된다.

     

     

    Access List Transaction


    Access List Transaction은 특정 스마트컨트랙트 주소와 Storage의 특정 슬롯(Slot)에 접근할 수 있도록 리스트를 트랜잭션에 작성하여 전송한다. 리스트에 작성된 Storage에 특정 슬롯(Slot)은 opcode SLOAD와 SSTORE의 가스비를 줄여준다.

    EIP-2929

     

    이더리움 베를린 하드포크(2021-04-15)에서 EIP-2929를 통해 일부 Opcode의 가스비가 조정되었다. 그 중에서 Storage에 접근(access) 유무에 따라 Opcode SLOADSSTORE의 가스비가 조정이 되었다.

    SLOAD의 경우 트랜잭션이 실행되는 동안, Storage의 특정 슬롯(Slot)에 접근한 적이 없다면 2100의 가스를 소모하게되고, 접근한 적 있다면 100 만큼의 가스를 소모한다.

     

    SSTORE 또한 접근 유무에 따라 가스비가 조정이 되는데, 이더리움의 Storage는 0(null)에서 새로운 데이터를 저장할때와, 이미 저장되어 있는 데이터를 다른 데이터로 변경하는 경우 소모되는 가스비가 다르다.

    슬롯 값이 0에서 새로운 데이터를 저장할 경우,접근한 적이 없다면 22100의 가스를 소모하게되고, 접근한 적 있다면 20000 만큼의 가스를 소모한다. 이미 저장되어 있는 데이터를 다른 데이터로 변경하는 경우, 접근한 적이 없다면 5000의 가스를 소모하게되고, 접근한 적 있다면 2900 만큼의 가스를 소모한다.

     

    Access List Transaction의 리스트에 작성된 Storage의 슬롯은 트랜잭션이 실행되기전 접근한것으로 간주한다. 만약, 특정 스마트컨트랙트의 함수가 evm에 의해 실행되는 동안 접근하는 Storage의 슬롯을 알고 있다면, Access List에 작성하여 가스비를 개선할 수 있을 것이다. 하지만, Access List에 주소와 슬롯을 추가할 때 마다 각각 2400과 1900의 가스비가 추가로 연산된다. SLOAD로 봤을 때, 리스트를 작성하게되면 2100에서 100으로 줄어들지만, 1900의 가스비가 추가되기에 가스비가 획기적으로 개선되지는 않는다.

     

    {
      chainId: 31337,
      nonce: 0,
      gasPrice: BigNumber { _hex: '0x6fc23ac0', _isBigNumber: true }, //1875000000
      gasLimit: 40000,
      to: '0x5FbDB2315678afecb367f032d93F642f64180aa3',
      value:0,
      data: '0xa9059cbb00000000000000000000000070997970c51812dc3a010c7d01b50e0d17dc79c80000000000000000000000000000000000000000000000000de0b6b3a7640000',
      accessList: [
          {
            address: "0x39a13a796a3cd9f480c28259230d2ef0a7026033",
            storageKeys: [
            "0x0000000000000000000000000000000000000000000000000000000000000000",
            ]
          }
      ],
      r: '0xc13c97e9815265d7ea01728616c5d96a304759a573e907ab8605a625d236509d',
      s: '0x096988ced93678837f96bc8035afb7b9f31d0575580cf7c01930650feed77c91',
      yParityAndS: '0x896988ced93678837f96bc8035afb7b9f31d0575580cf7c01930650feed77c91'
    }

    We introduce a new EIP-2718 transaction type, with the format 0x01 || rlp([chainId, nonce, gasPrice, gasLimit, to, value, data, accessList, signatureYParity, signatureR, signatureS]).

    EIP-2930 | https://github.com/ethereum/EIPs/blob/4aa4b310937280bc291e31bd7fa2f5b125874e6f/EIPS/eip-2930.md#abstract

     

    EIP-2930 표준에 나와있는 내용대로 Access List Transaction은 chainId, nonce, gasPrice, gasLimit, to, value, data, accessList, signatureYParity, signatureR, signatureS 총 11개의 필드로 구성되어 있고, 0x01의 transaction type을 갖고 있다.

     

    transaction = 0x01 + RLP([chainId, nonce, gasPrice, gasLimit, to, value, data, accessList, signatureYParity, signatureR, signatureS])

     

    type과 각 필드의 데이터를 RLP Encode을 합치면 이더리움이 수용하는 Access List 트랜잭션이 된다.

     

     

    EIP-1559 Transaction


    EIP-1559 또한 마찬가지로 이더리움 베를린 하드포크(2021-04-15)에서 등장하여 블록헤더에 BaseFee 필드가 추가된다. EIP-1559가 적용되기 전, 트랜잭션에서 사용되는 가스비는 사용자가 직접 입력한 gasPrice를 사용하여 수수료가 측정됬다. 이때 블록 채굴자는 채굴 보상으로 수수료를 가져가는 구조였으며, 수수료가 높은 트랜잭션만 골라 블록에 담으며 사용자들의 가스비 경쟁을 심화 시켰다.

    EIP-1559가 적용된 이후, 블록에 포함되는 트랜잭션들은 모두 기본적으로 BaseFee를 기반으로 gasPrice가 측정되었으며, 이더리움 네트워크의 혼잡도에따라 프로토콜의 알고리즘으로 설정된다. 이제는 사용자가 직접 입력한 gasPrice가 아닌 모두가 동일한 수수료를 내는 구조로 바뀐 것이다.

     

    maxPriorityFeePerGas& maxFeePerGas

     

    EIP-1559에 따라 새로운 EIP-1559 Transaction이 나타난다. 해당 트랜잭션에는 레거시 트랜잭션의 gasPrice가 없어지고 maxPriorityFeePerGas와 maxFeePerGas가 등장한다.

     

    maxFeePerGas는 gwei 단위로, 자신이 최대로 허용할 수 있는 가스의 gasPrice을 의미한다. 위에서 설명했던것 처럼 gasPrice는 프로토콜의 알고리즘에 의해 자동으로 설정되는 BaseFee를 기반으로 한다. 만약, 갑자기 네트워크가 혼잡해져서 BaseFee가 급증하게 될 때, 예상보다 높은 수수료의 트랜잭션을 거절하기 위해 maxFeePerGas를 사용된다.

     

    maxPriorityFeePerGas는 gwei 단위로, 채굴자에게 줄 수 있는 팁의 최대값을 의미한다. EIP-1559 등장 이후, 트랜잭션 수수료는 전부 소각된다. 때문에 채굴자들은 더 이상 채굴하는 명분이 없어지기에, 트랜잭션을 담지 않고 블록만 생성하는 행위를 할 것이다. 이를 방지하기 위해 사용자가 직접 보상을 입력한다.

     

    {
        chainId: 31337
        nonce: 0,
        maxPriorityFeePerGas: BigNumber { _hex: '0x59682f00', _isBigNumber: true }, // 1500000000
        maxFeePerGas: BigNumber { _hex: '0xd09dc300', _isBigNumber: true }, // 3500000000
        gasLimit: 40000,
        to: '0x5FbDB2315678afecb367f032d93F642f64180aa3',
        value:1000000000000000000, // 1 ether
        accessList: [
              {
                address: "0x39a13a796a3cd9f480c28259230d2ef0a7026033",
                storageKeys: [
                "0x0000000000000000000000000000000000000000000000000000000000000000",
                ]
              }
          ],
        data: '0x00',
        r: '0x3942576eba875ff89779507c0fb11e6865e309b53aeb2a7405e5695811a9d6cb',
        s: '0x19f6dc3f511c76b3d8d2872cecaac0a94ef29e1c8a2839342192fa3d2074c14f',
        yParityAndS: '0x19f6dc3f511c76b3d8d2872cecaac0a94ef29e1c8a2839342192fa3d2074c14f',
    }

    We introduce a new EIP-2718 transaction type, with the format 0x02 || rlp([chain_id, nonce, max_priority_fee_per_gas, max_fee_per_gas, gas_limit, destination(to), amount(value), data, access_list, signature_y_parity, signature_r, signature_s]).

    EIP-1559 Abstract | https://github.com/ethereum/EIPs/blob/4aa4b310937280bc291e31bd7fa2f5b125874e6f/EIPS/eip-1559.md

     

    EIP-1559는 표준에 나와있는 내용대로 EIP-1559 Transaction는 0x02 Type를 사용하며 chain_id, nonce, max_priority_fee_per_gas, max_fee_per_gas, gas_limit, destination(to), amount(value), data, access_list, signature_y_parity, signature_r, signature_s 총 12개의 필드로 구성되어 있고, 0x02의 transaction type을 갖고 있다. *만약 EIP-1559 Transaction에 Access List가 따로 없다면 필드에서 제외해도 무방하다.

     

    transaction = 0x02 + RLP([chain_id, nonce, max_priority_fee_per_gas, max_fee_per_gas, gas_limit, destination(to), amount(value), data, access_list, signature_y_parity, signature_r, signature_s])

     

    type과 각 필드의 데이터를 RLP Encode을 합치면 이더리움이 수용하는 EIP-1559 Transaction이 된다.

     

     

    마무리


    EIP-2718에서 정의한 Type은 0 ~ 0x7f(10진수로 127) 사이의 숫자로 굉장히 넓은 범위를 갖는다. 이는 앞으로 계속해서 추가될 트랜잭션을 식별자(type)로 구분하기 위함이다. 3월에 진행되는 덴쿤(Dancun) 업그레이드에서 Blob Transaction라는 새로운 트랜잭션이 이더리움에 도입될 예정이며, type 0x03을 갖게 된다. 이처럼 앞으로 이더리움에서 다양하게 등장하는 트랜잭션이 어떤 역할을 하는지 기대가 된다.

     

     

Designed by Tistory.