Skip to content

Commit 8bc2d2e

Browse files
committed
Add: 스마트 컨트랙트 실행하기
1 parent 8e04461 commit 8bc2d2e

File tree

3 files changed

+295
-32
lines changed

3 files changed

+295
-32
lines changed

_posts/2024-12-13-install-solidity.md

Lines changed: 0 additions & 32 deletions
This file was deleted.
Lines changed: 295 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,295 @@
1+
---
2+
title: 스마트 컨트랙트 실행하기
3+
date: 2024-12-16 12:22:00 +/-TTTT
4+
categories: [Security, Blockchain]
5+
tags: [security, blockchain, geth, ethereum, smart-contract, troubleshooting]
6+
math: true
7+
---
8+
9+
## Solidity Compiler 설치하기
10+
11+
npm을 이용하여 설치해보자.
12+
13+
```shell
14+
% sudo npm install -g solc
15+
```
16+
17+
## 프로그래밍 예제
18+
19+
> VSC에서 `hello.sol` 파일을 만들고 프로그래밍 해보자.
20+
21+
```
22+
// SPDX-License-Identifier: UNLICENSED
23+
pragma solidity 0.4.24;
24+
25+
contract hello{
26+
string message = "Hello world!";
27+
function showMsg() public view returns (string memory){
28+
return message;
29+
}
30+
}
31+
```
32+
33+
## 컴파일
34+
35+
> npm을 이용하여 설치해주었기 때문에, 기본 명령어가 `solc` 가 아닌 `solcjs`이다!
36+
{: .prompt-info}
37+
38+
```shell
39+
% solcjs --abi --bin hello.sol
40+
```
41+
42+
성공적으로 컴파일이 되면 해당 폴더 내 .abi, .bin파일이 생성된다.
43+
44+
- abi (Application Binary Interface) : 스마트 컨트랙트 코드의 설명이 담긴 JSON 파일
45+
- 어떤 함수가 들어있는지 설명하는 것.
46+
- bin : 컴파일 된 바이너리 파일
47+
48+
## 실행하기
49+
50+
### 콘솔 접속
51+
52+
먼저, 미리 만들어둔 사설 네트워크의 콘솔에 접속한다.
53+
54+
> 필자는 포트가 다른 프로세스와 겹쳐 임시로 30000으로 설정하였다.
55+
56+
```shell
57+
geth --datadir cslab --port 30000 --nodiscover console
58+
```
59+
60+
콘솔에서 `bin`, `abi` 변수를 각각 설정한다.
61+
- `bin` : 컴파일된 bin파일을 설정해줌.
62+
- `abi` : json파일 객체 그대로 설정.
63+
64+
### 변수 설정
65+
66+
> bin 변수 설정
67+
68+
- bin파일을 그대로 복사하여 앞에 16진수를 의미하는 0x를 붙여 변수로 설정한다.
69+
- 또한 문자열을 의미하는 "" 따옴표로 감싸서 변수 설정을 해준다.
70+
```shell
71+
> bin = "0x{컴파일된 16진수 bin파일 내용}"
72+
```
73+
74+
75+
> abi 변수 설정
76+
77+
- bin과 다르게 string 문자열이 아닌 객체 형태로 바로 설정한다.
78+
79+
```shell
80+
> abi = [{"inputs":[],"name":"showMsg","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"}]
81+
82+
// 결과
83+
[{
84+
inputs: [],
85+
name: "showMsg",
86+
outputs: [{
87+
internalType: "string",
88+
name: "",
89+
type: "string"
90+
}],
91+
stateMutability: "view",
92+
type: "function"
93+
}]
94+
```
95+
96+
### 이더리움에서 실행하기
97+
98+
`sendTransaction()` 함수는 주소를 반환한다.
99+
- 여기서는 `tx`라는 변수에 주소를 저장한다.
100+
101+
```shell
102+
> tx = eth.sendTransaction({from: eth.accounts[0], data: bin})
103+
```
104+
105+
<br>
106+
107+
하지만 위 명령어를 실행하면 다음과 같이 Authentication 에러가 발생한다.
108+
109+
![img](/assets/img/2024-12-16-run-smart-contract/0.png){: w="400" }{: .shadow}
110+
111+
그러므로, 0번 계좌의 비밀번호를 풀어주는 과정이 필요하다.
112+
113+
```shell
114+
> personal.unlockAccount(eth.accounts[0])
115+
```
116+
117+
그리고 다시 `sendTransaction()` 을 실행시켜주면! 성공적으로 실행된다.
118+
119+
### 채굴
120+
121+
하지만 현재는 바이트코드가 블록에는 들어갔으나, 다른 블록체인과 연결되지는 못한 상태이다.
122+
123+
```shell
124+
> eth.getTransactionReceipt(tx)
125+
null
126+
```
127+
128+
따라서 거래는 되었지만 블록체인에 연결이 되지 않아 결과값이 `null`인 것을 확인할 수 있다.
129+
130+
```shell
131+
> miner.start()
132+
> miner.stop()
133+
```
134+
135+
위 명령어를 통해 채굴을 시작하면 블록체인에 연결된다. 몇 초 기다린 뒤, stop() 해주도록 하자.
136+
137+
그리고 다시 receipt(부가 트랜잭션 정보)를 확인하면? 다음과 같은 결과값을 얻을 수 있다.
138+
139+
```shell
140+
> eth.getTransactionReceipt(tx)
141+
```
142+
143+
```json
144+
{
145+
blockHash: "0xa49a284293d151db5b61f8cf2b50b4fc61d58e01a46b1a6ea6f10ecebf040e9f",
146+
blockNumber: 7,
147+
contractAddress: "0x71776219f07d03224d3c615a6fc22ad450bb3753",
148+
cumulativeGasUsed: 3925000,
149+
from: "0xc71a5b838cf49fd8bd7db6e1493ae0222f8acf3c",
150+
gasUsed: 300000,
151+
logs: [],
152+
logsBloom: "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
153+
status: "0x0",
154+
to: null,
155+
transactionHash: "0xaaa6c78db34de837020fb61cbfb02c4e64c75b0f745b07b52a9caac32cba4f6f",
156+
transactionIndex: 3
157+
}
158+
```
159+
160+
특히 이 결과값에서 `contractAddress`를 주로 사용하게 될 건데, <br>`contractAddress`란? 바이너리 코드가 들어간 위치(**스마트 컨트랙트가 들어간 블록의 주소**)이다.
161+
162+
아래 명령어를 통해 contractAddress를 address라는 변수에 저장해주도록 하자.
163+
164+
```shell
165+
> address = eth.getTransactionReceipt(tx).contractAddress
166+
"0x71776219f07d03224d3c615a6fc22ad450bb3753"
167+
```
168+
169+
### 계약 인스턴스 가져오기
170+
171+
스마트 컨트랙트를 다음과 같이 배포하고, eth.contract()를 사용하여 계약 인스턴스를 가져오고 메서드들을 호출할 수 있다.
172+
173+
```shell
174+
> contractInterface = eth.contract(abi).at(address)
175+
```
176+
177+
```json
178+
{
179+
abi: [{
180+
inputs: [],
181+
name: "showMsg",
182+
outputs: [{...}],
183+
stateMutability: "view",
184+
type: "function"
185+
}],
186+
address: "0x71776219f07d03224d3c615a6fc22ad450bb3753",
187+
transactionHash: null,
188+
allEvents: function(),
189+
showMsg: function()
190+
}
191+
```
192+
193+
이 내용은 위에서 작성한 `hello.sol` 코드의 내용이다. 여기서 우리가 작성한 showMsg() 함수를 실행해보자.
194+
195+
### 함수 실행
196+
197+
```shell
198+
> contractInterface.showMsg.call()
199+
"Hello world!"
200+
```
201+
202+
## 트러블 슈팅
203+
204+
### `Error: intrinsic gas too low`
205+
206+
아래와 같은 에러가 발생한다면 배포하거나 호출하는 트랜잭션에서 가스 한도를 너무 적게 설정했을 가능성이 크다!
207+
208+
```shell
209+
> tx = eth.sendTransaction({from: eth.accounts[0], data: bin})
210+
Error: intrinsic gas too low
211+
at web3.js:3143:20
212+
at web3.js:6347:15
213+
at web3.js:5081:36
214+
at <anonymous>:1:6
215+
```
216+
217+
다음과 같이 `gas:3000000` 충분한 가스를 할당할 경우 해결할 수 있다.
218+
219+
```shell
220+
> tx = eth.sendTransaction({from: eth.accounts[0], data: bin, gas:3000000})
221+
INFO [12-16|18:23:07.052] Submitted contract creation fullhash=0x7ec5f48701c964fc7563fa5f2dcc52b00f8ad5298942d444f6ce297eeac6c066 contract=0x8EC59dabc0d88eAbeed9cC869B81795Ac321673d
222+
"0x7ec5f48701c964fc7563fa5f2dcc52b00f8ad5298942d444f6ce297eeac6c066"
223+
```
224+
225+
#### 여기서 가스란?
226+
227+
Gas(가스)는 이더리움이라는 거대한 컴퓨터를 쓰기 위한 일종의 연료이다.
228+
- 트랜잭션을 수행하는데에 있어 네트워크에 대한 **수수료**이다.
229+
230+
1. 스마트 컨트랙트를 배포할 때
231+
2. 함수에서 상태 변수에 변화를 줄 때
232+
233+
등등 컨트랙트 내부에서 특정 코드를 실행할 때 발생되는 수수료이다.
234+
235+
236+
#### 해결되지 않은 의문.....
237+
238+
가스 계산이 잘못된 것 같다. 내 코드에서는 상태 변수에 변화도 없고, 아주 간단하여 복잡성도 적다..
239+
240+
뭔가 세팅에서 잘못된 것 같은데.. 풀리지 않았다
241+
242+
```shell
243+
> eth.getTransactionReceipt(tx)
244+
{
245+
blockHash: "0xbb1dcbcf02bd1cdc064bfe6027e5021cc80ae9e542282091f9b58297f82f2976",
246+
blockNumber: 58,
247+
contractAddress: "0xa9d6fe280d6fbe013726e4f6cfb44871ff2d34b9",
248+
cumulativeGasUsed: 199384,
249+
from: "0xc71a5b838cf49fd8bd7db6e1493ae0222f8acf3c",
250+
gasUsed: 199384,
251+
logs: [],
252+
logsBloom: "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
253+
status: "0x1",
254+
to: null,
255+
transactionHash: "0x587427e8b4ada7cae90cc17dbb1e265df6bb3d5770fdb32db707f89dfbcc7a5e",
256+
transactionIndex: 0
257+
}
258+
```
259+
260+
이 트랜잭션에서는 199384의 gas를 사용하였는데, 왜이렇게 많이 사용했을까? 아니면 이게 일반적인 수치인가?
261+
262+
공부가 더 필요하다..
263+
264+
### Error: new BigNumber() not a base 16 number:
265+
266+
```shell
267+
> contractInterface.showMsg.call()
268+
Error: new BigNumber() not a base 16 number:
269+
at L (bignumber.js:3:2876)
270+
at bignumber.js:3:8435
271+
at a (bignumber.js:3:389)
272+
at web3.js:1110:23
273+
at web3.js:1634:20
274+
at web3.js:826:16
275+
at map (<native code>)
276+
at web3.js:825:12
277+
at web3.js:4080:18
278+
```
279+
280+
뜬금없이 함수 호출할 때, 다음과 같이 `Error: new BigNumber() not a base 16 number: ` 에러가 발생하였다.
281+
282+
구글링해보니 web3.js 버그.. 배포 주소 버그.. 등등에 관련한 질문이 나왔는데 **결과적으로는 solidity 컴파일러 버전과 geth 버전의 차이때문에 발생한 문제였다.**
283+
284+
geth 버전은 공부하고 있는 책의 버전 대로 `1.8.13-stable`을, 하지만 solidity 컴파일러는 `0.8.x`대의 최신 버전을 사용하여 발생한 에러였다.
285+
286+
```bash
287+
% npm uninstall solc
288+
% npm install -g solc@0.4.24
289+
```
290+
291+
위의 명령어를 통해 컴파일러를 삭제하고, geth 버전에 맞춘 `0.4.24` 버전으로 다운그레이드하여 해결하였다.
292+
293+
물론, solidity 코드도 `pragma solidity = 0.4.24;` 로 변경하여 특정 버전의 컴파일러를 선택하도록하였다.
294+
295+
Thanks to.. [힌트 주신 하나의 게시글](https://www.clien.net/service/board/cm_blockchain/13645133)
38.5 KB
Loading

0 commit comments

Comments
 (0)