안녕하세요. 스마트 컨트렉트 개발자 개발이 체질의 최원혁입니다.
이번 게시글에서 Solidity에서 지갑 주소(EOA : Externally Owned Account)와 컨트렉트 주소(CA : Contract Address)를 구분하여 예외처리하는 방법에 대해 알아보겠습니다.
❓ What is isContract()
Solidity로 구현하는 코드에서 Address type의 변수에 주소를 저장할 때, 지갑주소와 스마트컨트렉트 주소를 구분하여 예외처리가 되도록 코드를 짜야할 때가 있습니다.
https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/Address.sol
해당 예외처리를 하기 위해서 Solidity 언어의 코딩 방법은 Openzeppelin 라이브러리에서 isContract()라는 함수로 잘 구현되었고, 많은 스마트컨트렉트 개발자들이 해당 라이브러리를 가져다가 사용하면서 지갑주소와 컨트렉트 주소를 구분하는 로직을 isContract()라고 불리게 되었습니다.
이번 게시글에서 isContract()를 구현하는 방법에 대해 알아보겠습니다.
🧐 Explain How It Work
EVM의 "주소(address)"는 우리가 트랜잭션을 보내는 개인 소유 주소(EOA : Externally Owned Account)와 스마트컨트렉트 주소(CA : Contract Address) 두가지로 나뉩니다.
EVM의 "주소"는 각 역할이 정해진 4가지 필드(field)를 갖습니다. 그중 code hash라는 필드를 이용하여 isContract() 함수를 구현합니다.
스마트컨트렉트를 베포하게 되면 Solidity로 구현된 코드들은 ABI Encoding을 통해 16진수 덩어리로 변환됩니다. 그리고 컨트렉트 주소(CA)의 code hash라는 코드 저장고에 변환된 코드가 저장됩니다. 하지만 개인 소유 주소(EOA)는 code hash가 비어있습니다.
<address>.code
address.code gives the code stores at the account belonging to address. This could be any address (EOA's will return zero as they don't contain any code). For this, the address needs to exist on the blockchain.
address.code는 address에 속한 계정의 코드 저장소를 제공합니다. 아무 주소나 될 수 있습니다(EOA는 코드를 포함하지 않기 때문에 0을 반환합니다). 이를 위해서는 주소가 블록체인에 존재해야 합니다.
> stackExchang에 누군가 정의해 준 설명 (링크)
Solidity 문서에 나와있는 <address>.code 함수를 사용하면, EVM에 저장된 해당 컨트렉트 주소의 코드 저장소에서 데이터를 불러올 수 있습니다.
assembly { extcodesize(address) }
The following example provides library code to access the code of another contract and load it into a
bytes variable. This is possible with “plain Solidity” too, by using <address>.code. But the point here is that reusable assembly libraries can enhance the Solidity language without a compiler change.
다음 예제는 다른 계약의 코드에 액세스하고 이를 바이트 변수. 이것은 <address>.code를 사용하여 "일반 Solidity"에서도 가능합니다. 그러나 여기서 요점은 재사용 가능한 어셈블리 라이브러리가 컴파일러 변경 없이 Solidity 언어를 향상할 수 있다는 것입니다.
> Solidity 공식문서 (링크)
어셈블리어를 통해 좀 더 향상된 코드로도 사용할 수 있습니다. 공식문서에서 설명하는 것처럼, 기능 자체는 <address>.code 함수와 똑같습니다.
🛠 Example Code
📌 전체 코드는 아래 Github 링크를 참고해 주세요
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;
contract testIsContract {
function isContract1(address account) public view returns (uint) {
return account.code.length;
}
function isContract2(address account) public view returns (uint) {
uint size;
assembly {
size := extcodesize(account)
}
return size;
}
}
컨트렉트 주소(CA) 넣었을 때
개인 소유 주소(EOA) 넣었을 때
위 코드의 함수 파라미터에 CA와 EOA를 넣었을 때, EOA의 코드 저장소에는 아무것도 없지만, CA의 코드저장소에는 abi Encoding으로 변환된 데이터의 길이가 호출된 것을 확인할 수 있습니다.
때문에 isContract() 함수를 구현할 때, 해당 주소(address)의 코드 저장소 데이터 길이가 0보다 크면 주소는 CA라는 사실을 판별하여 CA와 EOA를 구분하는 예외처리를 구현할 수 있습니다.
🚫 isContract()는 EVM 구조 상, 사실 해킹의 요소가 있습니다! 필요한 부분에 잘 사용하면 문제가 없지만, 잘못사용하면 스마트컨트렉트의 취약점이 될 수 있습니다. 해킹 내용과 대책방법은 아래 링크에서 다뤘습니다. isContract()를 사용하신다면 꼭 참고해 주세요!!
지금까지 Solidity에서 지갑 주소(EOA : Externally Owned Account)와 컨트렉트 주소(CA : Contract Address)를 구분하여 예외처리하는 isContract()를 구현하는 방법에 대해 알아봤습니다.
감사합니다.
📝 Example Code GitHub :
https://github.com/imelon2/My-Solidity-Playground/blob/main/Solidity/IsContract.sol
'Block Chain > Solidity' 카테고리의 다른 글
[Solidity] Payable, Fallback and Receive || Solidity 0.8 || (0) | 2023.02.16 |
---|---|
[Solidity] 머클트리 Merkle Tree Root & Proof || Solidity 0.8 | Openzeppelin || (2) | 2023.01.29 |
[Solidity] 비트마스크(BitMask) 연산 || Solidity 0.8 | Operators || (0) | 2023.01.09 |
[Solidity] Array Memory에서 사용하는 방법 || Solidity 0.8 || (0) | 2022.12.17 |
[Solidity] Unchecked || Optimization of gas cost | Solidity 0.8 || (0) | 2022.11.27 |
댓글