336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.


지난 포스트에서는 이더리움이 추상적으로 어떻게 동작하는지와 컨트랙트의 생성, 함수 호출에 대해 알아보았습니다. 이번 포스팅에서는 이벤트의 개념과 사용, 블룸 필터 자료구조를 소개하겠습니다. P2P 네트워크 상에는 나와는 무관한 트랜잭션들이 떠돌아(?) 다니기 때문에 내가 관심있는 트랜잭션만 효과적으로 식별하기 위해 블룸 필터를 사용하고, 비트코인에서도 마찬가지의 이유로 사용됩니다.


이벤트

이전 예제에서는 이더를 주고 받을 수 있는 컨트랙트 예제를 설명드렸습니다. 프로그래밍을 하는 관점에서 봤을 때, 스마트 컨트랙트의 출력(output)은 이더입니다. 그러나 이더를 보내는 것은 제한된 용도의 출력입니다. 오늘 설명드릴 내용은 Event라고 하여 스마트 컨트랙트의 다른 출력 수단입니다.


Event(이하 이벤트)란 트랜잭션 내에서 호출될 수 있는 일종의 리턴값이 없는 함수입니다. 이벤트를 호출하면 그 호출한 기록이 Transaction Receipt라 불리는 트랜잭션 결과에 저장됩니다. 일종의 로그(log)입니다. 기술적인 상세는 뒤에서 설명하고 우선 코드 예제를 보겠습니다.



이더를 받는 폴백 함수에서 누가 얼마를 보냈는지를 기록하는 컨트랙트입니다. 지난 시간에 소개한 것과 같이 위와 같이 한 번 배포된 컨트랙트는 내용이 바뀔 수 없습니다. 위의 컨트랙트에 돈을 보내면 태웠다는 로그만 남으며 보낸 이더는 꺼낼 수 있는 방법이 없습니다. 돈이 태워진 것입니다.


위의 로그가 발생했다는 사실은 아래처럼 읽을 수 있습니다. 아래는 web3.js 라이브러리를 사용한 자바스크립트 예제입니다.




코드에 대해 부연설명을 하면 web3.js는 이더리움 노드의 RPC 함수들을 자바스크립트로 제어하기 위한 라이브러리입니다. abi 변수는 컨트랙트의 인터페이스 데이터입니다. 한 번 컴파일되어 배포된 코드와 그 입출력은 결국 ByteArray이기 때문에 그 데이터를 해석할 수 있는 스키마가 필요합니다. ABI에 대한 자세한 내용은 아래 링크에서 확인할 수 있습니다.


https://github.com/ethereum/wiki/wiki/Ethereum-Contract-ABI


저 코드를 이용해서 누가 제일 많이 태웠는지 보여주는 웹 페이지를 만드는 등을 상상해볼 수 있겠죠. :) 그런데 이더리움 네트워크에는 하루에도 수만 개의 트랜잭션이 발생하는데, 이 트랜잭션에서 내가 원하는 이벤트가 발생했다는 사실을 어떻게 알고 콜백을 받을 수 있을까요? 모든 트랜잭션의 로그를 들여다보는 것은 매우 비효율적입니다.


Topic과 Bloom Filter

이벤트 호출을 Topic으로 변환하고 그 토픽을 블룸 필터로 만듭니다. 현재 프로토콜 상 토픽은 4개로 제한되고, 첫 번째 토픽은 이벤트 시그니처를 해시하여 만듭니다.


SHA-3("burned(address,uint)") = 0x0970ce1235167a71...


이렇게 생성된 토픽들을 블룸 필터로 만듭니다.




블룸 필터에 대해 위의 그림 예제로 간단히 설명하겠습니다. x를 삽입할 때 x를 서로 다른 3개의 해시 함수로 얻은 값을 포인터로 사용해 해당 비트를 1로 표시합니다(파란색 화살표). 이렇게 한 뒤 x의 삽입 여부를 알고 싶다면 x를 다시 해시하여 해당 비트가 모두 1인지 여부를 확인하면 됩니다. 물론 해시 충돌로 인해서 False Positive 응답이 나올 수 있습니다.


블룸 필터에 데이터 w가 있었는지 여부를 물으면 대답은 두 가지입니다.

- "w가 있을 수도 있다"

- "w는 없다"


이더리움 노드는 트랜잭션을 실행한 결과를 블룸 필터를 만들고, 수십 개의 트랜잭션을 모아서 블록으로 만들 때에도 각 트랜잭션의 블룸 필터를 모아 하나로 만듭니다. 새로운 블록이 만들어졌을 때 내가 관심있는 이벤트가 발생했는지 여부를 알고 싶으면 해당 이벤트의 Topic을 블록 헤더의 블룸 필터에 조회해보면 됩니다. 만약 없다면 해당 블록에 이벤트가 발생하지 않았음을 바로 알 수 있습니다.


앞서 토픽은 4개로 제한되어 있다고 했습니다. 이벤트 시그니처를 제외한 나머지 토픽은 Indexed Parameter로 지정할 수 있습니다. 위의 예제를 확장하여 다시 코드를 보여드리겠습니다.



파라메터 타입 뒤에 "indexed" 키워드가 추가되었습니다.



web3.js 코드에는 callback 인자 앞에 { amount: 10 } 을 넘겨주고 있습니다. 이는 amount 값이 10인 로그만 콜백으로 받겠다는 의미입니다. 마찬가지로 who 에도 indexed를 주었기 때문에 특정 어카운트가 돈을 태웠는지를 이벤트로 받을 수도 있습니다.


Solidity의 기본형들은 32바이트인 토픽 사이즈 내에 들어오기 때문에 해시를 하지 않고, 배열의 경우 해시값이 토픽으로 들어갑니다. 일반적으로 배열이 토픽이 되는 경우는 없지만, 배열이 토픽으로 들어갈 경우 트랜잭션 결과(Receipt)에 로그가 저장될 때 배열이 복사되는게 아닌 해시값이 들어갑니다. 이 경우 원본 데이터가 날아가는 것은 아닙니다. 해당 트랜잭션을 내 이더리움 노드가 실행하여 재생산할 수 있기 때문입니다.


Topic은 4개로 제한되어 있기 때문에 indexed는 3개까지만 지정할 수 있습니다. 예외적으로 이벤트 시그니처를 Topic으로 취급하지 않는 익명(anonymous) 이벤트는 indexed 파라메터를 4개까지 가질 수 있습니다.


정리

지금까지 Solidity의 이벤트에 대해 다소 자세히 살펴봤습니다. 이더리움 네트워크는 스마트 컨트랙트를 실행하기 위한 단 한 대의 컴퓨터라고 생각할 수 있습니다. 그 컴퓨터의 출력은 이더를 송금하거나, 이벤트를 발생시키는 것입니다. 단 한 대만 존재하는 컴퓨터이기 때문에 이벤트를 무한히 발생시켜서는 안되기 때문에 트랜잭션 실행 비용을 받습니다. 다음 포스팅에선 Gas에 대해 알아보도록 하겠습니다.





+ Recent posts