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

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

by 사바라다 2019. 8. 21.
  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

안녕하세요. 이번주는 Solidity를 이용하여 제작한 Smart Contract와 통신테스트를 진행해보도록 하겠습니다. 통신에는 전 편과 마찬가지로 Json-RPC를 사용합니다.

프로토콜

Ethereum의 Smart Contarct와의 통신을 위해서 eth_calleth_sendTransaction을 사용할 예정입니다.

eth_call은

Executes a new message call immediately without creating a transaction on the block chain.

라고 명시되어 있습니다. 즉, block chain에 기록 없이 사용합니다.

eth_sendtransaction의 경우

Creates new message call transaction or a contract creation, if the data field contains code.

라고 명시되어있습니다. Smart Contract를 실행할 때 block chain에 기록합니다. 이러한 기록은 우리가 나중에 Event(Log) 등을 이용할 때 사용할 수 있습니다.

그럼 이제 본격적으로 통신을 해보도록 하겠습니다.

Smart Contarct 작성

이번주는 Get Method와의 통신을 알아보도록하겠습니다.
먼저 2주차에 만들었던 Smart Contarct를 확인해보겠습니다.
오늘 우리가 이용할 Solidity 소스는 아래와 같습니다.
전체 소스중 필요한 소스만 발췌하였습니다.

pragma solidity >=0.4.21 <0.6.0;

contract Lottery
{
    address payable public owner;
    uint 256 private _pot;

    contructor() 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 view returns(address)
    {
        return owner;
    }

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

오늘 테스트에 필요한 부분만 발췌했습니다. 간단하게 설명드리면

  • getPot() : 저장한 pot 반환
  • getNumber(uint256) : 입력받은 uint256 값 반환
  • getOwner() : Smart Contract Owner의 Address 반환

입니다.

정상적으로 동작하는지 truffle을 통해 Compile & Migrate해서 확인해보도록 합시다. 하는 방법을 모르시는 분은 Smart Contract(Lottery 시스템) 제작을 참조하시기 바랍니다.

Protocol 확인

eth_call의 Protocol은 아래와 같습니다.

Parameters

  1. Object - The transaction call object
  • from: DATA, 20 Bytes - (optional) The address the transaction is sent from.
  • to: DATA, 20 Bytes - The address the transaction is directed to.
  • gas: QUANTITY - (optional) Integer of the gas provided for the transaction execution. eth_call consumes zero gas, but this parameter may be needed by some executions.
  • gasPrice: QUANTITY - (optional) Integer of the gasPrice used for each paid gas
  • value: QUANTITY - (optional) Integer of the value sent with this transaction
  • data: DATA - (optional) Hash of the method signature and encoded parameters. For details see Ethereum Contract ABI
  • QUANTITY|TAG - integer block number, or the string "latest", "earliest" or "pending", see the default block parameter

Returns

DATA - the return value of executed contract.

curl -X POST --data '{"jsonrpc":"2.0","method":"eth_call","params":[{see above}],"id":1}'

이 프로토콜에 맞춰 데이터를 전송해주면 됩니다. 잠깐 프로토콜을 설명 드리자면

  • jsonrpc : json-rpc 2.0을 사용하므로 2.0으로 고정
  • method : 통신에 사용할 기능(method), 여기서는 이더리움과의 통신을 위해 eth_call을 사용
  • params : 이더리움과의 통신에 사용되는 부가적인 데이터들입니다. 저희가 하고자 하는 것은 smart contract와의 상호작용이므로 필수로 사용해야할 파라미터는 아래와 같습니다.
    • to : smart contract 주소
    • data : smart contract에 보내는 데이터

data Parameter 만들기

위의 프로토콜을 분석하여 저희는 어떤 데이터를 ehthereum으로 전송해야하는지 알았습니다. 그리고 data를 제외한 parameter는 알고 있습니다. 그렇다면 data파라미터는 어떻게 만들까요?

순서는 아래와 같습니다.

  1. smart contract의 method를 통해 keccak256해쉬값을 알아낸다.
  2. 그 값중 4byte를 발췌한다.
  3. Input 파라미터가 있다면 각 타입에 맞게 그 값을 Hash 변환하여 붙여준다.
  4. 2번에서 작업한 Hash값과 3번에서 작업한 Hash값을 붙여준다.

위 과정을 통해 data Parameter를 제작할 수 있습니다.
그렇다면 예제를 한번 보겠습니다.

  1. smart contract의 method를 통해 keccak256해쉬값을 알아낸다.

우리가 호출하고자 하는 메서드 리스트 입니다.

  • getPot()
  • getNumber(uint256 num)
  • getOwner()
  • setPot(uint256 pot)

각각 해시펑션으로 변환 시켜줍니다.
파라미터가 있는 method는 형만 잡아줍니다. 즉 getNumber(uint256 num)의 경우 f(getNumber(uint256)) 이렇게 해주시면 됩니다.

  • f(getPot()) -> 0x403c9fa819dce134320f54269a156a8eaa90ceca7b487caad97587293b3fce3f
  • f(getNumber(uint256)) -> 0xfc563658d25534cc7119f04ba172e8b25c6ff5cec285bf1f1335183f167c2f49
  • f(getOwner()) -> 0x893d20e82e7de4dc20ab2c04d17aa0699b89c86c9cd36ed761f937e212e0018a
  • f(setPot(uint256)) -> 0x80b5b80c4cbf8b9faf689bd66aefa0d452dc3ce6f0b2f5f2479fb3ddd627f1b7

저는 변환을 이 사이트를 통해 처리했습니다.

  1. 그 값중 4byte를 발췌한다.

해시펑션을 통해 도출 된 값에서 4Byte를 가져옵니다.

  • getPot -> 0x403c9fa8
  • getNumber -> 0xfc563658
  • geOwner -> 0x893d20e8
  • setPot -> 0x80b5b80c

잘 따라오고 계실거라고 생각합니다.

  1. Input 파라미터가 있다면 각 타입에 맞게 그 값을 Hash 변환하여 붙여준다.

getNumber와 setPot의 경우 현재 uint256 형태의 파라미터를 사용하기 때문에 추가해 줄 필요가 있습니다. ethereum은 32Byte를 기본으로 합니다. keccak256 알고리즘의 결과값도 그렇고 Gas계산을 할때도 32Byte를 기준으로 합니다. Input Parameter를 Data로 만들때도 동일하게 사용한다.

위 예제에서 Uint256은 32Byte 해시값으로 나타내면 된다. 즉, 5를 Input값으로 제공하고 싶다면

0x0000000000000000000000000000000000000000000000000000000000000005

이렇게 추가해주면 된다.

  1. 2번에서 작업한 Hash값과 3번에서 작업한 Hash값을 붙여준다.

이제 만들어진 Hash값인 2번과 3번을 합치면 Data에 들어갈 값이 만들어진다.

getNumber와 setPot의 Input Parameter를 5라고 하면 아래와 같이 만들어진다.

  • getPot -> 0x403c9fa8
  • getNumber -> 0xfc5636580000000000000000000000000000000000000000000000000000000000000005
  • geOwner -> 0x893d20e8
  • setPot -> 0x80b5b80c0000000000000000000000000000000000000000000000000000000000000005

이렇게 만들어진 Data 값을 이용하면 된다.

POST 통신

  • getPot
    curl -X POST --data '{"jsonrpc":"2.0","method":"eth_call","params":["to" : smart contract Address, "data" : "0x403c9fa8"],"id":1}'
  • getNumber
    curl -X POST --data '{"jsonrpc":"2.0","method":"eth_call","params":["to" : smart contract Address, "data" : "0xfc5636580000000000000000000000000000000000000000000000000000000000000005"],"id":1}'
  • getOwner
    curl -X POST --data '{"jsonrpc":"2.0","method":"eth_call","params":["to" : smart contract Address, "data" : "0x893d20e8"],"id":1}'
  • setPot
    curl -X POST --data '{"jsonrpc":"2.0","method":"eth_call","params":["to" : smart contract Address, "data" : "0x80b5b80c0000000000000000000000000000000000000000000000000000000000000005"],"id":1}'

위와 같이 통신하여 결과가 잘 오는지 확인해봅시다.

example

우리는 위의 curl을 통해 POST 통신으로 하던 부분을 이제 Java Code로 변경하면 됩니다. 진행해봅시다.

Link를 보시면 구현한 예제가 있습니다.

감사합니다.

댓글