- [ethereum]Ethereum 설치 및 실행
- [ethereum]Smart Contract(Lottery 시스템) 제작
- [ethereum]ethereum과 통신해보기
- [ethereum]Smart Contract(Lottery 시스템)과 통신해보기 - basic
- [ethereum]Smart Contract(Lottery 시스템)과 통신해보기 - event & log
- [ethereum & java]web3j를 통해 ethereum과 통신해보기
- [ethereum & java]Smart Contract(Lottery 시스템)과 web3j를 통해 통신해보기 - basic
- Smart Contract(Lottery 시스템)과 web3j를 통해 통신해보기 - event & log
안녕하세요. 저번시간에 우리는 web3j 오픈소스를 이용해서 ethereum과 통신해보았습니다. 저번시간에 해봤던 테스트들은 세팅확인용, 또는 계정이 가지고 있는 Balance확인, 계정 정보확인 등 공통으로 사용하는 method들이었습니다. 하지만 우리의 직접 제작한 Smart Contract와 적절한 통신을 할 수 있어야 실무에서 그리고 목표의 완성이라고 할 수 있겠습니다.
오늘은 web3j를 이용하여 Smart Contract와 직접통신할 수 있는 방법에 대해서 알아보도록 하겠습니다.
Web3j Transaction 소개
위 그림은 web3j 공식문서에서 설명하는 trancation에 대한 흐름도입니다. 위의 Ethereum Transaction이 잘 이해가 안되시는 분은 제 포스팅중에 [Ethereum]Ethereum의 Transaction 처리 Life Cycle 분석이라는 포스팅을 한적이 있습니다. 해당 포스팅을 참고하시면 도움이 되실거라고생각됩나다.
web3j는 위와 같은 flow에 따라서 내부 로직을 잘 모르더라도 쉽게 ethereum과 통신할 수 있도록 잘 wrapping되어 있는 오픈소스입니다. 이전 4번째 시간에 post 통신을 통해서 기본적인 transaction을 구현해봤었습니다. web3j는 이런 ethereum의 json-rpc 규약의 잘 지킨 오픈소스이므로 통신하는 원리는 같습니다.
이렇게 web3j의 transaction의 소개를마치고 그럼 직접 구현해보도록 하겠습니다.
Smart Contract
smartContract는 4번째 시간의 코드와 동일합니다.
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;
}
}
이렇게 코드를 작성한 후 compile & migrate를 진행해줍니다. 저는 ganache-cli를 ehtereum network로 사용하며, truffle을 이용해 컴파일과 배포를 진행했습니다.
사용할 1번계정은 0xF76c9B7012c0A3870801eaAddB93B6352c8893DB
, private key는 0x8d22a0aa9c43da157ebc24bc7d70c26d198381e042ab93434757752e3f0ee8e5
, contract 주소는 0x55815f746a53574523296831B33c2277B2760562
이렇게 구성되었습니다. 이제 이 값을 가지고 smartContract를 호출해보도록 하겠습니다.
- 일반적이라면 private key는 공개하면 안됩니다만, ganache-cli는 항상 어느 pc에서 접속하든 동일한 계정과 private key를 반환하기 때문에 공개합니다.
Java Code_get
이번 보여드리는 예제는 sync를 사용하도록 하겠습니다. async와 Rx는 web3j를 통해 ethereum과 통신해보기을 참고하시어 진행해보시면 좋을것 같습니다.
private String from = public String getPot() throws IOException, ExecutionException, InterruptedException {
// 1. 호출하고자 하는 function 세팅[functionName, parameters]
Function function = new Function("getPot",
Collections.emptyList(),
Arrays.asList(new TypeReference<Uint256>() {}));
// 2. ethereum을 function 변수로 통해 호출
return ethereumService.ethCall(function);
}
public String ethCall(Function function) throws IOException {
//3. transaction 제작
Transaction transaction = Transaction.createEthCallTransaction(from, contract,
FunctionEncoder.encode(function));
//4. ethereum 호출후 결과 가져오기
EthCall ethCall = web3j.ethCall(transaction, DefaultBlockParameterName.LATEST).send();
//5. 결과값 decode
List<Type> decode = FunctionReturnDecoder.decode(ethCall.getResult(),
function.getOutputParameters());
System.out.println("ethCall.getResult() = " + ethCall.getResult());
System.out.println("getValue = " + decode.get(0).getValue());
System.out.println("getType = " + decode.get(0).getTypeAsString());
return (String)decode.get(0).getValue();
}
get에 대해서 구현해보았습니다. ethereum에서의 get function은 block에 data를 insert하거나 update하지 않습니다. 그렇기 때문에 별도의 Transaction비용이 발생하지 않습니다. 그리고 결과를 바로 알기 위해서 eth_call을 구현한 ethCall method를 통해서 구현하였습니다. flow를 설명드리겠습니다.
- smartContract안에서 호출할 method의 이름과 parameter를 입력하여 ABI module을 제작한다. 내부 로직에 의해서 어떻게 변화되는지는 Smart Contract(Lottery 시스템)과 통신해보기 - basic 포스팅을 확인해주시면 됩니다.
- ethCall이라는 제작한 method를 호출한다.
- from Address, contract Address, function을 ABI로 encoding한 데이터를 통해 transaction가능한 데이터가 되도록 제작합니다. (이전 포스팅의 json-rpc 형식의 데이터)
- ethereum을 sync로 호출한 후 response를 가져온다.
- ABI encoing되어있는 결과값을 decoding한다.
결과값
ethCall.getResult() = 0x0000000000000000000000000000000000000000000000000000000000000000
getValue = 0
getType = uint256
Java Code_set
public void setPot(int num) throws IOException, ExecutionException, InterruptedException {
// 1. 호출하고자 하는 function 세팅[functionName, parameters]
Function function = new Function("setPot",
Arrays.asList(new Uint256(num)),
Collections.emptyList());
// 2. sendTransaction
String txHash = ethereumService.ethSendTransaction(function);
// 7. getReceipt
TransactionReceipt receipt = ethereumService.getReceipt(txHash);
System.out.println("receipt = " + receipt);
}
public String ethSendTransaction(Function function)
throws IOException, InterruptedException, ExecutionException {
// 3. Account Lock 해제
PersonalUnlockAccount personalUnlockAccount = web3j.personalUnlockAccount(from, pwd).send();
if (personalUnlockAccount.accountUnlocked()) { // unlock 일때
//4. account에 대한 nonce값 가져오기.
EthGetTransactionCount ethGetTransactionCount = web3j.ethGetTransactionCount(
from, DefaultBlockParameterName.LATEST).sendAsync().get();
BigInteger nonce = ethGetTransactionCount.getTransactionCount();
//5. Transaction값 제작
Transaction transaction = Transaction.createFunctionCallTransaction(from, nonce,
Transaction.DEFAULT_GAS,
null, contract,
FunctionEncoder.encode(function));
// 6. ethereum Call
EthSendTransaction ethSendTransaction = web3j.ethSendTransaction(transaction).send();
// transaction에 대한 transaction Hash값 얻기.
String transactionHash = ethSendTransaction.getTransactionHash();
// ledger에 쓰여지기 까지 기다리기.
Thread.sleep(5000);
return transactionHash;
}
else {
throw new PersonalLockException("check ethereum personal Lock");
}
}
public TransactionReceipt getReceipt(String transactionHash) throws IOException {
//8. transaction Hash를 통한 receipt 가져오기.
EthGetTransactionReceipt transactionReceipt = web3j.ethGetTransactionReceipt(transactionHash).send();
if(transactionReceipt.getTransactionReceipt().isPresent())
{
// 9. 결과확인
System.out.println("transactionReceipt.getResult().getContractAddress() = " +
transactionReceipt.getResult());
}
else
{
System.out.println("transaction complete not yet");
}
return transactionReceipt.getResult();
}
set은 ethereum의 block에 data를 쓰는 행위가 있습니다. 이런 행위 자체는 비용이 발생합니다. transaction 비용이 발생하는 건에 대해서는 eth_call이 아닌 eth_sendTransaction을 사용해야할 필요가있습니다. 또한 중복 요청등을 막기위해서 nonce를 이용합니다.
- setPot이라는 function을 호출하기 위한 이에 맞는 ABI encoding어야 합니다. Function은 그 기본이 됩니다.
- 직접 만든 함수를 호출합니다.
- 한 계정에서 비용이 발생하는 transaction을 발생시키기 위해서는 계정을 해제(본인인증)을 할 필요가 있습니다. 따라서
PersonUnlockAccount
를 통하여 unlock을 진행할 필요가 있습니다. - account에 대한 nonce값을 가져옵니다.
- ethereum에 전송할 데이터를 제작합니다.
- ethereum을 호출하고 transaction Hash값을 얻어옵니다. eth_sendTransaction은 바로 결과값을 가져올 수 없습니다. 결과값은 block에 쓰여졌을 때 log의 형태로 가져올 수 있습니다.
- ethSendTransaction method를 통해 호출 해서 반환받은 transaction Hash값을 통하여 Receipt를 얻기위한 method입니다.
- eth_GetTransactionReceipt를 호출합니다.
- transactionHash를 확인합니다. (결과값을 보시면 data가 없는것을 알 수 있습니다. 데이터를 receipt에서 바로 확인하시고 싶으시면, logs가 출력이되어야 합니다. 이것은 event가 호출되었을 경우에만 호출되어집니다. 그렇지 않다면 return값을 가져야 하구요. event는 다음시간에 보겠습니다. )
결과값
transactionReceipt.getResult().getContractAddress() = TransactionReceipt{transactionHash='0xca7c02dfd1c62833f8d77a792508ac71675e8ad1661cf7323500feb721a2f961', transactionIndex='0x0', blockHash='0xa3bcab3137ae976376b52f3304c3f90b9ed5cda5869f5e662f962dc9577cde48', blockNumber='0x3', cumulativeGasUsed='0xa2f5', gasUsed='0xa2f5', contractAddress='null', root='null', status='0x1', from='0xf76c9b7012c0a3870801eaaddb93b6352c8893db', to='0x55815f746a53574523296831b33c2277b2760562', logs=[], logsBloom='0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'}
마무리
오늘은 web3j를 이용하여 ethereum의 smart-contract와 통신을 주고받는 것을 보여드렸습니다.
github에 소스를 올려두었으니 소스를 참조하시기 바랍니다.
solidity 소스
java 소스
감사합니다!
참조
https://docs.web3j.io/transactions/
https://github.com/ethereum/wiki/wiki/JSON-RPC
'기타 > 블록체인' 카테고리의 다른 글
[ethereum & java]Smart Contract(Lottery 시스템)과 web3j를 통해 통신해보기 - event & log (0) | 2019.09.18 |
---|---|
[java & ethereum] web3j를 통해 ethereum과 통신해보기 (0) | 2019.09.02 |
[ethereum] Smart Contract(Lottery 시스템)과 통신해보기 - event & log (0) | 2019.08.28 |
[Ethereum]Ethereum의 Transaction 처리 Life Cycle 분석 (0) | 2019.08.23 |
[ethereum] Smart Contract(Lottery 시스템)과 통신해보기 - basic (2) | 2019.08.21 |
댓글