Transaction Data 살펴보기 (feat. ABI)
Transaction을 구성하는 여러가지 요소들이 있다.
대부분 쉽게 이해할 수 있는 반면, Data라는 친구는 명확하게 어떤 것을 해주는 항목인지 알기 힘들다.
오늘은 Transaction의 Data가 어떤 역할을 하는지 알아보도록 하자.
Transaction의 구성요소
- nonce: 해당 계좌에서 발생된 Transaction의 수
- from: receive address
- to: sender address
- value: 수신자에게 보내는 Ether 수량
- gas limit: 가스의 최대 사용량
- gas price: 가스 가격
- data: 가변길이의 바이너리 데이터
- v, r, s: ECDSA 서명 구성 요소
Transfer Transaction
var transaction = {
nonce: web3.toHex(nonce),
gasPrice: eb3.toHex(web3.toWei(1, 'Gwei').toNumber()),
gasLimit: web3.toHex(100000),
to: '0xe456064545F872B311aE7432689a0fECE90C9a29',
value: web3.toHex(web3.toWei(0.1, 'ether').toNumber()),
data: '0x0'
};
일반적인 송금에서는 data에 아무런 값을 넣어주지 않아도 된다. (않아 보다는 할 필요가 전혀 없다.)
Contract Execution Transaction
var transaction = {
nonce: web3.toHex(nonce),
gasPrice: eb3.toHex(web3.toWei(1, 'Gwei').toNumber()),
gasLimit: web3.toHex(5000000),
to: contractAddress,
value: '0x0',
data: '0x18b0c3fd'
};
Contract Execution Transaction의 경우 receive address가 일반 EA(External Account )가 아닌 CA(Contract Account)인 점
그리고, data에 어떠한 값이 들어간 점이 달라졌다.
Transaction을 '어떠한 명령'을 수행하라는 주문서라고 생각해보자.
Transfer
(1)From이 (2)To에게 (3)Value 만큼을 (4)GasLimit * GasPrice 만큼의 수수료를 내고 수행해달라!
굉장히 단순하고 쉽게 실행이 되지만, Contract Method를 호출하게될 때는 어떤 형대로 진행이 될까?
Contract Execution
(1)From이 (2)Contract에게 (3)Value 만큼을 (4)GasLimit * GasPrice 만큼의 수수료를 내고 (5)Data에 16진수를 수행해달라!
Data에 16진수
16진수라는 데이터를 가지고, 어떤 함수에 어떤 인자값을 넣었는지 어떻게 알 수 있을까?
Solidity docs에서 그 해답을 찾을 수 있다. (링크)
> Contract를 호출 할 때에는 ABI Spec에 맞추어 data를 변환 해야하고, 그 data를 Transaction에 추가한다.
pragma solidity >=0.4.16 <0.7.0;
contract Foo {
function bar(bytes3[2] memory) public pure {}
function baz(uint32 x, bool y) public pure returns (bool r) { r = x > 32 || y; }
function sam(bytes memory, bool, uint[] memory) public pure {}
}
(1) CA(Contract Account)의 Contract Address를 통하여 'Foo' Contract의 접근
(2) 호출하고 싶은 Method ID 입력
> Method ID는 각 function마다 다른 값을 갖을 수 있다.
> the first 4 bytes of the Keccak hash of the ASCII form of the signature
> ASCII 서명 방식의 Keccak해시 값의 처음 4 bytes
> bytes4(keccak256('transferFrom(address,address,uint256)')) 를 통해 얻을 수 있다.
> 정말 말 그대로 해당 함수의 interface를 해시화 하고, 앞의 4 bytes를 가지고 오면 Method ID가 된다.
> Solidity에서는 'selector'라는 Method를 통해 Method ID를 추출할 수 있다. (ex. this.bar.selector)
bytes4(keccak256('function bar(bytes3[2])'))
=> 0xfce353f6
(3) 인자값이 들어가야할 경우 32bytes 형식으로 변환하여 Method ID 뒤에 연결한다.
> ["abc", "def"] 라는 인자를 function bar와 함께 호출하고 싶다면
> abc = 0x6162630000000000000000000000000000000000000000000000000000000000
> def = 0x6465660000000000000000000000000000000000000000000000000000000000
> 이대로 Method ID와 합성을 해주면 된다.
Transaction에 포함시킬 Data
"0xfce353f661626300000000000000000000000000000000000000000000000000000000006465660000000000000000000000000000000000000000000000000000000000"
Argument가 왜 저런 모습의 16진수가 되는건가요?
- 사전에 EVM에서 약속된 ABI Spec에 따라 변환되어야 하기 때문에 그렇다.
> Encoding 규칙에 대해서 나열할 순 없으니 (링크) 를 참조하자!
Etheruem ABI Encoding / Decoding은 다양한 사이트에서 실험해볼 수 있다.
https://adibas03.github.io/online-ethereum-abi-encoder-decoder/#/decode
[추가] Ethereum Contract ABI Converter