본문 바로가기
프로그래밍/블록체인

[ethereum] Smart Contract(Lottery 시스템)과 통신해보기 - event & log

by 사바라다 2019. 8. 28.
  1. [ethereum]Ethereum 설치 및 실행
  2. [ethereum]Smart Contract(Lottery 시스템) 제작
  3. [ethereum]ethereum과 통신해보기
  4. [ethereum]Smart Contract(Lottery 시스템)과 통신해보기 - basic
  5. [ethereum]Smart Contract(Lottery 시스템)과 통신해보기 - event & log
  6. [ethereum & java]web3j를 통해 ethereum과 통신해보기
  7. [ethereum & java]Smart Contract(Lottery 시스템)과 web3j를 통해 통신해보기 - basic
  8. Smart Contract(Lottery 시스템)과 web3j를 통해 통신해보기 - event & log

client에서 ehtereum을 통해 정보를 받을 수 있는 방법에는 2가지가 있습니다. 첫번째는 저번시간에 실습해 보았던 get을 이용하여 return값을 직접 받는 것이고 방법입니다. 그리고 두번째는 Event를 통해서 정보를 수신받는 방법입니다.

ethereum은 block에 transaction을 쓰면 해당 transaction에 대해서 Receipt(영수증)를 남깁니다. 이런 Receipt에는 여러정보가 포함되게 됩니다. 이렇게 포함되는 정보중에는 log가 있습니다. 우리는 이 Log를 통해 해당 이더리움으로 들어오는 정보를 알 수 있습니다.

이러한 log는 모니터링하면서 ethereum network로 들어오는 모든 Transaction을 확인할 수 도 있습니다. 대표적으로 etherscan이 그렇게 하고 있지요.

이런 Log에 포함되어 Ethereum Network로부터 정보를 받아 올 수 있는게 Event라고 생각하시면 됩니다.

Event

Events는 solidity readthedocs에서 아래와 같이 정의하고 있습니다.

Solidity events give an abstraction on top of the EVM’s logging functionality. Applications can subscribe and listen to these events through the RPC interface of an Ethereum client.

Events are inheritable members of contracts. When you call them, they cause the arguments to be stored in the transaction’s log - a special data structure in the blockchain. These logs are associated with the address of the contract, are incorporated into the blockchain, and stay there as long as a block is accessible (forever as of the Frontier and Homestead releases, but this might change with Serenity). The Log and its event data is not accessible from within contracts (not even from the contract that created them).

It is possible to request a simple payment verification (SPV) for logs, so if an external entity supplies a contract with such a verification, it can check that the log actually exists inside the blockchain. You have to supply block headers because the contract can only see the last 256 block hashes.

You can add the attribute indexed to up to three parameters which adds them to a special data structure known as “topics” instead of the data part of the log. If you use arrays (including string and bytes) as indexed arguments, its Keccak-256 hash is stored as a topic instead, this is because a topic can only hold a single word (32 bytes).

All parameters without the indexed attribute are ABI-encoded into the data part of the log.

Topics allow you to search for events, for example when filtering a sequence of blocks for certain events. You can also filter events by the address of the contract that emitted the event.

중요한 부분만 요약 하자면

  • Client는 event를 구독하고 정보를 가져올 수 있음
  • Event 메서드를 호출하면 Block Transaction의 log에 저장됨
  • 로그들은 smart contract의 address와 연관이 깊음
  • 해당 block에 접근할 수 있을 때까지 우리는 log를 활용가능
  • indexed 를 붙이면 topics로 처리가 됨, data로 가지 않고.
  • indexed 된 데이터를 제외하고는 ABI-encoded 되어 data에 포함되어짐

이정도가 되겠습니다. 부가적으로 중요한 키워드 몇가지를 우리는 알 수 있었습니다.
event 구독, log, indexed, ABI-encoded 정도가 되겠네요. 아래에서 실제로 어떻게 구현되어지며 사용되어지는지 확인해보도록 하겠습니다.

Smart Contract 소스

pragma solidity >=0.4.21 <0.6.0;

contract Lottery
{
    address payable public owner;
    uint256 private _pot;

    event SETPOT(uint256 _pot);
    event WHO(address indexed caller);

    constructor() public
    {
        owner = msg.sender;
    }

    function getPot() public view returns(uint256)
    {
        return _pot;
    }

    function getNumber(uint256 num) public pure returns(uint256)
    {
        return num;
    }

    function getOwner() public returns(address)
    {
        emit WHO(msg.sender);
        return owner;
    }

    function setPot(uint256 pot) public
    {
        emit SETPOT(pot);
        _pot = pot;
    }
}

위의 Smart-Contract를 처리해보도록 하겠습니다. 위의 예제는 이전 포스팅예제에서 event를 2개 추가한 예제입니다.

json-rpc 프로토콜 설명

ethereum client에서 이번에 사용할 method를 확인해보겠습니다.
전체적인 프로토콜은 아래 링크를 확인하시면 될것이며 저는 요약만 하도록 하겠습니다.

eth_newFilter

이벤트 필터 오브젝트를 생성한다. 그리고 그 필터에 해당하는 Log가 생기면 알려준다. eth_getFilterChange를 통해 변화가 있었는지 주기적인 확인이 필요하다.

eth_getfilterchanges

filter를 위한 polliing method. eth_getfilterchanges는 마지막으로 가져온 log 이후로 발생한 log를 가져온다.

우리는 위의 프로토콜 설명을 통해 flow를 알 수 있었습니다.

  1. eth_newfilter를 통해 topics에 대한 filter를 새로 만든다.
  2. eht_getfilterchanges를 통해 만들어진 filter에 걸린 event를 가져온다.

protocol parameter분석

그럼이제 json-rpc 프로토콜 중 이번에 사용할 protocol에 대해서 알아보도록 하겠습니다.

eth_newFilter

  • parameter

fromBlock: QUANTITY|TAG - (optional, default: "latest") Integer block number, or "latest" for the last mined block or "pending", "earliest" for not yet mined transactions.

toBlock: QUANTITY|TAG - (optional, default: "latest") Integer block number, or "latest" for the last mined block or "pending", "earliest" for not yet mined transactions.

address: DATA|Array, 20 Bytes - (optional) Contract address or a list of addresses from which logs should originate.

topics: Array of DATA, - (optional) Array of 32 Bytes DATA topics. Topics are order-dependent. Each topic can also be an array of DATA with "or" options.

  • return

QUANTITY - A filter id.

eth_newFilter의 parameter와 return 값입니다. 만들 필터의 설정값으로 어떤 블럭부터 볼껀지 (fromBlock), 어디블럭까지 볼것인지(toBlock), 어떤 contract를 볼것인지(address), 어떤 이벤트를 볼것인지(topics)를 지정해 주면 사용할 수 있는 filterid를 반환하는 것을 알 수 있습니다. filterId는 어떤 blockchain network를 사용하느냐에 따라서 값의 형식이 다릅니다.

ex) geth는 Hash값을 가지지만 ganache는 0 숫자부터 올라갑니다.

eth_getfilterchanges

  • parameter

QUANTITY - the filter id.

  • return

Array - Array of log objects, or an empty array if nothing has changed since last poll.

removed: TAG - true when the log was removed, due to a chain reorganization. false if its a valid log.

logIndex: QUANTITY - integer of the log index position in the block. null when its pending log.

transactionIndex: QUANTITY - integer of the transactions index position log was created from. null when its pending log.

transactionHash: DATA, 32 Bytes - hash of the transactions this log was created from. null when its pending log.

blockHash: DATA, 32 Bytes - hash of the block where this log was in. null when its pending. null when its pending log.

blockNumber: QUANTITY - the block number where this log was in. null when its pending. null when its pending log.

address: DATA, 20 Bytes - address from which this log originated.

data: DATA - contains the non-indexed arguments of the log.

topics: Array of DATA - Array of 0 to 4 32 Bytes DATA of indexed log arguments. (In solidity: The first topic is the hash of the signature of the event (e.g. Deposit(address,bytes32,uint256)), except you declared the event with the anonymous specifier.)

eth_getfilterchages는 filterId를 이용하여 해당 filter에 걸린 Event에 대한 Log를 출력해 줍니다. 위의 return값 중에서 많이 다루게 되는것은 indexed Data의 값이 들어있는 topics와 실제 데이터가 있는 data정도 입니다. 그렇다면 실제 한번 테스트를 진행해보도록 하겠습니다.

post 통신으로 확인

테스트는 ganache-cli를 통해서 아래와 같이 진행합니다.

  1. smart contract를 배포한다.
  2. eth_newFilter로 FilterID를 가져온다.
  3. eth_getfilterchanage를 통해 event를 확인해본다.
  4. eth_sendtransaction을 통해 event가 들어있는 function을 실행시켜본다.
  5. eth_getfilterchagne를 통해 event를 확인한다.

먼저, 아까 작성했던 Lottery소스를 배포해보겠습니다.

truffle migrate
⚠️  Important ⚠️
If you're using an HDWalletProvider, it must be Web3 1.0 enabled or your migration will hang.


Starting migrations...
======================
> Network name:    'development'
> Network id:      1566969320969
> Block gas limit: 6721975


1_initial_migration.js
======================

   Deploying 'Migrations'
   ----------------------
   > transaction hash:    0x758fb7ac3ab7da143e0b1396355ff3fa2064572cd01feb91043c36409af52886
   > Blocks: 0            Seconds: 0
   > contract address:    0xC8cbDe94d91791d7Fd2282c04C021271449eFc4D
   > account:             0xF76c9B7012c0A3870801eaAddB93B6352c8893DB
   > balance:             99.99430184
   > gas used:            284908
   > gas price:           20 gwei
   > value sent:          0 ETH
   > total cost:          0.00569816 ETH

   > Saving artifacts
   -------------------------------------
   > Total cost:          0.00569816 ETH


Summary
=======
> Total deployments:   1
> Final cost:          0.00569816 ETH

0xC8cbDe94d91791d7Fd2282c04C021271449eFc4D라는 주소로 smart-contract가 배포되어진것을 확인할 수 있습니다.

smart-contract를 배포했으니 이제 event filter를 만들어보겠습니다.

➜ curl -X POST --data '{"jsonrpc" : "2.0", "method" : "eth_newFilter", "params" :[{"topics": ["0x29AF742A7D5900C031FDC39A763AFB988BDE5C738356676D8C5CF9D76AEECED7"]}],"id" : 73}' localhost:8545 -H "content-type:application/json"

{"jsonrpc":"2.0","id":73,"result":"0x11608b118ce7dea85e9fa15c72b165bc"}

0x2라는 filterId가 반환되었습니다. 이것은 ganache에서 주는 것이며 geth등 다른 ehtereum network는 다릅니다.

이렇게 만든 필터를 이용하여 log를 확인해보도록 하겠습니다.

~ curl -X POST --data '{"jsonrpc" : "2.0", "method" : "eth_getFilterChanges", "params" :["0x11608b118ce7dea85e9fa15c72b165bc"],"id" : 74}' localhost:8545 -H "content-Type:application/json"

{"jsonrpc":"2.0","id":74,"result":[]}

log가 없습니다...? 저희는 transaction을 발생시킨적이 없으므로 당연히 없는겁니다. 이제 transaction을 발생시켜 보겠습니다.

curl -X POST --data '{"jsonrpc":"2.0","method":"eth_sendTransaction","params":[{"from":"0x7d324dbc8fc704881d302da3b264e2243007bdba","to":"0xb763ab5a54892fcbd0a9e49018c2300b1387926a","data":"0x80b5b80c0000000000000000000000000000000000000000000000000000000000000005"}],"id":0,"responseType":"org.blockchain.domain.dto.EthResultVO"}' -H "content-type:application/json" localhost:8545

{"jsonrpc":"2.0","id":0,"result":"0x6677d8dbd05df5e464e8b01b119073069b4755bca86c803e4000bd4cbbbb6a5b"}

getPot에 5의 data를 파라미터로 하게끔 function을 실행시켰으며, 결과값으로 transaction Hash가 넘어옴을 확인할 수 있었습니다.

이제 다시 filter Log를 확인해보겠습니다.

curl -X POST --data '{"jsonrpc" : "2.0", "method" : "eth_getFilterChanges", "params" :["0x11608b118ce7dea85e9fa15c72b165bc"],"id" : 74}' localhost:8545 -H "content-Type:application/json"

{"jsonrpc":"2.0","id":74,"result":[{"address":"0xb763ab5a54892fcbd0a9e49018c2300b1387926a","topics":["0x29af742a7d5900c031fdc39a763afb988bde5c738356676d8c5cf9d76aeeced7"],"data":"0x0000000000000000000000000000000000000000000000000000000000000005","blockNumber":"0x1609","transactionHash":"0x6677d8dbd05df5e464e8b01b119073069b4755bca86c803e4000bd4cbbbb6a5b","transactionIndex":"0x0","blockHash":"0x1751283f79b87486ec53572dc29e973822555fce7622b8d086beb1dcf1c0fc51","logIndex":"0x0","removed":false}]}

이번에는 발생된 로그가 남음을 확인했습니다. 우리는 이 정보를 모니터링하고 있다가 여러 행위를 할 수 있음을 알 수 있었습니다. 정보는 result#data를 보시면 확인하실 수 있습니다.
만약 indexed되어있는 데이터라면 topic에서 확인할 수 있습니다.

오늘은 여기까지 하도록 하겠습니다.

ethereum-client의 transaction의 흐름에 대해서 전반적으로 알아봤으니 다음주부터는 실제로 web3j를 사용하여 ehtereum과 통신, smartContarct와의 통신 등을 진행해보도록 하겠습니다.

감사합니다!

example

https://github.com/KoangHoYeom/Ethereum-With-Java-Ver.easy

 

KoangHoYeom/Ethereum-ClientTest

Ethereum JSON-RPC for study ! Contribute to KoangHoYeom/Ethereum-ClientTest development by creating an account on GitHub.

github.com

 

댓글