[BlockChain]이더리움 블록체인_web3.js 함수호출하기

3 minute read

이번 포스팅은 스마트컨트랙트의 기능을 단순 Call이 아니라 직접 Parameter를 넘겨서 Function Call을 할 수 있도록 하겠습니다.

즉 스마트컨트랙트의 변수 값에 직접적으로 접근하여 변경시키는 것이죠. 이는 트랙잭션을 일으켜야 합니다.(Set 작업)

그러나 저번 스마트컨트랙트에서와 마찬가지로 한가지 문제점은 스마트컨트랙트 트랜잭션 해쉬값을 어떻게 관리해야 된다는 점입니다.

앞선 포스팅에서는 Contract 변수값을 고정값으로 넣었지만 매번 이렇게 할 수는 없으니 이 값을 앞으로 Mysql을 통해서 관리하여 스마트컨트랙트를 언제든지 호출할 수 있도록 하였습니다.
(다양한 방법이 존재하니, 꼭 관계형 데이터베이스를 사용할 필요는 없습니다.)

MySQL

우리가 흔히 아는 관계형 데이터베이스입니다. Mysql은 단순 트랜잭션만 저장하는 용도이기 때문에 간단하게 테이블을 구성하고 이를 트랜잭션을 저장하도록 하겠습니다.리눅스에서 Mysql을 설치하는 것은 크게 어렵지 않습니다.

$ sudo apt-get update
$ sudo apt-get mysql-server
$ npm install mysql

위 명령어를 차례대로 치면 mysql은 차례대로 설치가 됩니다.
정상적으로 설치가 되었다면 mysql -uroot -p 라는 명령어를 치면 mysql Server에 접속할 수 있습니다. (단 설치시 비밀번호를 설정하게 되는데 이때 mysql server에 접속하는 비밀번호와 동일하게 적용됩니다.)

정상적으로 설치가 되었다면 blockchain이라는 database를 생성해봅시다.

CREATE DATABASES blockchain CHARACTER SET utf8 COLLATE utf8_general_ci;

Mysql Database Create

Image Alt 텍스트

CREATE TABLE `tx_hash` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `contract_address` varchar(100) NOT NULL,
  PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

그리고 SQL명령어 문을 실행시켜 TX_HASH를 생성합니다. TX_HASH는 스마트컨트랙트를 실행시킨 주소값을 저장할 것입니다. 테이블을 생성한 뒤 contract_address값을 Insert하면 해당 데이터가 이상없이 들어간 것을 확인 할 수 있습니다.

Mysql Table Select

Image Alt 텍스트

var conn = mysql.createConnection({
  host     : 'localhost',
  user     : 'root',
  password : 'password',
  database : 'blockchain'
});
conn.connect();

소스코드에 해당 mysql 모듈을 이용하여 Database의 Connection을 맺습니다.

스마트컨트랙트 함수 실행

이전 포스팅에서는 스마트컨트랙트 return 함수를 단순히 Call 했기 때문에 객체를 생성하고 Method를 Call하면 실행이 됬었습니다.(간접적인 함수호출)=> Web3를 이용한 스마트컨트랙트 실행방법

스마트컨트랙트의 함수에 변수를 전달하고 트랜잭션을 실행시킨 후 블록체인에 해당 변수가 잘 전달됬는지 확인하는 방법에 대해서 알아봅시다.

스마트컨트랙트에 setContract라는 함수를 추가하여 이 함수에 변수값이 들어오면 컨트랙트 내부에 있는 변수값에 할당하도록 하였습니다.

Database에 저장된 Contract_address를 가지고 실행시킨 코드입니다. 즉 contract_address를 따로 관리하여 스마트컨트랙트를 작동시키는 것이죠.

txObject에 넣는 데이터가 다르다는 것을 확인할 수 있습니다. data에는 해당 함수를 실행시킬 Function Code를 작성하고 이를 encodeABI()로 변환해주어야 합니다. to에는 Contract Address가 다시한번 들어가게 되는데 그 이유는 스마트컨트랙트의 주소값 넘겨주어야 이더리움 블록체인에서 이 값을 통해 확인 할 수 있기 때문입니다. 이해하기 어렵다면 한 번 실행하고 그 결과 값을 보도록 하겠습니다.

contract_address가 이상없이 Database에서 호출된 것을 확인할 수 있으며, Smart Contract 트랜잭션 처리를 통해 Hello, World에서 SmartContract Hello으로 정상적으로 바뀐 것을 확인 할 수 있습니다.

이처럼 SmartContract에 직접적으로 접근하면 Sign을 통해 트랜잭션을 일으켜야 합니다. 간접적인 접근은 단순히 Contract Address만 있다면 Web3를 이용해서 쉽게 호출이 가능하죠.

var express = require('express');
var app = express();
var Web3 = require('web3');
var web3 = new Web3(new Web3.providers.WebsocketProvider('wss://ropsten.infura.io/ws'));
var Tx = require('ethereumjs-tx');

var mysql = require('mysql');

const send_account    = "0x82Ff486364ebDbEe1b34b7538ceD72448d9b38cb";
const receive_account = "0xf2a58d3F268642f7f5416F4248f4Fb2623a5D929";
const privateKey = Buffer.from('privateKey', 'hex');

var conn = mysql.createConnection({
  host     : 'localhost',
  user     : 'root',
  password : 'password',
  database : 'blockchain'
});
conn.connect();


var solc = require('solc');
var fs = require('fs'); //file 시스템

app.get('/smartcontract', function(req,res){
  //File Read
  var id = 1;
  var source = fs.readFileSync("./contracts/HelloWorld.sol", "utf8");

  console.log('transaction...compiling contract .....');
  let compiledContract = solc.compile(source);
  console.log('done!!' + compiledContract);

  var bytecode = '';
  var abi = '';
  for (let contractName in compiledContract.contracts) {
     // code and ABI that are needed by web3
     abi = JSON.parse(compiledContract.contracts[contractName].interface);
     bytecode = compiledContract.contracts[contractName].bytecode; //컨트랙트 생성시 바이트코드로 등록
     // contjson파일을 저장
  }
  console.log(abi);

  var sql = `SELECT contract_address
             FROM tx_hash WHERE id = ?`;
  conn.query(sql, [id], function(err, contract){
    console.log('Contract Address',  contract[0].contract_address);

    const MyContract = new web3.eth.Contract(abi,contract[0].contract_address);

    MyContract.methods.say().call().then(result => console.log("SmartContract Call: " + result));

    web3.eth.getTransactionCount(send_account, (err, txCount) => {

    const txObject = {
      nonce:    web3.utils.toHex(txCount),
      gasLimit: web3.utils.toHex(1000000), // Raise the gas limit to a much higher amount
      gasPrice: web3.utils.toHex(web3.utils.toWei('10', 'gwei')),
      to : contract[0].contract_address,
      data : MyContract.methods.setcontract("SmartContract Hello").encodeABI()
    };

    const tx = new Tx(txObject);
    tx.sign(privateKey);

    const serializedTx = tx.serialize();
    const raw = '0x' + serializedTx.toString('hex');

    web3.eth.sendSignedTransaction(raw)
       .once('transactionHash', (hash) => {
         console.info('transactionHash', 'https://ropsten.etherscan.io/tx/' + hash);
       })
       .once('receipt', (receipt) => {
         console.info('receipt', receipt);
         MyContract.methods.say().call().then(result => console.log("SmartContract Call: " + result));
      }).on('error', console.error);
    });
  });

});

//app을 listen
app.listen(4000, function(){
  console.log('Connected BlockChain, 4000 port!');
})