Technology

Toward Ethereum Equivalence #4 — Ethereum 트랜잭션 타입 지원

Klaytn v1.8.0에서는 이더리움 트랜잭션 타입을 지원하기 위한 새로운 트랜잭션 타입이 추가되었습니다. 이 포스팅에서는 개발자들이 새롭게 추가된 트랜잭션에 대해 이해하고 사용할 수 있도록 새로운 트랜잭션 타입의 정의와 목적 그리고 사용 할 때 유의할 점에 대해서 설명합니다.

Ethereum 트랜잭션 타입 지원의 목적

이전의 이더리움은 지금의 LegacyTransaction이라고 불리는 하나의 트랜잭션 형태만 지원하고 있었으며 클레이튼에서는 이전부터 다양한 트랜잭션을 타입을 지원하고 있었습니다. 하지만 이더리움이 EIP-2718 Typed Transaction Envelop을 도입하면서 여러 타입의 트랜잭션을 지원하게 되었고 이로 인해 클레이튼에서 제공하는 트랜잭션 타입과 이더리움의 트랜잭션 타입을 수용함과 동시에 구분할 수 있는 방안이 필요했습니다. Klaytn v1.8.0에서는 이를 위해 이더리움 트랜잭션 포맷과 호환이 가능한 트랜잭션 타입이 추가되었습니다.

이 포스팅에서는 클레이튼에서 이더리움 트랜잭션을 사용하는 경우 유의해야 할 점에 대해서 먼저 설명한 뒤, 이더리움 트랜잭션 타입과 클레이튼에 새롭게 추가된 트랜잭션 타입에 대해서 설명합니다.

클레이튼에서 이더리움 트랜잭션 사용하기

먼저 이더리움과 클레이튼의 차이로 인하여 일부 필드는 이더리움과 다르게 동작할 수 있다는 점을 유의해야 합니다. 예를 들어 클레이튼은 고정 가스값을 사용하기 때문에 DynamicFeeTx의 GasTipCap(maxPriorityFeePerGas)과 GasFeeCap(maxFeePerGas)은 거버넌스에 의해 정해진 가스값(현재 25 ston)일 때만 처리됩니다.

또한 이더리움 개발자가 최소한의 수정으로 클레이튼에 마이그레이션을 쉽게 하기 위하여, 클레이튼에서는 eth namespace API가 이더리움의 트랜잭션 포맷을 사용하여 이더리움과 동일하게 동작하도록 제공됩니다. 서명이나 트랜잭션 해시를 위한 RLP 규칙은 eth namespace와 klay namespace 모두 동일한 규칙이 사용됩니다. 하지만 클레이튼에서는 트랜잭션 타입에 충돌없이 클레이튼과 이더리움의 트랜잭션을 모두 처리하기 위하여 klay namespace에서는 eth namespace와는 다른 Raw Transaction RLP규칙을 적용합니다. 이에 대한 상세한 내용은 Ethereum Tx Type 과 Klaytn New Tx Type에서 설명합니다. 아래에는 API를 사용하는 유형 별로 따라야 하는 RLP 규칙을 설명합니다.

eth namespace API를 사용하는 경우

eth namespace에서는 이더리움에 정의된 트랜잭션의 포맷을 그대로 사용할 수 있도록 API가 제공됩니다. 그렇기 때문에 아래와 같이 서명, 트랜잭션 해시 그리고 Raw Transaction을 생성하는 규칙이 이더리움과 동일합니다.

SigHashRLP = EthereumTransactionType || TransactionPayloadTxHashRLP = EthereumTransactionType || TransactionPayloadRawTx = EthereumTransactionType || TransactionPayload

Geth client, 이더리움 개발 도구(ethers.js, web3.js, Hardhat, Truffle, …) 를 통해 eth namespace API를 사용하는 개발자는 수정없이 이더리움 트랜잭션을 Klaytn에서도 사용할 수 있습니다.

klay namespace API를 사용하는 경우

klay namespace에서도 서명 또는 트랜잭션 해시를 구하는 RLP 규칙은 eth namespace와 동일하게 동작합니다. 하지만 Raw Transaction을 구할 때에는 조금 다른 RLP 규칙이 적용됩니다. 클레이튼에서 제공하는 별도의 타입 구분자 EthereumTxTypeEnvelope가 추가된다는 것을 유의하시기 바랍니다.

SigHashRLP = EthereumTransactionType || TransactionPayloadTxHashRLP = EthereumTransactionType || TransactionPayloadRawTx= EthereumTxTypeEnvelope || EthereumTransactionType || TransactionPayload

Klaytn Client, Klaytn 개발 도구(caver-js, caver-java, …) 를 통해 klay namespace API를 사용하는 개발자는 위의 규칙을 따라서 이더리움 트랜잭션 타입을 사용해야 합니다.

Ethereum Tx Type 과 Klaytn New Tx Type

Klaytn v1.8.0에는 이더리움의 AccessListTxType 과 DynamicFeeTxType 트랜잭션을 지원하기 위해 각각 TxTypeEthereumAccessList 와 TxTypeEthereumDynamicFee 트랜잭션 타입이 추가되었습니다. 아래는 이더리움 트랜잭션 타입과 그에 상응하는 클레이튼 트랜잭션 타입을 나타낸 표입니다.

새로운 클레이튼 트랜잭션 타입은 이더리움과의 호환성을 위해 추가되었기 때문에 사용되는 필드는 이더리움 트랜잭션과 동일하며 서명이나 해시를 위한 RLP 규칙도 동일합니다. 아래는 서명을 위한 해시를 구하는 RLP 규칙으로 이더리움과 클레이튼 트랜잭션 모두 동일한 규칙을 사용하는 것을 확인할 수 있습니다.

// AccessListTxType (Ethereum Tx)
sigRLP = 0x01 || rlp([chainId, nonce, gasPrice, gasLimit, to, value, data, accessList])
sigHash = keccak256(sigRLP)

// TxTypeEthereumAccessList (Klaytn Tx)
sigRLP = 0x01 || rlp([chainId, nonce, gasPrice, gasLimit, to, value, data, accessList])
sigHash = keccak256(sigRLP)

// DynamicFeeTxType (Ethereum Tx)
sigRLP = 0x02 || rlp([chain_id, nonce, max_priority_fee_per_gas, max_fee_per_gas, gas_limit, destination, amount, data, access_list])
sigHash = keccak256(sigRLP)

// TxTypeEthereumDynamicFee (Klaytn Tx)
sigRLP = 0x02 || rlp([chain_id, nonce, max_priority_fee_per_gas, max_fee_per_gas, gas_limit, destination, amount, data, access_list])
sigHash = keccak256(sigRLP)

트랜잭션 해시를 구하는 RLP 규칙도 아래와 같이 이더리움과 클레이튼 트랜잭션 모두 동일한 규칙을 사용합니다.

// AccessListTxType (Ethereum Tx)
TxHashRLP = 0x01 || rlp([chainId, nonce, gasPrice, gasLimit, to, value, data, accessList, signature_y_parity, signature_r, signature_s])
TxHash = keccak256(TxHashRLP)

// TxTypeEthereumAccessList (Klaytn Tx)
TxHashRLP = 0x01 || rlp([chainId, nonce, gasPrice, gasLimit, to, value, data, accessList, signature_y_parity, signature_r, signature_s])
TxHash = keccak256(TxHashRLP)

// DynamicFeeTxType (Ethereum Tx)
TxHashRLP = 0x02 || rlp([chain_id, nonce, max_priority_fee_per_gas, max_fee_per_gas, gas_limit, destination, amount, data, access_list, signature_y_parity, signature_r, signature_s])
TxHash = keccak256(TxHashRLP)

// TxTypeEthereumDynamicFee (Klaytn Tx)
TxHashRLP = 0x02 || rlp([chain_id, nonce, max_priority_fee_per_gas, max_fee_per_gas, gas_limit, destination, amount, data, access_list, signature_y_parity, signature_r, signature_s])
TxHash = keccak256(TxHashRLP)

Raw Transaction은 이더리움과 클레이튼이 다른 규칙을 사용합니다. 클레이튼에서는 이더리움과 클레이튼 트랜잭션을 구분하기 위하여 EthereumTxTypeEnvelope을 Raw Transaction 앞에 추가합니다.

아래는 트랜잭션 타입 표기법에 대해서 정리한 표입니다.

클레이튼에서는 기존에 존재하던 클레이튼만의 트랜잭션 타입과 이더리움 트랜잭션 타입을 구분하기 위해 EthereumTxTypeEnvelope (0x78)를 사용합니다. 이 구분자를 통해 클레이튼 위에서도 이더리움 트랜잭션 타입들을 지속적으로 추가해나갈 수 있게되었습니다.

EthereumTxTypeEnvelope 구분자는 Raw Transaction을 표기할 때도 사용됩니다. 아래와 같이 클레이튼은 이더리움과 달리 Raw Transaction 가장 앞에 구분자를 추가하고 있습니다.(단, eth namespace API에서는 이더리움 동일성 지원을 위해 EthereumTxTypeEnvelope를 생략한채 이더리움과 동일한 형식 사용)

// AccessListTxType (Ethereum Tx)
RawTx = 0x01 || rlp([chainId, nonce, gasPrice, gasLimit, to, value, data, accessList, signature_y_parity, signature_r, signature_s])

// TxTypeEthereumAccessList (Klaytn Tx)
RawTx = EthereumTxTypeEnvelope || 0x01 || rlp([chainId, nonce, gasPrice, gasLimit, to, value, data, accessList, signature_y_parity, signature_r, signature_s])

// DynamicFeeTxType (Ethereum Tx)
RawTx = 0x02 || rlp([chain_id, nonce, max_priority_fee_per_gas, max_fee_per_gas, gas_limit, destination, amount, data, access_list, signature_y_parity, signature_r, signature_s])

// TxTypeEthereumDynamicFee (Klaytn Tx)
RawTx = EthereumTxTypeEnvelope || 0x02 || rlp([chain_id, nonce, max_priority_fee_per_gas, max_fee_per_gas, gas_limit, destination, amount, data, access_list, signature_y_parity, signature_r, signature_s])

여기까지 이더리움의 트랜잭션 타입과 클레이튼의 새로운 트랜잭션 타입에 대한 설명이었습니다. 이 포스팅이 개발자 여러분들이 클레이튼에서 이더리움 트랜잭션을 지원하는 목적과 사용하는 방법에 대해서 이해하는 데 도움이 되셨길 바랍니다. 새로운 트랜잭션 타입에 대한 더욱 자세한 설명은 Klaytn Docs를 참고하시기 바랍니다.

관련 미디엄 포스팅 목록