테스트넷 스마트컨트렉트와 NodeJS 연동(2)


2. Geth — Ropsten testnet

아래에서 정보를 확인하실 수 있습니다.
https://github.com/ethereum/ropsten

Geth 사용이 숙달이 되었다면 테스트넷 노드에 참여하는 것은 간단합니다.
아래와 같이 부트노드를 추가하면 됩니다.

geth — testnet removedb
geth — testnet — fast — bootnodes “enode://6332792c4a00e3e4ee0926ed89e0d27ef985424d97b6a45bf0f23e51f0dcb5e66b875777506458aea7af6f9e4ffb69f43f3778ee73c81ed9d34c51c4b16b0b0f@52.232.243.152:30303,enode://94c15d1b9e2fe7ce56e458b9a3b672ef11894ddedd0c6f247e0f1d3487f52b66208fb4aeb8179fce6e3a749ea93ed147c37976d67af557508d199d9594c35f09@192.81.208.223:30303”

여기서 자신이 사용하는 옵션을 추가하면 됩니다. 
예를들면 아래와 같이 추가할 수 있습니다.

geth — testnet — fast — bootnodes “enode://20c9ad97c081d63397d7b685a412227a40e23c8bdc6688c6f37e97cfbc22d2b4d1db1510d8f61e6a8866ad7f0e17c02b14182d37ea7c3c8b9c2683aeb6b733a1@52.169.14.227:30303,enode://6ce05930c72abc632c58e2e4324f7c7ea478cec0ed4fa2528982cf34483094e9cbc9216e7aa349691242576d552a2a56aaeae426c5303ded677ce455ba1acd9d@13.84.180.240:30303” — etherbase 0xc0383c2332eFEc00ed99e4dA93Cb8A11c88D58d3 — mine — minerthreads 1 — ws — wsaddr “localhost” — wsport 8546 — wsorigins “*” — rpc — rpcaddr “localhost” — rpcport 8545 — rpccorsdomain “*” — rpcapi “admin,db,eth,debug,miner,net,shh,txpol,personal,web3” console 2>> /path/to/geth_testnet.log

테스트넷을 처음 구동한다면 기존 데이터를 동기화 해야 합니다. 동기화에는 몇시간 이상 소요될 수 있습니다. mine 옵션을 켠다면 가끔 이더를 채굴할 수 있습니다.


3.0 Node 코딩에 들어가기에 앞서….
어쩌면 기초적이라고 할 수 있는 내용을 짚고 넘어가겠습니다.
언급하고자 하는 내용은 Stream 에 관련한 내용입니다.

블록체인은 어떤 관점에서 본다면 거대한 Stream 일 수 있습니다.
그러면 다시, Stream 은 무엇인가?
Stream 은 0과 1이 나열된 연속된 stream 입니다.
때문에 스트림을 이해한다면 메모리, 디스크, 통신, 영상 등등을 이해한다고 볼 수 있을 겁니다.
컴퓨터의 정보나 명령은 모든것이 Stream 으로 구성되어 있다고 말할 수 있습니다.
그런데 하나 짚고 넘어가야 할 것이 있습니다.
연속된 일련의 0과 1의 나열속에 어떤 정보가 무엇인지 어떻게 판별할 수 있을까요?

예를들어 스트림에 0x00000064 값이 있다고 합시다.
이 값을 32비트 컴퓨터에서 읽어 들이는 방식에 따라 다음과 같이 해석될 수 있습니다. 
정수 : 100
부동 소수점 1.401298e-43 
ASCII : d
ARGB : (0,0,0,100)
혹은 어떤 Instruction 의 일부분일 수도 있을 겁니다.

즉, Stream은 읽어들이는 정보의 방식에 따라 해석이 달리 될 수 있습니다.
쉬운 예이지만 실전으로 넘어간다면 BMP 등의 file 헤더를 기록하거나 읽을때 규약에 명시된 정보의 순서로 읽어들이면 됩니다.
BMP 파일 구조 : https://en.wikipedia.org/wiki/BMP_file_format
이외에도 국제 표준에 따른 통신 코딩등을 할 경우에도 위의 개념으로 이루어 집니다.
예를들어 TV에서 HD 방송을 수신할 경우에도 국제 표준에 명시된 대로 stream 을 읽어들여 화면에 표시하는 것입니다.
만일 아직 한번도 파일을 열어 stream 을 분석해 보지 않았다면 열어서 한번 분석해 보시는 것도 이해에 많은 도움이 될 겁니다. 말로만 들었던 컴퓨터에서 역워드로 저장하는 것을 실제로 볼 수도 있을 겁니다. 그러면 통신에서 byte order 체크하는 과정이 있어야 하는 것도 이해 하실수 있을 겁니다.

자, 그러면 블록체인에서는 이것이 어떻게 적용되는지 보겠습니다.
기존 컨트렉트를 읽어들일때 다음의 정보가 필요합니다.

Contract를 읽기위해서 요구하는 것은 두가지 입니다. 한개는 Contract의 주소값이고 나머지 한개는 눈치채셨겠지만…
그렇습니다. 
해당 데이터를 어떻게 읽어야 하는지의 구성 정보입니다.

remix ide 로 돌아가서 details 버튼을 눌러 정보를 보시면 ABI 정보가 있을 겁니다.

[
{
“constant”: false,
“inputs”: [
{
“name”: “s”,
“type”: “string”
}
],
“name”: “setString”,
“outputs”: [],
“payable”: false,
“stateMutability”: “nonpayable”,
“type”: “function”
},
….
]

Foo Contract는 위와 같이 정보가 구성되어 있어 어떻게 스트림을 읽어야 하는지 명시되어 있습니다.

자 그렇다면 여기서 문제.
ABI를 다른 contract의 ABI로 교체하면 읽을 수 있을까요?

당연히 읽는데는 문제가 없습니다.

하지만 해당 정보로는 정보가 맞지않아 동작이 되지 않거나 잘못된 정보가 표시될 겁니다.

3. NodeJS 코드작성

node web3는 아래 링크에서 참고하실 수 있습니다.
https://github.com/ethereum/web3.js/

여기서는 한가지 정도만 주의하면 됩니다. 
그것은 TestNet에서 이벤트를 수신하려면 HttpProvider 대신 WebsocketProvider 를 사용해야 한다는 것입니다.

아래와 같이 소켓 통신으로 통신하면 됩니다. 만일 Node 서버와 Geth 서버가 다르다면 Geth 서버의 IP 주소를 적어주시면 됩니다.

Node 코드에서도 마찬가지로 주소의 값과 ABI 정보를 통해 정보를 해석하고 있는 것을 볼 수 있습니다.

var Web3 = require(‘web3’);
var web3 = new Web3(new Web3.providers.WebsocketProvider(“http://localhost:8546"));
const MOGAO_WALLET_ADDRESS = ‘<<address>>’;
const FOO_CONTRACT_ADDRESS = “<<contract address>>”;

const DEFAULT_GAS_PRICE = ‘20000000000’;
var _abiContract = [
{ … }
];
// 서버에서는 스마트컨트랙트의 인스턴스를 많이 사용하므로 스마트 컨트렉트 인스턴스를 저장하고 있습니다.
var _g_instanceMGOFooContract = new web3.eth.Contract(_abiCrowdSales, FOO_CONTRACT_ADDRESS, {
from: MOGAO_WALLET_ADDRESS,
gasPrice: DEFAULT_GAS_PRICE
});
////////////////////////////////////////////////////////////////////
// Required Modules
////////////////////////////////////////////////////////////////////
module.exports.route = function(app)
{
app.get (‘/api/blockchain/info’, getInfo );
app.set (‘/api/blockchain/info’, setInfo );
};
function getInfo(request, response)
{
"use strict";
try 
{
// 여기서 정보를 수신할 경우 string 으로 정보가 넘어옵니다.
async.parallel
(
{
getString : (callback)=>{
_g_instanceMGOFooContract.methods.startTime().call( (err, result)=>{
if( err )
return callback(err);
callback(null, parseInt(result));
});
},
getInteger : (callback)=> {
_g_instanceMGOFooContract.methods.deadline().call( (err, result)=>{
if( err )
return callback(err);
callback(null, parseInt(result));
} );
}
}, // end jobs
function onResult(err, results) {
if( err )
utils.defaultErrorProcess(response, err);
else
utils.responseMessage( response, results );
} // end onResult()
); // end async()
} // end try
catch(err)
{
return utils.defaultErrorProcess( response, err );
}
}
function setInfo(request, response)
{
"use strict";
try 
{
// check body values and assign new string
CheckValues(request.body.newString);
var newString = request.body.newString;

// if valid data....
_g_instanceMGOFooContract.methods.setString( newString ).call( (err, result)=>{
utils.responseMessage( response );
});
}
catch(err)
{
return utils.defaultErrorProcess( response, err );
}
}
var event = _g_instanceMGOFooContract.events.StringChanged({ fromBlock: 'latest' });
event.on("data", (event)=>{
console.log("=========data==============");    
console.log(event);
console.log("=========returnValues==============");    
console.log(event.returnValues);
console.log("=========values==============");    
console.log(event.returnValues.newstring);
} )
Like what you read? Give Team Mogao a round of applause.

From a quick cheer to a standing ovation, clap to show how much you enjoyed this story.