Web3.py를 이용해 Ethereum 블록 크롤링

이주현
Hexlant
Published in
9 min readNov 22, 2018
Etherscan

이더리움을 활용하기 위해서 대다수가 javascript 기반의 라이브러리인 web3js를 선택하지만 오늘은 파이썬의 web3.py를 이용한 이더리움 블록의 정보를 활용할수 있는 방법을 알아보겠습니다.

왜 완성도 높은 web3js를 놔두고 굳이 Python의 라이브러리인 web3py를 사용하는 이유는 토큰의 프리세일 페이지를 Django 기반으로 만들었기에 같은 플랫폼 안에서 동작되면 좋겠다는 생각이 들었기 때문입니다

Web3py 설치

현재의 프로젝트에 web3.py를 사용하기 위해 아래와 같은 명령어를 입력하여 설치합니다. (가급적이면 패키지 관리를 위해 virtualenv 설치하면 좋습니다.)

pip install web3

설치가 완료되고 나면 임의의 파일 하나를 생성하여 최신의 블록 정보 가져오는 명령어를 실행합니다. True 값은 모든 트랜잭션을 보여주는 옵션입니다.

>>block = w3.eth.getBlock('latest', True) 

이 명령어를 실행된 결과를 파이썬은 자동으로 Dict 형태로 리턴해 줍니다.

AttributeDict({ 
‘difficulty’:6330977155,
‘extraData’:HexBytes(‘0xd883010812846765746888676f312e31302e34856c696e7578’),
‘gasLimit’:8000000,
‘gasUsed’:7983218,
‘hash’:HexBytes(‘0xe8d9d2b369678e851290c86d0b3d0094f1d44efa1ce1aa76365ddabccbc4157e’),
‘logsBloom’:HexBytes(‘0x00004020000001000000000000000010000000000000000000000000000000000001000000000000000000100000000000000000000000400000000000000000010000000000400000005000000008000000000000000000000000000000000000000020000000800000000000020000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000010000000000000002000000000000000000000000000080000000000100000000000000000100000000000000400000000000000000000000000000000000000000000000000000100000000000000000000020000000000000000000’),
‘miner’:’0x6A9ECfa04e99726eC105517AC7ae1aba550BeA6c’,
‘mixHash’:HexBytes(‘0xde82c28855773ba3ec865e36f8089bf9ba303a9e64c96fe774deda9f329d3050’),
‘nonce’:HexBytes(‘0xfd1ccd1091e645b4’),
‘number’:4475465,
‘parentHash’:HexBytes(‘0xd9cc98d9523797ed67fd28e5339c4c69cf086d9b5d69f7e8e3dfb4939e861079’),
‘receiptsRoot’:HexBytes(‘0x0281d1b88091c151312e4eeecbe9ab60177d981d69dd8cf5661dcc578113f027’),
‘sha3Uncles’:HexBytes(‘0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347’),
‘size’:28674,
‘stateRoot’:HexBytes(‘0xe502dfcb86ed2f07672835069f14e830f3f4c3c5ef4c8ba9957507d1718c42ec’),
‘timestamp’:1542818136,
‘totalDifficulty’:14902459275963265,
‘transactions’:[
HexBytes(‘0x97847ce020451a37d5d56738ac1561e3fda49e1fa6c8cadf8b087ee97ae42e72’),
HexBytes(‘0x3f545f302f2ee0fbd60df7ea31d0a44e105fdd4bf6f7000a862fbb6e92c5cbf4’),
HexBytes(‘0x92076e0dac61c68dd7fb60e97be3d98377912016bac7f34f78ac04b2191d29b4’),
… …
HexBytes(‘0x3a92a62f2cb3dd1e90000f8647ffdfebdbc286aed64da43c32994c9528ba21b0’)
],
‘transactionsRoot’:HexBytes(‘0xed82b10e9ee30b92dc54dff2a3ee4fb6f85d342f8484682af286bec1e1815fb9’),
‘uncles’:[ ]
})

이 결과에서 보셔야 할 부분은 transactions 이 부분입니다

트랜잭션들은 다음과 같은 필드로 구성됩니다.

  • nonce: EOA에 발급되는 트랜잭션 일련번호를 나타냅니다.
  • gas price: 가스의 가격입니다.
  • gas limit: 가스의 최대 사용량입니다.
  • from: 발신자 주소입니다.
  • to: 수신자 주소입니다.
  • value: 수신자에게 보내는 이더(ether) 개수입니다.
  • data: 가변길이의 바이너리 데이터(payload)입니다.
  • v, r, s: ECDSA 서명 구성 요소 입니다.
transactions = block.transactions 
for tx in transactions: # 이 트랜잭션들을 반복하여 데이터를 불러옵니다.
to = tx['to'] # 받는 이더리움의 주소입니다.
value = tx['value'] # 전송한 ether의 wei값입니다.
eth_from = tx['from'] # 보낸 이더리움의 주소입니다.
hash = tx['hash']
print('To : {}'.format(to))
print('From : {}'.format(eth_from))
print('Value : {}'.format(value))
To : 0xBf6F9a2458c4A1cfA3ac650FA45175E114f5A1Fe
From : 0x716393570fD39cfb2eFA88C32A1B2C32E0568a6a
Value : 0
To : 0xd46555572b532618a61DA3c4B3043b8e58386E43
From : 0x72AD19b35C6DCE9d8ee53Fb066B1f77500AAE862
Value : 0
To : 0x695FA0217fdfF2aDC60351c6712a3e87440C3C95
From : 0x568b4aBc7Cfb4649A59856520235e0893ce5D1A3
Value : 0
... ...

간단하게 이더리움의 블록 정보로 해당 블록의 거래내역을 가져오는 로직을 살펴보았습니다.

위와 같은 방법을 이용하여 현재부터 주기적으로 데이터를 가져오려고 한다면:

블록이 생성되는 시간 안에 위와 같이 블록 탐색이 끝날 수 있게 좁은 범위를 자주 탐색하도록 만듭니다. (20초 ~ 1분)

마지막 블록 넘버를 저장하여 마지막 블록으로부터 현재까지의 블록 범위를 구성합니다.

tx_receipt = w3.eth.getTransactionReceipt(hash)
status = tx_receipt['status']
logs = tx_receipt['logs']
if status == 1:
# 로직

이 transactionRecipt 함수는 트랜잭션이 잘 수행되어있는지를 볼 수 있는 체크하는 함수입니다. (이더리움의 영수증)

거래 영수증 항목들입니다.

  • blaockHash: 거래를 어떤 블록에 저장했는지 나타냅니다.
  • blockNumber: 거래를 몇 번째 블록에 저장했는지 나타냅니다.
  • contractAddress: 컨트랙트에 참여한 트랜잭션이라면 컨트랙트가 나타납니다.
  • cumulativeGasUsed: 트랜잭션에서 사용한 가스 사용량입니다.
  • from: 거래 발신자의 주소가 있습니다. 20바이트(160비트) 값입니다.
  • gasUsed: 사용한 가스 사용량입니다.
  • logsBloom: 블록 안에서 출력하는 로그 데이터를 블룸필터 형태로 저장한 것입니다
  • logs: 거래에서 생성된 로그들입니다.
  • root: 상태 트리를 바꾼 후의 상태 루트 값입니다.
  • to: 거래 수신자의 주소가 있습니다.
  • transactionHash: 32바이트의 거래 해시값을 나타냅니다.

영수증의 데이터에서는 Gas가 부족해서 발생한 취소는 판별이 힘듭니다.

이 내역에서는 status가 중요합니다. 이 내역이 성공적으로 처리가 되었다면 1이 반환됩니다.

***2017년 10월 비잔티움 포크전에는 status 필드가 추가 되기전이어서 logs를 보면서 확인했어야 합니다.

이렇게 블록안의 성공한 트랜잭션들의 데이터를 가져오는 실습과 트랜젝션데이터의 내용들을 살펴 보았습니다.

--

--