Blockchain

#20 개인 네트워크 만들기 (geth)

Sila 2022. 7. 3. 13:00

 

오늘은 geth의 기능중 하나인 puppeth을 이용해 개인 네트워크를 셋업하고 실행해본 후, 

 

이와 IPC 통신, RPC 통신을 통해 상호작용을 하는 방식에 대해 알아보자.

 

 

1. 네트워크 셋업 (puppeth 이용)

puppeth은 네트워크를 셋업해주는데 도움을 주는 툴이다.

 

git에서 clone을 받아온 디렉토리의 go-ethereum 폴더에서 make all을 입력하면 /build/bin 폴드안에

 

go-etherum이 제공하는 기능을 가진 모든 파일들이 생성된다.

 

 

이제 다시 내가 작업을 시작할 디렉토리로 돌아와 다음과 같이 genesis.json 파일을 생성하고 계정을 하나 만든 후,

 

puppeth을 입력해 네트워크 초기값을 설정한다.

 

/*  genesis.json  */

{
    "config": {
      "chainId": 7722,
      "homesteadBlock": 0,
      "eip150Block": 0,
      "eip150Hash": "0x0000000000000000000000000000000000000000000000000000000000000000",
      "eip155Block": 0,
      "eip158Block": 0,
      "byzantiumBlock": 0,
      "constantinopleBlock": 0,
      "petersburgBlock": 0,
      "istanbulBlock": 0,
      "ethash": {}
    },
    "nonce": "0x0",
    "timestamp": "0x62bd7dc2",
    "extraData": "0x0000000000000000000000000000000000000000000000000000000000000000",
    "gasLimit": "0x47b760",
    "difficulty": "0x80000",
    "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
    "coinbase": "0x0000000000000000000000000000000000000000",
    "alloc": {
      "7188630a24591fdb0218d94b53a80091fa5507b8": {
        "balance": "0x200000000000000000000000000000000000000000000000000000000000000"
      },
      "aaa3e898bd61216387edb4f878ae0e1f712cbef2": {
        "balance": "0x200000000000000000000000000000000000000000000000000000000000000"
      },
      "be7388f4d2f72ad4abf49355c920152365ca0594": {
        "balance": "0x200000000000000000000000000000000000000000000000000000000000000"
      }
    },
    "number": "0x0",
    "gasUsed": "0x0",
    "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
    "baseFeePerGas": null
  }

 

geth --datadir node account new

// 2cf64427a1067cbb20552ca477ba9b633a1cd333

 

 

우선 네트워크 이름을 정하고, 새로운 제네시스 블록을 만드는 것에 대한 셋업이다.

 

account는 방금 생성한 주소를 붙여넣기하면 된다. networkId는 알아서 정해주면 된다.

 

여기까지 하면 새로운 제네시스 블록에 대한 설정이 완료된다.

 

 

생성한 제네시스 블록을 이용해 네트워크 설정을 완료하고 마지막에 폴더명을 지정하면 폴더 안에

 

json 파일들이 생성될 것이다.

 

혹시 여기까지 끝났을 때 똑같은 질문이 다시 뜨면 터미널을 닫고 다시 열면 된다.

 

이제 본격적으로 네트워크를 구동해보자.

 

 

2. 네트워크 구동

이제 셋업된 네트워크를 구동해보자.

 

geth --datadir node init ./prac_/prac.json
// 네트워크 초기화

geth --datadir node --ipcpath "ipc path 입력"
// --ipcpath없이도 문제 없디 실행되면 --ipcpath 부터는 입력하지 않아도 된다.

 

네트워크가 실행되면 터미널을 하나 더 열고 geth attach를 실행한다.

 

여러 정보를 출력을 하면서 IPC endpoint 값을 줄텐데 이를 geth attach 실행시 넣어주면 된다.

 

geth attach 'IPC endpoint 입력'

 

이렇게 만든 네트워크를 외부에서도 RPC 통신을 통해 접근할 수 있도록 허용하려면

 

네트워크 구동 시 몇 가지 옵션을 추가해주면 된다.

 

geth --datadir node --http --http.addr "0.0.0.0" --http.port 9000 --http.corsdomain "*" --http.api "admin,miner,txpool,web3,personal,eth,net" --syncmode full --networkid 3476 --ipcpath "~/.Ethereum/geth.ipc"

순서대로 http 허용여부, 허용할 ip 주소 (0.0.0.0은 전부 허용), 포트 넘버,

 

CORS 허용, 허용 기능, sync mode, chainId, ipcpath를 적어준 것이다.

 

이 상태로 구동한 네트워크에서 geth attach에서 접근하려면 ip 주소를 추가로 입력해주면 된다.

 

geth attach /home/sila/.Ethereum/geth.ipc http://127.0.0.1:9000

 

 

3. 블록 생성하기

처음 만든 계정이 코인베이스 계정이 된다. 이 코인 베이스 계정으로 채굴을 조금 진행해보자.

 

personal.newAccount()
// 0x462ff27ae1f1a3c53e5134aadb9f765eab4540fe

 

이제 채굴을 좀 해서 코인베이스 계정으로 코인을 조금 입금한다.

 

miner.start(1)

 

채굴이 좀 진행 된 후, 채굴을 멈추고 생성된 블록 수와 코인이 들어왔는지 잔고를 확인해본다.

 

miner.stop()
// null

web3.fromWei(eth.getBalance(eth.coinbase), 'ether')
// [코인베이스 계좌의 잔고]

eth.blockNumber
// [생성된 블록 수]

// 잔액이나 블록 수는 당연히 사람마다 다르게 나온다

 

이제 테스트 코드를 이용해 블록이 생성된 것을 확인해보자.

 

루트 디렉토리에 explorer 폴더를 만들고 npm으로 web3, jest를 설치해준다.

 

npm i web3 jest

 

jest 설정은 다음과 같이 해준다.

 

/*  explorer/jest.config.js  */

const config = {
    verbose : true,
    testMatch : ['<rootDir>/**/*.test.(js|ts)']
}

module.exports = config

 

우선 생성된 블록의 내용을 확인해볼 것이다. 네트워크가 구동되는 상태로 다음과 같이 테스트 코드를 작성해 실행해보자.

 

/*  explorer/block.test.js  */

const Web3 = require('web3')

describe('block', () => {
    let web3
    
    it('web3 연결 테스트', async () => {
        web3 = new Web3(new Web3.providers.HttpProvider('http://127.0.0.1:9000')
        
        const block_number = await web3.eth.getBlockNumber()
        console.log(block_number)
        // 블록 갯수를 출력 - 좀전에 attach에서 출력한 블록 수와 같다
        
        for(let i = 1; i <=block_number; i++) {
            const block = await web3.eth.getBlock(i, true)
            console.log(block)
        }
        console.log(await web3.eth.getBlock(32, true))
    })
})

 

실행해보면 블록이 순서대로 출력될 것이다.

 

4. tx 생성하기

다음으로

 

1. 코인베이스 계정에서 다른 계정으로 코인을 tx 하고,

 

2. 이를 tx pool에서 확인한 후,

 

3.채굴을 다시 진행해 블록을 만들어 만든 tx를 블록에 담고,

 

4. 각 계정의 잔고 변화를 확인한 후,

 

5. 블록체인 네트워크 상에서 해당 tx를 확인해보자.

 

personal.sendTransaction({ from : eth.coinbase, to : '0x462ff27ae1f1a3c53e5134aadb9f765eab4540fe', value: web3.toWei(10, 'ether')},'1234')
// "0x7874a0a72423024b7b689349df8fe9fda9286398ff5a7dab0427acf1526939dc"

 

person.sendTransaction 명령어로 tx를 만들 수 있다.

 

보내는 계좌와 받는 계좌, 잔액, geth account의 비밀번호를 넣어주면 tx가 일어난다.

 

(geth account의 비밀번호로 보안을 한 단계 더 넣을 수 있다고 보면 된다.)

 

결과물로는 tx의 hash값을 리턴해줄 것이다.

 

> txpool
{
  content: {
    pending: {
      0x2Cf64427A1067CBB20552CA477bA9b633a1cd333: {
        2: {...}
      }
    },
    queued: {}
  },
  inspect: {
    pending: {
      0x2Cf64427A1067CBB20552CA477bA9b633a1cd333: {
        2: "0x462Ff27Ae1f1A3C53E5134aaDb9f765eAb4540fe: 10000000000000000000 wei + 21000 gas × 1000000000 wei"
      }
    },
    queued: {}
  },
  status: {
    pending: 1,
    queued: 0
  },
  contentFrom: function(),
  getContent: function(callback),
  getInspect: function(callback),
  getStatus: function(callback)
}
>

 

txpool을 입력하면 객체를 출력해주는데 현재는 tx후 블록이 새로 생성되지 않았으므로 txpool에 pending 으로

 

저장되어있음을 알 수 있다.

 

이제 다시 채굴 작업을 진행해 이 txpool 안의 tx를 블록에 담을 것이다.

 

miner.start(1)
// null
// 매개변수인 숫자는 채굴을 수행하는데 사용할 스레드의 갯수이다


// 채굴이 어느 정도 된 후 중지한다.

miner.stop()
// null

 

txpool을 다시 입력하면 아무런 tx도 없음을 확인할 수 있을 것이다.

 

이는 방금 발생시킨 tx가 블록으로 이동했음을 의미한다.

 

eth.getBalance('0x462ff27ae1f1a3c53e5134aadb9f765eab4540fe')

 

받은 계정의 잔고를 한 번 확인해보고, 방금 발생시킨 tx의 hash를 이용해 블록 체인상에서 tx에 관한 정보를 찾아보자.

 

/*  explorer/block.test.js  */

const Web3 = require('web3')

describe('block', () => {
    let web3

    it('web3 연결 테스트', async () => {
        web3 = new Web3(new Web3.providers.HttpProvider('http://127.0.0.1:9000'))

        const block_number = await web3.eth.getBlockNumber()
        console.log(block_number)

        // for(let i = 1; i<=block_number; i++) {
        //     const block = await web3.eth.getBlock(i, true)
        //     console.log(block)
        // }
        // console.log(await web3.eth.getBlock(23, true))
    })

    it('getTransaction', async () => {
        const tx = await web3.eth.getTransaction('0x7874a0a72423024b7b689349df8fe9fda9286398ff5a7dab0427acf1526939dc')
        console.log(tx)
    })

    it('getTransactionReceipt', async () => {
        const tx = await web3.eth.getTransactionReceipt('0x7874a0a72423024b7b689349df8fe9fda9286398ff5a7dab0427acf1526939dc')
        console.log(tx)
    })
})

 

tx와 txReceipt는 각각 EVM에서 컴파일되기 전 후의 tx 객체를 불러와준다.

 

// getTransaction('0x7874a0a72423024b7b689349df8fe9fda9286398ff5a7dab0427acf1526939dc')
 {
      blockHash: '0x6e96aed0a5f1e9f9047c818237fcc4441511859c7cfe7fdc2469c0b45c630268',
      blockNumber: 33,
      from: '0x2Cf64427A1067CBB20552CA477bA9b633a1cd333',
      gas: 21000,
      gasPrice: '1000000000',
      hash: '0x7874a0a72423024b7b689349df8fe9fda9286398ff5a7dab0427acf1526939dc',
      input: '0x',
      nonce: 2,
      to: '0x462Ff27Ae1f1A3C53E5134aaDb9f765eAb4540fe',
      transactionIndex: 0,
      value: '10000000000000000000',
      type: 0,
      v: '0x3c77',
      r: '0x61f8ce86a9257bd194d223187350cb02f5fc34c6f7ee8ebf132b708905f8898f',
      s: '0x3c04b44250d099fbdcaa02e0577515f6a52e4455050c39a760d218098dd68405'
    }

// getTransactionReceipt('0x7874a0a72423024b7b689349df8fe9fda9286398ff5a7dab0427acf1526939dc')

    {
      blockHash: '0x6e96aed0a5f1e9f9047c818237fcc4441511859c7cfe7fdc2469c0b45c630268',
      blockNumber: 33,
      contractAddress: null,
      cumulativeGasUsed: 21000,
      effectiveGasPrice: 1000000000,
      from: '0x2cf64427a1067cbb20552ca477ba9b633a1cd333',
      gasUsed: 21000,
      logs: [],
      logsBloom: '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
      status: true,
      to: '0x462ff27ae1f1a3c53e5134aadb9f765eab4540fe',
      transactionHash: '0x7874a0a72423024b7b689349df8fe9fda9286398ff5a7dab0427acf1526939dc',
      transactionIndex: 0,
      type: '0x0'
    }

 

'Blockchain' 카테고리의 다른 글

#22 Truffle을 이용한 컨트랙트 배포  (0) 2022.07.12
#21 smart contract  (0) 2022.07.11
#19 geth  (0) 2022.06.30
#18 metamask 활용  (0) 2022.06.29
#17 ethereum  (0) 2022.06.29