Skip to content

Commit 36b4b64

Browse files
committed
ch3. node network
1 parent ba14a53 commit 36b4b64

15 files changed

+1641
-83
lines changed

README.md

+13-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,15 @@
11
# python-blockchain
22
파이썬으로 공부하는 블록체인
3-
https://github.com/bjpublic/blockchain-python
3+
https://github.com/bjpublic/blockchain-python
4+
5+
## 1. 파이썬 실습
6+
7+
## 2. 간단한 비트코인 네트워크 구축
8+
one node
9+
10+
multiple node
11+
12+
## 3. 간단한 이더리움 네트어크 구축
13+
PoW
14+
15+
PoS

bitcoin/node_network/node_network_1.ipynb

+374
Large diffs are not rendered by default.

bitcoin/node_network/node_network_2.ipynb

+358
Large diffs are not rendered by default.

bitcoin/node_network/node_network_3.ipynb

+358
Large diffs are not rendered by default.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
{
2+
"cells": [
3+
{
4+
"cell_type": "code",
5+
"execution_count": null,
6+
"metadata": {},
7+
"outputs": [],
8+
"source": [
9+
"import requests\n",
10+
"import json \n",
11+
"import pandas as pd\n",
12+
"import hashlib # 해시 함수용 sha256 사용할 라이브러리 \n",
13+
"import random "
14+
]
15+
},
16+
{
17+
"cell_type": "code",
18+
"execution_count": null,
19+
"metadata": {},
20+
"outputs": [],
21+
"source": [
22+
"headers = {'Content-Type' : 'application/json; charset=utf-8'}\n",
23+
"res = requests.get(\"http://localhost:5000/chain\", headers=headers)\n",
24+
"json.loads(res.content)"
25+
]
26+
},
27+
{
28+
"cell_type": "code",
29+
"execution_count": null,
30+
"metadata": {},
31+
"outputs": [],
32+
"source": [
33+
"headers = {'Content-Type' : 'application/json; charset=utf-8'}\n",
34+
"res = requests.get(\"http://localhost:5001/chain\", headers=headers)\n",
35+
"json.loads(res.content)"
36+
]
37+
},
38+
{
39+
"cell_type": "code",
40+
"execution_count": null,
41+
"metadata": {},
42+
"outputs": [],
43+
"source": [
44+
"headers = {'Content-Type' : 'application/json; charset=utf-8'}\n",
45+
"res = requests.get(\"http://localhost:5002/chain\", headers=headers)\n",
46+
"json.loads(res.content)"
47+
]
48+
},
49+
{
50+
"cell_type": "markdown",
51+
"metadata": {},
52+
"source": [
53+
"노드 1,2,3 번 등록"
54+
]
55+
},
56+
{
57+
"cell_type": "code",
58+
"execution_count": null,
59+
"metadata": {},
60+
"outputs": [],
61+
"source": [
62+
"## 노드 등록하기\n",
63+
"headers = {'Content-Type' : 'application/json; charset=utf-8'}\n",
64+
"data = {\n",
65+
" \"nodes\": 'http://localhost:5001'\n",
66+
"}\n",
67+
"requests.post(\"http://localhost:5000/nodes/register\", headers=headers, data=json.dumps(data)).content"
68+
]
69+
},
70+
{
71+
"cell_type": "code",
72+
"execution_count": null,
73+
"metadata": {},
74+
"outputs": [],
75+
"source": [
76+
"## 노드 등록하기\n",
77+
"headers = {'Content-Type' : 'application/json; charset=utf-8'}\n",
78+
"data = {\n",
79+
" \"nodes\": 'http://localhost:5002'\n",
80+
"}\n",
81+
"requests.post(\"http://localhost:5000/nodes/register\", headers=headers, data=json.dumps(data)).content"
82+
]
83+
},
84+
{
85+
"cell_type": "markdown",
86+
"metadata": {},
87+
"source": [
88+
"transaction 입력하기"
89+
]
90+
},
91+
{
92+
"cell_type": "code",
93+
"execution_count": null,
94+
"metadata": {},
95+
"outputs": [],
96+
"source": [
97+
"headers = {'Content-Type' : 'application/json; charset=utf-8'}\n",
98+
"data = {\n",
99+
" \"sender\": \"test_from\",\n",
100+
" \"recipient\": \"test_to\",\n",
101+
" \"amount\": 3,\n",
102+
"}\n",
103+
"requests.post(\"http://localhost:5000/transactions/new\", headers=headers, data=json.dumps(data)).content"
104+
]
105+
},
106+
{
107+
"cell_type": "markdown",
108+
"metadata": {},
109+
"source": [
110+
"노드의 블록 정보 확인"
111+
]
112+
},
113+
{
114+
"cell_type": "code",
115+
"execution_count": null,
116+
"metadata": {},
117+
"outputs": [],
118+
"source": [
119+
"headers = {'Content-Type' : 'application/json; charset=utf-8'}\n",
120+
"res = requests.get(\"http://localhost:5000/chain\", headers=headers)\n",
121+
"json.loads(res.content)"
122+
]
123+
},
124+
{
125+
"cell_type": "markdown",
126+
"metadata": {},
127+
"source": [
128+
"채굴하기"
129+
]
130+
},
131+
{
132+
"cell_type": "code",
133+
"execution_count": null,
134+
"metadata": {},
135+
"outputs": [],
136+
"source": [
137+
"headers = {'Content-Type' : 'application/json; charset=utf-8'}\n",
138+
"res = requests.get(\"http://localhost:5002/mine\")\n",
139+
"print(res)"
140+
]
141+
},
142+
{
143+
"cell_type": "markdown",
144+
"metadata": {},
145+
"source": [
146+
"Pandas를 활용한 거래 내역 조회"
147+
]
148+
},
149+
{
150+
"cell_type": "code",
151+
"execution_count": null,
152+
"metadata": {},
153+
"outputs": [],
154+
"source": [
155+
"status_json = json.loads(res.text)\n",
156+
"status_json['chain'] \n",
157+
"tx_amount_l = []\n",
158+
"tx_sender_l = []\n",
159+
"tx_reciv_l = []\n",
160+
"tx_time_l = []\n",
161+
"\n",
162+
"for chain_index in range(len(status_json['chain'])):\n",
163+
" chain_tx = status_json['chain'][chain_index]['transactions']\n",
164+
" for each_tx in range(len(chain_tx)):\n",
165+
" tx_amount_l.append(chain_tx[each_tx]['amount'])\n",
166+
" tx_sender_l.append(chain_tx[each_tx]['sender'])\n",
167+
" tx_reciv_l.append(chain_tx[each_tx]['recipient'])\n",
168+
" tx_time_l.append(chain_tx[each_tx]['timestamp'])\n",
169+
"\n",
170+
"df_tx = pd.DataFrame()\n",
171+
"df_tx['timestamp'] = tx_time_l \n",
172+
"df_tx['sender'] = tx_sender_l \n",
173+
"df_tx['recipient'] = tx_reciv_l\n",
174+
"df_tx['amount'] = tx_amount_l \n",
175+
"df_tx"
176+
]
177+
}
178+
],
179+
"metadata": {
180+
"language_info": {
181+
"name": "python"
182+
}
183+
},
184+
"nbformat": 4,
185+
"nbformat_minor": 2
186+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
{
2+
"cells": [
3+
{
4+
"cell_type": "markdown",
5+
"metadata": {},
6+
"source": [
7+
"블록 지갑 백엔드 코드"
8+
]
9+
},
10+
{
11+
"cell_type": "code",
12+
"execution_count": null,
13+
"metadata": {},
14+
"outputs": [],
15+
"source": [
16+
"from flask import Flask\n",
17+
"from datetime import datetime\n",
18+
"from flask import render_template\n",
19+
"from flask import request\n",
20+
"from flask import url_for\n",
21+
"from flask import redirect\n",
22+
"\n",
23+
"import requests\n",
24+
"import json\n",
25+
"import os\n",
26+
"import pandas as pd\n",
27+
"import random\n",
28+
"\n",
29+
"app = Flask(__name__, template_folder=os.getcwd())\n",
30+
"node_port_list = ['5000','5001','5002']\n",
31+
"\n",
32+
"@app.route('/', methods=['GET', 'POST'])\n",
33+
"def login():\n",
34+
"\n",
35+
" if request.method=='POST':\n",
36+
" print(\"login 버튼을 누름\")\n",
37+
" input_value = request.form.to_dict(flat=False)\n",
38+
" print(\"login 지갑주소 : \" , input_value)\n",
39+
" \n",
40+
" ## 노드 주소 랜덤 선정\n",
41+
" node_id = random.choice(node_port_list)\n",
42+
" \n",
43+
" ### 기존 user 정보 확인\n",
44+
" headers = {'Content-Type' : 'application/json; charset=utf-8'}\n",
45+
" ## 선정된 노드 주소로 데이터 요청\n",
46+
" res = requests.get(\"http://localhost:\" +node_id + \"/chain\", headers=headers)\n",
47+
" print(\"*\"*8)\n",
48+
" status_json = json.loads(res.text)\n",
49+
" status_json['chain'] \n",
50+
" tx_amount_l = []\n",
51+
" tx_sender_l = []\n",
52+
" tx_reciv_l = []\n",
53+
" tx_time_l = []\n",
54+
"\n",
55+
" for chain_index in range(len(status_json['chain'])):\n",
56+
" chain_tx = status_json['chain'][chain_index]['transactions']\n",
57+
" for each_tx in range(len(chain_tx)):\n",
58+
" tx_amount_l.append(chain_tx[each_tx]['amount'])\n",
59+
" tx_sender_l.append(chain_tx[each_tx]['sender'])\n",
60+
" tx_reciv_l.append(chain_tx[each_tx]['recipient'])\n",
61+
" tx_time_l.append(chain_tx[each_tx]['timestamp'])\n",
62+
"\n",
63+
" df_tx = pd.DataFrame()\n",
64+
" df_tx['timestamp'] = tx_time_l \n",
65+
" df_tx['sender'] = tx_sender_l \n",
66+
" df_tx['recipient'] = tx_reciv_l\n",
67+
" df_tx['amount'] = tx_amount_l \n",
68+
" df_tx\n",
69+
"\n",
70+
"\n",
71+
"\n",
72+
" df_sended = pd.DataFrame(df_tx.groupby('sender')['amount'].sum()).reset_index()\n",
73+
" df_sended.columns = ['user','sended_amount']\n",
74+
" df_received= pd.DataFrame(df_tx.groupby('recipient')['amount'].sum()).reset_index()\n",
75+
" df_received.columns = ['user','received_amount']\n",
76+
" df_received\n",
77+
"\n",
78+
" df_status = pd.merge(df_received,df_sended, on ='user', how= 'outer').fillna(0)\n",
79+
" df_status['balance'] = df_status['received_amount'] - df_status['sended_amount'] \n",
80+
" df_status \n",
81+
" \n",
82+
" \n",
83+
" if (df_status['user']==input_value['wallet_id'][0] ).sum() == 1:\n",
84+
" print(\"로그인성공\")\n",
85+
" return render_template(\"wallet.html\", wallet_id = input_value['wallet_id'][0], \n",
86+
" wallet_value = df_status[df_status['user']== df_status['user'].iloc[0]]['balance'].iloc[0])\n",
87+
" else:\n",
88+
" return \"잘못된 지갑주소입니다.\"\n",
89+
" \n",
90+
" return render_template('login.html')\n",
91+
"\n",
92+
"@app.route('/wallet', methods=['GET', 'POST'])\n",
93+
"def wallet():\n",
94+
" if request.method=='POST':\n",
95+
" send_value = int(request.form.to_dict(flat=False)['send_value'][0] )\n",
96+
" send_target = request.form.to_dict(flat=False)['send_target'][0]\n",
97+
" send_from = request.form.to_dict(flat=False)['send_from'][0]\n",
98+
" \n",
99+
" if send_value > 0:\n",
100+
" print(send_value)\n",
101+
" ## transaction 입력하기\n",
102+
" headers = {'Content-Type' : 'application/json; charset=utf-8'}\n",
103+
" \n",
104+
" ## 노드 주소 랜덤 선정\n",
105+
" data = {\n",
106+
" \"sender\": send_from,\n",
107+
" \"recipient\": send_target,\n",
108+
" \"amount\": send_value,\n",
109+
" }\n",
110+
" \n",
111+
" ## 선정된 노드 주소로 데이터 요청\n",
112+
" requests.post(\"http://localhost:\" +node_id + \"/transactions/new\", headers=headers, data=json.dumps(data)).content\n",
113+
"\n",
114+
" return \"전송 완료!\"\n",
115+
"\n",
116+
" else:\n",
117+
" return \"0 pyBTC 이상 보내주세요!\"\n",
118+
"\n",
119+
" \n",
120+
" \n",
121+
" return render_template('wallet.html')\n",
122+
" \n",
123+
" \n",
124+
" \n",
125+
"app.run(port=8081)"
126+
]
127+
}
128+
],
129+
"metadata": {
130+
"language_info": {
131+
"name": "python"
132+
}
133+
},
134+
"nbformat": 4,
135+
"nbformat_minor": 2
136+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
{
2+
"cells": [
3+
{
4+
"cell_type": "markdown",
5+
"metadata": {},
6+
"source": [
7+
"블록 스캔 백엔드 코드 "
8+
]
9+
},
10+
{
11+
"cell_type": "code",
12+
"execution_count": null,
13+
"metadata": {},
14+
"outputs": [],
15+
"source": [
16+
"from flask import Flask\n",
17+
"from datetime import datetime\n",
18+
"from flask import render_template\n",
19+
"import requests\n",
20+
"import os\n",
21+
"import json\n",
22+
"import pandas as pd\n",
23+
"import random\n",
24+
"\n",
25+
"app = Flask(__name__, template_folder=os.getcwd())\n",
26+
"\n",
27+
"node_port_list = ['5000','5001','5002']\n",
28+
"\n",
29+
"@app.route('/')\n",
30+
"def index():\n",
31+
" headers = {'Content-Type' : 'application/json; charset=utf-8'}\n",
32+
" # 블록 체인 내 블록 정보를 제공하는 url(http://localhost:5000/chain)에 request 방식으로 데이터를 요청\n",
33+
" node_id = random.choice(node_port_list)\n",
34+
" res = requests.get(\"http://localhost:\" + node_id + \"/chain\", headers=headers)\n",
35+
" print(\"Selected Node : \", node_id)\n",
36+
" # 요청 결과 데이터(res.text)를 json 으로 로드\n",
37+
" status_json = json.loads(res.text)\n",
38+
" # 결과 데이터를 pandas의 dataframe(df_scan)으로 정리\n",
39+
" df_scan = pd.DataFrame(status_json['chain'] )\n",
40+
" # Front 구성내용이 담길 html(one_node_scan.html)파일에 Dataframe 정보(df_scan)과 블록의 길이(block_len)을 제공\n",
41+
" return render_template('/node_network_scan.html', df_scan = df_scan, block_len = len(df_scan))\n",
42+
"\n",
43+
"app.run(port=8080)"
44+
]
45+
}
46+
],
47+
"metadata": {
48+
"language_info": {
49+
"name": "python"
50+
}
51+
},
52+
"nbformat": 4,
53+
"nbformat_minor": 2
54+
}
File renamed without changes.
File renamed without changes.

0 commit comments

Comments
 (0)