ABI (Application Binary Interface) 란
ABI는 블록체인 외부와 Contract 간 상호 작용을 위해 EVM의 Contract와 상호작용하는 표준 방법입니다.
Solidity로 코딩 후 Compile 시키면 위 사진처럼 json형태의 abi 데이터를 얻을 수 있습니다. json의 형태는 사람이 알아볼 수 있는 데이터의 형태입니다. low-level 언어를 사용하는 블록체인 네트워크는 abi.json 형태의 데이터를 해석할 수 없습니다.
때문에 네트워크가 이해할 수 있는 형태로 인코딩을 해야 합니다.
블록체인 네트워크가 이해할 수 있도록 EVM은 ABI를 32바이트로 변환 후, 네트워크로 전달하는 방법을 정의했습니다.
이때 address , uint256 또는 bytes32 같은 정적 타입은 바이트 32로 변환되어 사용하지만,
string 또는 arrays 같은 동적 타입은 Solidity에서 지정한 특정 방식으로 인코딩 됩니다.
abi.encode(...)
Solidity 내장 함수 abi.encode()를 사용하면 모든 데이터 타입을 EVM에서 직접 해석할 수 있는 바이트 형태로 인코딩할 수 있습니다.
정적 타입 (static types)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
contract testAbi {
function encodeUint(uint256 _uint) public pure returns(bytes memory) {
return abi.encode(_uint);
}
function encodeAddress(address _address) public pure returns(bytes memory) {
return abi.encode(_address);
}
function encodeBool(bool _bool) public pure returns(bytes memory) {
return abi.encode(_bool);
}
}
address , uint256 또는 bytes32와 같은 Solidity의 대부분의 정적 유형(static types)은 32바이트 단어로 인코딩 됩니다.
Solidity는 바이트 Padding을 지원하고 있습니다. Padding는 지정한 고정 크기 바이트의 빈 공간을 0으로 채워지는 기능을 뜻합니다.
동적 타입 (Dynamical types)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
contract testAbi {
function encodeString(string calldata _string) public pure returns(bytes memory) {
return abi.encode(_string);
}
}
string과 고정 배열(fixed size array) 같은 동적 타입은 Solidity ABI에서 지정한 특정 방식으로 인코딩 됩니다.
abi.encode("BornToDev")
> // result
0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000009426f726e546f4465760000000000000000000000000000000000000000000000
> // 32 bytes 구분
"0000000000000000000000000000000000000000000000000000000000000020"
"0000000000000000000000000000000000000000000000000000000000000009"
"426f726e546f4465760000000000000000000000000000000000000000000000"
String "BornToDev"를 인코딩하면
"0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000009426f726e546f4465760000000000000000000000000000000000000000000000"
결과가 나오게 되고, 32 바이트(256비트)씩 나누게 되면
"0000000000000000000000000000000000000000000000000000000000000020"(64개)
"0000000000000000000000000000000000000000000000000000000000000009"(64개)
"426f726e546f4465760000000000000000000000000000000000000000000000"(64개)
위와 같은 결과가 나타나게 됩니다.
이때 각 줄마다 다른 의미가 정의되어 있습니다.
- 1st : offset
결과값에서 인코딩 된 문자가 시작되는 인덱스를 나타냅니다.
"0000000000000000000000000000000000000000000000000000000000000020"(0x20)
0x20(16진수) = 32(10진수)
문자열 32번째부터 인코딩 된 값이 시작된다는 뜻입니다.
나중에 나오겠지만 3번째 줄이 그 인코딩 된 값입니다.
바이트 결과값에서 128번이 지나고 인코딩 결과값이 나타나게 되는데, 이는 문자열을 바이트로 변환되어 32번째로 나오는 것입니다.
실제로 문자열 1개는 4바이트를 갖습니다. 문자열 32번째는, 즉 바이트 128번째 이기 때문에, 128인 덱스부터 결과값이 나오는 것입니다.
- 2st : string length
문자열의 길이를 뜻합니다(공백 포함)
"0000000000000000000000000000000000000000000000000000000000000009"(0x09)
0x09(16진수) = 9(10진수)
"BornToDeb"의 Length는 9입니다.
- 3st : encoded string(utf8)
각 문자열들이 utf8 table을 토대로 인코딩 된 결과값입니다.
"426f726e546f4465760000000000000000000000000000000000000000000000"(= "BornToDev")
abi.encodePacked(...)
Solidity는 내장 함수 abi.encodePacked()를 통해 비표준 인코딩 모드를 제공합니다.
이를 통해 ABI에서 지정한 규칙을 따르지 않고 데이터를 원시 바이트 형태로 인코딩할 수 있습니다.
비표준 인코딩은 기존의 ABI 표준 인코딩을 제외합니다.
정적 유형(uint, address, bool...) : padding 지원 x
동적 유형(string, array) : offset, length 표기 제외
abi.encode("BornToDev")
> // result(32 bytes 구분)
"0000000000000000000000000000000000000000000000000000000000000020"
"0000000000000000000000000000000000000000000000000000000000000009"
"426f726e546f4465760000000000000000000000000000000000000000000000"
abi.encodePacked("BornToDev")
> // result
"0x426f726e546f446576"
위 결과값처럼 offset과 length를 표기하던 줄은 없어지고 utf8 인코딩 결과값만 나타내는 걸 알 수 있습니다.
abi.encodeWithSignature(...)
Solidity는 내장 함수 abi.encodeWithSignature()를 통해 외부 계약 호출을 위한 바이트로 변환할 수 있습니다.
callName()이라는 함수를 실행시킬 때, EVM에서는 함수 callName()을 call(ether가 포함될 경우 send)한다고 합니다.
이때 input이라는 공간에
0x96abdf1d00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000009426f726e546f4465760000000000000000000000000000000000000000000000
라는 값을 넣어 함수를 call 하게 됩니다.
이때의 데이터가 바로 ABI를 인코딩하여 EVM이 해석할 수 있게 변환된 데이터입니다.
즉 블록체인 네트워크에 ABI 인코딩 데이터를 보내 작동을 시키는 겁니다.
블록체인에 함수를 실행시키기 위해 보내는 데이터는 Solidity 내 abi.encodeWithSignature() 함수를 통해 만들 수 있습니다.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
contract test {
function callNameABI() public pure returns(bytes memory) {
return abi.encodeWithSignature("callName(string)", "BornToDev");
}
}
callName("BornToDev");
>
"0x96abdf1d"
"0000000000000000000000000000000000000000000000000000000000000020"
"0000000000000000000000000000000000000000000000000000000000000009"
"426f726e546f4465760000000000000000000000000000000000000000000000"
abi.encodeWithSignature("callName(string)","BornToDev")의 결과값은
위 사진에서 call data와 일치합니다.
결과값을 자세히 보시면
"0x96abdf1d"를 제외한 아래 3줄은
abi.encode("BornToDev")와 값이 같은걸 볼 수 있습니다.
바로 ABI 표준에 따라 인코딩 된 값입니다.
"0000000000000000000000000000000000000000000000000000000000000020"
"0000000000000000000000000000000000000000000000000000000000000009"
"426f726e546f4465760000000000000000000000000000000000000000000000"
그리고 첫 번째 줄
"0x96abdf1d"
은 MethodID입니다.
MethodID는 함수와 파라미터를 Keccak hash로 암호화 후, 4 bytes(앞 8글자)를 따로 분류한 값입니다.
Soildity에서
bytes4(keccak256(bytes("callName(string)")) 와 같은 값으로 호출할 수 있습니다.
정리 :
외부 또는 Contract에서 블록체인과 상호작용하기 위해선 명령을 보내야 는데, 명령은 16진수로 된 EVM ABI 인코딩 표준을 통해 구할 수 있습니다.
abi.encodeWithSignature("...")
abi.encodePacked(bytes4(keccak256(bytes("..."))),abi.encode("..."))
abi.encodeWithSignature("...")로 구할 수 있으며,
abi.encodePacked(bytes4(keccak256(bytes("..."))),abi.encode("..."))로 같은 결과값을 도출해 낼 수 있습니다.
ABI는 맨 처음에 명시된 거처럼 외부와 Contract(on-chain)에서 블록체인 네트워크와 상호작용하기 위해 정의된 EVM 표준 중 하나입니다.
Solidity로 작성된 내용이 EVM에서 어떻게 인코딩 되는지에 대해 알아봤습니다.
- https://coinsbench.com/solidity-tutorial-all-about-abi-46da8b517e7
'Block Chain > Solidity' 카테고리의 다른 글
[Solidity] Array Memory에서 사용하는 방법 || Solidity 0.8 || (0) | 2022.12.17 |
---|---|
[Solidity] Unchecked || Optimization of gas cost | Solidity 0.8 || (0) | 2022.11.27 |
[Solidity] Event || emit | indexed | ethers | Solidity 0.8 || (0) | 2022.11.21 |
[Solidity] 가스비(gas fee) 줄이는 코딩 방법 || Solidity 0.8 || KR (0) | 2022.10.06 |
[Solidity] Access Control 구현 || Grant & Revoke Role | Solidity 0.8 || KR (2) | 2022.10.01 |
댓글