[Ethereum] 프라이빗 네트워크에 컨트랙트 배포 및 테스트

메타코인 예제 프로젝트를 이용해 프라이빗 네트워크에 컨트랙트를 배포하고 테스트해보겠습니다.

메타코인

트러플을 이용해 개발한 간단한 코인입니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
% cd private-net
% mkdir truffle-metacoin
% cd truffle-metacoin
% truffle unbox metacoin


Starting unbox...
=================

✓ Preparing to download box
✓ Downloading
✓ Cleaning up temporary files
✓ Setting up box

Unbox successful, sweet!

Commands:

Compile contracts: truffle compile
Migrate contracts: truffle migrate
Test contracts: truffle test

truffle-config.js 수정

truffle-config.js의 networks 부분을 아래와 같이 수정합니다.

1
2
3
4
5
6
7
8
9
10
11
12
networks: {
//... 생략
development: {
host: "127.0.0.1", // Localhost (default: none)
port: 8545, // Standard Ethereum port (default: none)
network_id: "*", // Any network (default: none)
},
test: "127.0.0.1",
port: 8545,
network_id: "*"
//... 생략
},

geth

아래의 명령어를 실행하여 geth를 구동합니다.

1
% geth --datadir ~/private-net --networkid 15 --nodiscover --maxpeers 0 --mine --miner.threads 1 --http --http.addr "0.0.0.0" --http.corsdomain "*" --http.vhosts "*" --http.api "eth,web3,persional,net,miner" --ipcpath ~/private-net/geth.ipc --ws --ws.addr "0.0.0.0" --ws.api "eth,web3,persional,net,miner" --ws.origins "*" --allow-insecure-unlock --password ~/private-net/password

새 터미널을 열고 geth 접속

1
2
3
4
5
6
7
8
9
10
% geth attach http://localhost:8545
Welcome to the Geth JavaScript console!

instance: Geth/v1.10.26-stable/darwin-arm64/go1.19.3
coinbase: 0x945cd603a6754cb13c3d61d8fe240990f86f9f8a
at block: 1533 (Wed Dec 14 2022 17:41:40 GMT+0900 (KST))
modules: eth:1.0 miner:1.0 net:1.0 personal:1.0 rpc:1.0 web3:1.0

To exit, press ctrl-d or type exit
>

첫번째 계정 잠금 해제

password 파일의 첫번째 행이 첫번째 계정의 비밀번호입니다.

저는 blah로 정했습니다.

1
2
3
4
> web3.personal.unlockAccount(eth.accounts[0])
Unlock account 0x945cd603a6754cb13c3d61d8fe240990f86f9f8a
Passphrase:
true

나머지 4개의 계정 잠금 해제

위의 eth.accounts[0]부터 eth.accounts[4]까지 반복하여 5번째 계정까지 모두 해제합니다.

프라이빗 네트워크에 계약 배포

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
% truffle migrate --network development

Compiling your contracts...
===========================
> Compiling ./contracts/ConvertLib.sol-bin. Attempt #1
> Compiling ./contracts/MetaCoin.sol
> Artifacts written to /Users/devsawd/private-net/truffle-metacoin/build/contracts
> Compiled successfully using:
- solc: 0.8.13+commit.abaa5c0e.Emscripten.clang
⠸ Fetching solc version list from solc-bin. Attempt #1
✓ Downloading compiler. Attempt #1.
Starting migrations...
======================
> Network name: 'development'
> Network id: 15
> Block gas limit: 15265600 (0xe8ef40)


1_deploy_contracts.js
=====================

Deploying 'ConvertLib'
----------------------
> transaction hash: 0x2c4b0d752d2ad6fe16fdbbc904ddcc4bdb0292cff854ede6a59a662d99c152beg compiler. Attempt #1.
> Blocks: 1 Seconds: 4lc-bin. Attempt #1
> contract address: 0xc786144E0BEb71b86B71eF4b93e0c05F39904E75
> block number: 1621nds: 0
> block timestamp: 1671007549
> account: 0x945Cd603A6754cB13C3D61d8fe240990f86f9f8A
> balance: 500003242
> gas used: 180656 (0x2c1b0)
> gas price: 1 gwei
> value sent: 0 ETH
> total cost: 0.000180656 ETH


Linking
-------
* Contract: MetaCoin <--> Library: ConvertLib (at address: 0xc786144E0BEb71b86B71eF4b93e0c05F39904E75)

Deploying 'MetaCoin'
--------------------
> transaction hash: 0x0c5a64372ed0ed72455c908216aa58817062a13a9ccc3b3db71310ae31b7d737
> Blocks: 0 Seconds: 0lc-bin. Attempt #1
> contract address: 0x2785BA9c8c1d765E85f6F46DF3E6fF22B6EdcaeA
> block number: 1623nds: 0
> block timestamp: 1671007555
> account: 0x945Cd603A6754cB13C3D61d8fe240990f86f9f8A
> balance: 500003246
> gas used: 496498 (0x79372)
> gas price: 1 gwei
> value sent: 0 ETH
> total cost: 0.000496498 ETH

> Saving artifacts
-------------------------------------
> Total cost: 0.000677154 ETH

Summary
=======
> Total deployments: 2
> Final cost: 0.000677154 ETH

트러플

트러플을 이용하여 잔액 확인 및 트랜잭션을 확인합니다.

1
2
% truffle console --network development
truffle(development)>

메타코인 계약 정보와 계정 정보를 상수에 저장

1
2
3
4
5
let instance = await MetaCoin.deployed()
undefined
truffle(development)> let accounts = await web3.eth.getAccounts()
undefined
truffle(development)>

web-eth 패키지를 이용해 이더리움 블록체인 및 이더리움 스마트 컨트랙트와 상호 작용할 수 있습니다.

첫 번째와 두 번째 계정의 잔액 확인

1
2
3
4
5
6
7
8
9
10
truffle(development)> instance.getBalance(accounts[0])
BN {
negative: 0,
words: [ 10000, <1 empty item> ],
length: 1,
red: null
}
truffle(development)> instance.getBalance(accounts[1])
BN { negative: 0, words: [ 0, <1 empty item> ], length: 1, red: null }
truffle(development)>

MetaCoin.sol 소스코드를 보면 다른 함수 목록과 코드를 볼 수 있습니다.

트랜잭션 실행

첫 번째 계정에서 10 메타코인을 두 번째 계정으로 보내는 트랜잭션을 실행해봅니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
truffle(development)> instance.sendCoin(accounts[1], 10, {from: accounts[0]})
{
tx: '0x1671f227950efd2e8fe6c35397670af8e614bc2dbb42274a279af7e3d855b59b',
receipt: {
blockHash: '0x3f8bdbe2f2fd7fd94bf6d2cb6c411f9c9f90dcaa651c576a1528e7255b860837',
blockNumber: 1745,
contractAddress: null,
cumulativeGasUsed: 51921,
effectiveGasPrice: 1000000000,
from: '0x945cd603a6754cb13c3d61d8fe240990f86f9f8a',
gasUsed: 51921,
logs: [ [Object] ],
logsBloom: '0x00000000000000000000000100000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000800000040004000000000000000000000000000000000000800000000000000000000000000000010000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000',
status: true,
to: '0x2785ba9c8c1d765e85f6f46df3e6ff22b6edcaea',
transactionHash: '0x1671f227950efd2e8fe6c35397670af8e614bc2dbb42274a279af7e3d855b59b',
transactionIndex: 0,
type: '0x0',
rawLogs: [ [Object] ]
},
logs: [
{
address: '0x2785BA9c8c1d765E85f6F46DF3E6fF22B6EdcaeA',
blockNumber: 1745,
transactionHash: '0x1671f227950efd2e8fe6c35397670af8e614bc2dbb42274a279af7e3d855b59b',
transactionIndex: 0,
blockHash: '0x3f8bdbe2f2fd7fd94bf6d2cb6c411f9c9f90dcaa651c576a1528e7255b860837',
logIndex: 0,
removed: false,
id: 'log_a1c1c0d3',
event: 'Transfer',
args: [Result]
}
]
}

만약 인증이 필요하다는 오류가 발생한다면..

아래와 같이 인증 오류가 발생할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
Uncaught:
Error: Returned error: authentication needed: password or unlock -- Reason given: Custom error (could not decode).
at evalmachine.<anonymous>
at sigintHandlersWrap (node:vm:276:12)
at Script.runInContext (node:vm:139:14)
at runScript (/opt/homebrew/lib/node_modules/truffle/build/webpack:/packages/core/lib/console.js:454:1)
at Console.interpret (/opt/homebrew/lib/node_modules/truffle/build/webpack:/packages/core/lib/console.js:469:1)
at bound (node:domain:426:15)
at REPLServer.runBound [as eval] (node:domain:437:12)
at REPLServer.onLine (node:repl:902:10)
at REPLServer.emit (node:events:513:28)
at REPLServer.emit (node:domain:482:12)
at REPLServer.[_onLine] [as _onLine] (node:internal/readline/interface:425:12)
at REPLServer.[_line] [as _line] (node:internal/readline/interface:886:18)
at REPLServer.[_ttyWrite] [as _ttyWrite] (node:internal/readline/interface:1264:22)
at REPLServer.self._ttyWrite (node:repl:997:9)
at ReadStream.onkeypress (node:internal/readline/interface:273:20)
at ReadStream.emit (node:events:513:28)
at ReadStream.emit (node:domain:482:12)
at emitKeys (node:internal/readline/utils:357:14)
at emitKeys.next (<anonymous>)
at ReadStream.onData (node:internal/readline/emitKeypressEvents:64:36) {
data: null,
reason: 'Custom error (could not decode)',
hijackedStack: 'Error: Returned error: authentication needed: password or unlock -- Reason given: Custom error (could not decode).\n' +
' at Object.ErrorResponse (/opt/homebrew/lib/node_modules/truffle/build/webpack:/node_modules/web3-core-helpers/lib/errors.js:28:1)\n' +
' at /opt/homebrew/lib/node_modules/truffle/build/webpack:/node_modules/web3-core-requestmanager/lib/index.js:300:1\n' +
' at /opt/homebrew/lib/node_modules/truffle/build/webpack:/packages/provider/wrapper.js:123:1\n' +
' at XMLHttpRequest.request.onreadystatechange (/opt/homebrew/lib/node_modules/truffle/build/webpack:/node_modules/web3-providers-http/lib/index.js:98:1)\n' +
' at XMLHttpRequestEventTarget.dispatchEvent (/opt/homebrew/lib/node_modules/truffle/build/webpack:/node_modules/xhr2-cookies/dist/xml-http-request-event-target.js:34:1)\n' +
' at XMLHttpRequest.exports.modules.996763.XMLHttpRequest._setReadyState (/opt/homebrew/lib/node_modules/truffle/build/webpack:/node_modules/xhr2-cookies/dist/xml-http-request.js:208:1)\n' +
' at XMLHttpRequest.exports.modules.996763.XMLHttpRequest._onHttpResponseEnd (/opt/homebrew/lib/node_modules/truffle/build/webpack:/node_modules/xhr2-cookies/dist/xml-http-request.js:318:1)\n' +
' at IncomingMessage.<anonymous> (/opt/homebrew/lib/node_modules/truffle/build/webpack:/node_modules/xhr2-cookies/dist/xml-http-request.js:289:48)\n' +
' at IncomingMessage.emit (node:events:525:35)\n' +
' at IncomingMessage.emit (node:domain:545:15)\n' +
' at endReadableNT (node:internal/streams/readable:1359:12)\n' +
' at processTicksAndRejections (node:internal/process/task_queues:82:21)'
}

앞서 계정의 비밀번호를 입력하는 과정을 한번 더 거친후 트랜잭션을 실행합니다.

일정 시간이 지나면 계정이 잠기니 실습이 오래걸리면 발생할 수 있습니다.

트랜잭션 확인

계정 잔고를 확인해 트랜잭션이 실행되었는지 확인합니다.

1
2
3
4
5
6
7
8
9
truffle(development)> instance.getBalance(accounts[0])
BN {
negative: 0,
words: [ 9990, <1 empty item> ],
length: 1,
red: null
}
truffle(development)> instance.getBalance(accounts[1])
BN { negative: 0, words: [ 10, <1 empty item> ], length: 1, red: null }