[연재] 이더스캔(etherscan.io)을 만들어 보자 - web3.js로 트랜잭션 정보 저장

이더스캔(etherscan.io)을 만들어 보자 - web3.js로 트랜잭션 정보 저장


블럭 정보를 저장하였다면 다음으로 블럭 내에 있는 트랜잭션 정보를 저장해보자.

트랜잭션을 저장하는 방식은 크게 두 가지이다.

첫번째는 블럭을 저장할 때 함께 저장하는 방식과 둘째는 트랜잭션만 따로 저장하는 방식이다.

블럭과 트랜잭션은 1 대 다의 관계이다.

블럭과 트랜잭션의 관계는 관계형 데이터 베이스에 있는 테이블 식으로 표현하자만 1 대 0..*의 관계다.
블럭 안에는 다수의 트랜잭션이 들어 있다. 사실 블럭이라는 것이 발생한 트랜잭션을 묶어서 블럭을 만든 것이기 때문이다.

메인넷 같이 트랜잭션이 많은 경우 모든 블럭에 트랜잭션이 들어 있지만, 사설 네트웍의 경우에는 트랜잭션이 들어 있지 않는 블럭도 존재한다.


블럭을 저장할 때 함께 저장하는 방식

블럭과 달리 트랜잭션은 번호가 아닌 해쉬값으로 조회한다.
따라서 0번 블럭부터 블럭을 순회할 때 블럭 내에 들어 있는 트랜잭션 해쉬 값으로 트랜잭션 정보를 읽어온다.

앞 연재 블럭정보 저장에서 블럭 정보를 저장하는 코드를 다시 보자.

 web3.eth.getBlock(blockHashOrBlockNumber [, returnTransactionObjects] [, callback])

출처: https://github.com/ethereum/wiki/wiki/JavaScript-API#web3ethgetblock


언급했듯이 returnTransactionObjects는 트랜잭션 정보를 함께 읽어 올것인지, 아니면 해쉬값만 읽어 올 것인지 여부에 대한 파라미터이다.

true, false 각각에 대해 알아보자.


1. returnTransactionObjects = true 일때

returnTransactionObjects 값이 true일 때는 블럭 정보를 읽어올 때 transaction 정보도 함께 읽어 배열로 넘어 오기 때문에 이를 루핑 돌며 바로 DB에 저장하면 된다.

web3.eth.getBlock(i, true, function(err, block) {
    # TODO: DB에 block insert하는 코드
    block.transaction.forEach(function(tx){
        # TODO: DB에 transaction insert하는 코드
    });
});

가장 간단한 방법이다.


2.returnTransactionObjects = false 일때

false 일 때는 트랜잭션 해쉬값만 넘어오기 때문에 해쉬값으로 트랜잭션을 읽어오는 코드를 추가하여야 한다.

web3.eth.getBlock(i, false, function(err, block) {
    # TODO: DB에 block insert하는 코드
    block.transaction.forEach(function(tx_hash){
        web3.eth.getTransaction(tx_hash,function(err,tx){
            # TODO: DB에 transaction insert하는 코드
        });
    });
});

web3.eth.getTransaction 함수는 트랜잭션 해쉬값으로 트랜잭션 정보를 읽어오는 함수이다.

web3.eth.getTransaction(transactionHash [, callback])

출처: https://github.com/ethereum/wiki/wiki/JavaScript-API#web3ethgettransaction


따로 순회하며 트랜잭션만 저장하는 방식

다음은 블럭을 순회하는 다른 방식을 소개하고자 한다.

블럭 정보를 저장하면서, 트랜잭션 정보를 입력하는 방식은 위와 같이 하면 되지만, 별도로 트랜잭션만 저장할 것이라면 트랜잭션이 들어 있는 블럭만 순회하며 트랜잭션 정보를 저장할 수 있다.

이 때 사용하는 함수가 바로 web3.eth.getBlockTransactionCount이다.

web3.eth.getBlockTransactionCount(hashStringOrBlockNumber [, callback])

출처: https://github.com/ethereum/wiki/wiki/JavaScript-API#web3ethgetblocktransactioncount

이 함수도 getBlock과 같이 첫번째 파라미터가 번호 또는 해쉬값이다.

따라서 0번 블럭부터 현재블럭까지 순회하며 트랜잭션이 몇 개 들어 있는지 확인할 수 있다.

web3.eth.getBlockNumber(function(err, rtn) {
    var latest_block_number = rtn;

    for(var i=0; i <= latest_block_number; i++){
        web3.eth.getBlockTransactionCount(i, true, function(err, cnt) {
......
        });
    }
});

함수 리턴 값인 cnt에는 트랜잭션 개수가 들어 오는데, 0 이상인 경우에만 저장하면 된다. 코드를 완성하면 다음과 같다.

web3.eth.getBlockNumber(function(err, rtn) {
    var latest_block_number = rtn;
    for(var i=0; i <= latest_block_number; i++){
        web3.eth.getBlockTransactionCount(i, true, function(err, cnt) {
            if(ctn > 0){
                for(var j=0; j < cnt; j++){
                    web3.eth.getTransactionFromBlock(i,j,function(err,tx){
                        # TODO: DB에 transaction insert하는 코드
                    });
                }
            }
        });
    }
});

getTransactionFromBlock 함수는 특정 블럭, 특정 트랜잭션을 참고할 때 사용하는 함수이다.

getTransactionFromBlock(hashStringOrNumber, indexNumber [, callback])

출처: https://github.com/ethereum/wiki/wiki/JavaScript-API#web3ethgettransactionfromblock

이렇게 해서 읽어오면 트랜잭션 정보가 들어오느데 참고로 다음과 비슷한 형식일 것이다.

{
  "blockHash": "0x7eb38eae313ca43220d17c9ca6e120b6ea5accd41d69db51b89d9d882c851716",
  "blockNumber": 144643,
  "from": "0x0a5476a467ae58155f39bc30c1d2d3f85d2c7ffc",
  "gas": 90000,
  "gasPrice": "1800000000000",
  "hash": "0x86f62b2df4e8d806c83fa3cfcc31741f092e755b9a817eb0b97eefa996b7ebc4",
  "input": "0xa9059cbb000000000000000000000000294b6878c6ba4e6fd1496949ebc8de28b2ff93e8000000000000000000000000000000000000000000000000000000000000000a",
  "nonce": 2082,
  "r": "0x2e81823cecaf1b546b8475e132a03f2fae6eded77410ef52b675247907c0c527",
  "s": "0x30900d2e5aa27287f3899598c1acdb308a65e15a26336c11f2d75dd98688871d",
  "to": "0x422d66e796a24d7aaa228523dfd5f1f4d3381b4a",
  "transactionIndex": 0,
  "v": "0xd3",
  "value": "0"
}

참고로 gasPrice와 value는 Big Number로 넘어오기 때문에 다음과 같이 10진수로 변환하여 DB에 입력해야 한다.

tx.gasPrice.toString(10)


결론

우리는 여기서 트랜잭션을 저장하는 두가지 방식에 대해 살펴보았다.

첫번째 블럭을 저장할 때 저장하는 방식과, 둘째 따로 트랜잭션이 있는 블럭만 저장하는 방식.

다양한 함수들을 살펴 보았고, 어떻게 사용하는 지도 알아보았기 때문에 각자 상황에 맞게 응용하면 될 것이다.