├── chapter2 ├── example.py ├── example.txt ├── sample.html ├── example_SC.py ├── templates │ ├── sample.html │ ├── backend_sample.html │ ├── javascript.html │ ├── login.html │ └── home.html ├── flask.ipynb ├── flask_login.ipynb └── basic_code.ipynb ├── chapter4 ├── NFT_Wallet.html ├── login.html ├── NFT_Wallet_login.html ├── one_node_scan.html ├── wallet.html ├── Python_exec.ipynb ├── NFTWallet.ipynb ├── node_chainScan.ipynb ├── node.ipynb └── node_command_DApp.ipynb ├── chapter3 ├── one_node │ ├── login.html │ ├── one_node_scan.html │ ├── wallet.html │ ├── one_node_chainScan.ipynb │ └── one_node_command.ipynb └── node_network │ ├── login.html │ ├── node_network_scan.html │ ├── wallet.html │ ├── node_network_Wallet.ipynb │ └── node_network_chainScan.ipynb ├── chapter5 ├── one_onde │ ├── login.html │ ├── one_node_scan.html │ ├── wallet.html │ ├── one_node_chainScan.ipynb │ └── one_node.ipynb └── smart_contract │ ├── NFT_Wallet_login.html │ ├── NFTWallet.ipynb │ ├── node_command_DApp.ipynb │ └── node.ipynb └── README.md /chapter2/example.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /chapter2/example.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /chapter2/sample.html: -------------------------------------------------------------------------------- 1 | This is sample html -------------------------------------------------------------------------------- /chapter2/example_SC.py: -------------------------------------------------------------------------------- 1 | def add_(x,y): return x+y -------------------------------------------------------------------------------- /chapter2/templates/sample.html: -------------------------------------------------------------------------------- 1 |

This is sample html

-------------------------------------------------------------------------------- /chapter2/templates/backend_sample.html: -------------------------------------------------------------------------------- 1 | 가지고 있는 비트코인개수 2 | {{backend_result}} 3 | 4 | -------------------------------------------------------------------------------- /chapter4/NFT_Wallet.html: -------------------------------------------------------------------------------- 1 |

pyETH NFT Wallet

2 | 3 |
4 |
NFT NAME : {{nft_name}}

5 |
NFT Address : {{nft_addresss}}

6 |
7 | 8 |
9 | 10 | 11 | 12 |
13 | -------------------------------------------------------------------------------- /chapter4/login.html: -------------------------------------------------------------------------------- 1 |

pyETH Block Chain Network Wallet

2 | 3 |
4 |
5 | 6 |
7 |
8 | 9 |
10 |
11 | 12 |
13 | -------------------------------------------------------------------------------- /chapter3/one_node/login.html: -------------------------------------------------------------------------------- 1 |

pyBTC Block Chain Network Wallet

2 | 3 |
4 |
5 | 6 |
7 |
8 | 9 |
10 |
11 | 12 |
13 | -------------------------------------------------------------------------------- /chapter5/one_onde/login.html: -------------------------------------------------------------------------------- 1 |

pyBTC Block Chain Network Wallet

2 | 3 |
4 |
5 | 6 |
7 |
8 | 9 |
10 |
11 | 12 |
13 | -------------------------------------------------------------------------------- /chapter4/NFT_Wallet_login.html: -------------------------------------------------------------------------------- 1 |

pyETH NFT Wallet

2 | 3 |
4 |
5 | 6 |
7 |
8 | 9 |
10 |
11 | 12 |
13 | -------------------------------------------------------------------------------- /chapter3/node_network/login.html: -------------------------------------------------------------------------------- 1 | 2 |

pyBTC Block Chain Network

3 | 4 | 5 |
6 |
7 | 8 |
9 |
10 | 11 | 12 |
13 | 14 |
15 | 16 | 17 |
18 | -------------------------------------------------------------------------------- /chapter5/smart_contract/NFT_Wallet_login.html: -------------------------------------------------------------------------------- 1 |

pyETH NFT Wallet

2 | 3 |
4 |
5 | 6 |
7 |
8 | 9 |
10 |
11 | 12 |
13 | -------------------------------------------------------------------------------- /chapter2/templates/javascript.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | 보내는사람 지갑주소 :
5 | 받을사람 지갑주소 :
6 | 보낼 코인 : 7 | 8 | 9 | 10 |
11 | 12 | 13 | -------------------------------------------------------------------------------- /chapter4/one_node_scan.html: -------------------------------------------------------------------------------- 1 |

pyETH Block Scan

2 | 3 |
4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | {%for i in range(0,block_len)%} 14 | 15 | 16 | 17 | 18 | 19 | 20 | {%endfor%} 21 | 22 |
timestampprevious_hashnoncetransactions
{{df_scan.loc[i,'timestamp']}}{{df_scan.loc[i,'previous_hash']}}{{df_scan.loc[i,'nonce']}}{{df_scan.loc[i,'transactions']}}
23 | 24 | 25 |
-------------------------------------------------------------------------------- /chapter3/one_node/one_node_scan.html: -------------------------------------------------------------------------------- 1 |

pyBTC Block Scan

2 | 3 |
4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | {%for i in range(0,block_len)%} 14 | 15 | 16 | 17 | 18 | 19 | 20 | {%endfor%} 21 | 22 |
timestampprevious_hashnoncetransactions
{{df_scan.loc[i,'timestamp']}}{{df_scan.loc[i,'previous_hash']}}{{df_scan.loc[i,'nonce']}}{{df_scan.loc[i,'transactions']}}
23 | 24 | 25 |
-------------------------------------------------------------------------------- /chapter5/one_onde/one_node_scan.html: -------------------------------------------------------------------------------- 1 |

pyBTC Block Scan

2 | 3 |
4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | {%for i in range(0,block_len)%} 14 | 15 | 16 | 17 | 18 | 19 | 20 | {%endfor%} 21 | 22 |
timestampprevious_hashnoncetransactions
{{df_scan.loc[i,'timestamp']}}{{df_scan.loc[i,'previous_hash']}}{{df_scan.loc[i,'nonce']}}{{df_scan.loc[i,'transactions']}}
23 | 24 | 25 |
-------------------------------------------------------------------------------- /chapter3/node_network/node_network_scan.html: -------------------------------------------------------------------------------- 1 |

pyBTC Block Scan

2 | 3 |
4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | {%for i in range(0,block_len)%} 14 | 15 | 16 | 17 | 18 | 19 | 20 | {%endfor%} 21 | 22 |
timestampprevious_hashnoncetransactions
{{df_scan.loc[i,'timestamp']}}{{df_scan.loc[i,'previous_hash']}}{{df_scan.loc[i,'nonce']}}{{df_scan.loc[i,'transactions']}}
23 | 24 | 25 |
-------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 파이썬으로 공부하는 블록체인 2 | * **저자** 일등박사 3 | * **출판사** 비제이퍼블릭 4 | * **정가** 25,000원 5 | * **페이지** 296쪽 6 |



7 | ### 📖 책 소개 8 | > 조선왕조실록도 블록체인일까? 내 코인도 채굴이 가능할까? 9 | 10 | **파**이썬으로 **공**부하는 **블**록체인 11 | 12 | 이 책은 Python으로 블록체인 요소들을 학습니다. 13 | PoS, PoW 방식의 실제 블록체인 네트워크를 각각 구현한 뒤 14 | DeFi, NFT 등을 직접 자신의 블록체인 네트워크 상에 만들 수 있습니다. 15 |



16 | ### 🖥️ 기술 스택 17 | 18 | -------------------------------------------------------------------------------- /chapter3/node_network/wallet.html: -------------------------------------------------------------------------------- 1 | 2 |

pyBTC Block Chain Network Wallet

3 | 4 |

내 지갑 주소 : {{wallet_id}}

5 |
6 |

내 지갑 잔액 : {{wallet_value}} pyBTC

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 | -------------------------------------------------------------------------------- /chapter2/templates/login.html: -------------------------------------------------------------------------------- 1 | 2 |

파공블 Block Chain Network

3 | 4 | 5 |
6 |
7 | 8 |
9 |
10 | 11 |
12 |
13 |
14 | 15 | 16 |
17 | 18 |
19 | 20 |
21 | 22 | 23 |
24 | 25 |
26 | Create an Account! 27 | 28 |
-------------------------------------------------------------------------------- /chapter4/wallet.html: -------------------------------------------------------------------------------- 1 | 2 |

pyETH Block Chain Wallet

3 | 4 |

내 지갑 주소 : {{wallet_id}}

5 |
6 |

내 지갑 잔액 : {{wallet_value}} pyBTC

7 | 8 |
9 | 10 |
11 | 12 | 13 |
14 | 15 |
16 | 17 |
18 |
19 | 20 |
21 | 22 |
23 | 24 |
25 | 26 |
27 | 28 | -------------------------------------------------------------------------------- /chapter3/one_node/wallet.html: -------------------------------------------------------------------------------- 1 | 2 |

pyBTC Block Chain Wallet

3 | 4 |

내 지갑 주소 : {{wallet_id}}

5 |
6 |

내 지갑 잔액 : {{wallet_value}} pyBTC

7 | 8 |
9 | 10 |
11 | 12 | 13 |
14 | 15 |
16 | 17 |
18 |
19 | 20 |
21 | 22 |
23 | 24 |
25 | 26 |
27 | 28 | -------------------------------------------------------------------------------- /chapter5/one_onde/wallet.html: -------------------------------------------------------------------------------- 1 | 2 |

pyBTC Block Chain Wallet

3 | 4 |

내 지갑 주소 : {{wallet_id}}

5 |
6 |

내 지갑 잔액 : {{wallet_value}} pyBTC

7 | 8 |
9 | 10 |
11 | 12 | 13 |
14 | 15 |
16 | 17 |
18 |
19 | 20 |
21 | 22 |
23 | 24 |
25 | 26 |
27 | 28 | -------------------------------------------------------------------------------- /chapter4/Python_exec.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 26, 6 | "id": "b429e7dd", 7 | "metadata": {}, 8 | "outputs": [ 9 | { 10 | "name": "stdout", 11 | "output_type": "stream", 12 | "text": [ 13 | "8\n" 14 | ] 15 | } 16 | ], 17 | "source": [ 18 | "def func_add(a,b):\n", 19 | " return a+b\n", 20 | "\n", 21 | "print(func_add(3,5))" 22 | ] 23 | }, 24 | { 25 | "cell_type": "code", 26 | "execution_count": 1, 27 | "id": "9f5a02a5", 28 | "metadata": {}, 29 | "outputs": [ 30 | { 31 | "data": { 32 | "text/plain": [ 33 | "'\\ndef func_add(a,b):\\n return a+b\\n'" 34 | ] 35 | }, 36 | "execution_count": 1, 37 | "metadata": {}, 38 | "output_type": "execute_result" 39 | } 40 | ], 41 | "source": [ 42 | "\"\"\"\n", 43 | "def func_add(a,b):\n", 44 | " return a+b\n", 45 | "\"\"\"\n", 46 | "\n" 47 | ] 48 | }, 49 | { 50 | "cell_type": "code", 51 | "execution_count": 1, 52 | "id": "9d4ff293", 53 | "metadata": {}, 54 | "outputs": [ 55 | { 56 | "name": "stdout", 57 | "output_type": "stream", 58 | "text": [ 59 | "8\n" 60 | ] 61 | } 62 | ], 63 | "source": [ 64 | "exec(\"\"\"\n", 65 | "def func_add(a,b):\n", 66 | " return a+b\n", 67 | "\n", 68 | "print(func_add(3,5))\n", 69 | "\"\"\")" 70 | ] 71 | } 72 | ], 73 | "metadata": { 74 | "kernelspec": { 75 | "display_name": "Python 3", 76 | "language": "python", 77 | "name": "python3" 78 | }, 79 | "language_info": { 80 | "codemirror_mode": { 81 | "name": "ipython", 82 | "version": 3 83 | }, 84 | "file_extension": ".py", 85 | "mimetype": "text/x-python", 86 | "name": "python", 87 | "nbconvert_exporter": "python", 88 | "pygments_lexer": "ipython3", 89 | "version": "3.8.8" 90 | } 91 | }, 92 | "nbformat": 4, 93 | "nbformat_minor": 5 94 | } 95 | -------------------------------------------------------------------------------- /chapter2/flask.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "id": "d1259ae9", 7 | "metadata": {}, 8 | "outputs": [ 9 | { 10 | "name": "stdout", 11 | "output_type": "stream", 12 | "text": [ 13 | " * Serving Flask app \"__main__\" (lazy loading)\n", 14 | " * Environment: production\n", 15 | " WARNING: This is a development server. Do not use it in a production deployment.\n", 16 | " Use a production WSGI server instead.\n", 17 | " * Debug mode: off\n" 18 | ] 19 | }, 20 | { 21 | "name": "stderr", 22 | "output_type": "stream", 23 | "text": [ 24 | " * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)\n", 25 | "127.0.0.1 - - [10/Jun/2022 22:04:36] \"\u001b[37mGET / HTTP/1.1\u001b[0m\" 200 -\n", 26 | "127.0.0.1 - - [10/Jun/2022 22:04:36] \"\u001b[33mGET /favicon.ico HTTP/1.1\u001b[0m\" 404 -\n", 27 | "127.0.0.1 - - [10/Jun/2022 22:04:41] \"\u001b[37mGET /backend_sample HTTP/1.1\u001b[0m\" 200 -\n" 28 | ] 29 | } 30 | ], 31 | "source": [ 32 | "from flask import Flask\n", 33 | "from datetime import datetime\n", 34 | "from flask import render_template\n", 35 | "\n", 36 | "app = Flask(__name__)\n", 37 | "\n", 38 | "\n", 39 | "@app.route('/')\n", 40 | "def index():\n", 41 | " return 'Flask 웹사이트다!'\n", 42 | "\n", 43 | "@app.route('/html_sample')\n", 44 | "def html_sample():\n", 45 | " return render_template('sample.html')\n", 46 | "\n", 47 | "@app.route('/naver')\n", 48 | "def naver():\n", 49 | " return render_template('naver.html')\n", 50 | "\n", 51 | "@app.route('/backend_sample')\n", 52 | "def backend_sample():\n", 53 | " return render_template('backend_sample.html', backend_result = \"1000 개!!\")\n", 54 | "\n", 55 | "app.run()\n" 56 | ] 57 | }, 58 | { 59 | "cell_type": "code", 60 | "execution_count": null, 61 | "id": "e64d03e1", 62 | "metadata": {}, 63 | "outputs": [], 64 | "source": [] 65 | } 66 | ], 67 | "metadata": { 68 | "kernelspec": { 69 | "display_name": "Python 3", 70 | "language": "python", 71 | "name": "python3" 72 | }, 73 | "language_info": { 74 | "codemirror_mode": { 75 | "name": "ipython", 76 | "version": 3 77 | }, 78 | "file_extension": ".py", 79 | "mimetype": "text/x-python", 80 | "name": "python", 81 | "nbconvert_exporter": "python", 82 | "pygments_lexer": "ipython3", 83 | "version": "3.8.8" 84 | } 85 | }, 86 | "nbformat": 4, 87 | "nbformat_minor": 5 88 | } 89 | -------------------------------------------------------------------------------- /chapter4/NFTWallet.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "d19da799", 6 | "metadata": {}, 7 | "source": [ 8 | "## Packages Import" 9 | ] 10 | }, 11 | { 12 | "cell_type": "code", 13 | "execution_count": 3, 14 | "id": "d205c77b", 15 | "metadata": {}, 16 | "outputs": [], 17 | "source": [ 18 | "from flask import Flask\n", 19 | "from flask import render_template\n", 20 | "from flask import request\n", 21 | "\n", 22 | "import requests\n", 23 | "import json\n", 24 | "import os" 25 | ] 26 | }, 27 | { 28 | "cell_type": "markdown", 29 | "id": "c86e64c8", 30 | "metadata": {}, 31 | "source": [ 32 | "## Flask app 선언" 33 | ] 34 | }, 35 | { 36 | "cell_type": "code", 37 | "execution_count": 4, 38 | "id": "d59038b5", 39 | "metadata": {}, 40 | "outputs": [], 41 | "source": [ 42 | "app = Flask(__name__, template_folder=os.getcwd())" 43 | ] 44 | }, 45 | { 46 | "cell_type": "markdown", 47 | "id": "39bad1cf", 48 | "metadata": {}, 49 | "source": [ 50 | "## NFT Wallet 사이트 \n", 51 | "#### 첫 접속시에는 NFT_Wallet_login.html 파일을 랜더링\n", 52 | "#### POST 방식으로 NFT의 스마트컨트랙트 주소를 입력한 경우,\n", 53 | "#### > 알맞은 지갑주소일 경우 NFT_Wallet.html 파일 랜더링\n", 54 | "#### > 잘못된 지갑주소일 경우 에러 메시지 띄움" 55 | ] 56 | }, 57 | { 58 | "cell_type": "code", 59 | "execution_count": 5, 60 | "id": "124ddcc5", 61 | "metadata": {}, 62 | "outputs": [ 63 | { 64 | "name": "stdout", 65 | "output_type": "stream", 66 | "text": [ 67 | " * Serving Flask app \"__main__\" (lazy loading)\n", 68 | " * Environment: production\n", 69 | " WARNING: This is a development server. Do not use it in a production deployment.\n", 70 | " Use a production WSGI server instead.\n", 71 | " * Debug mode: off\n" 72 | ] 73 | }, 74 | { 75 | "name": "stderr", 76 | "output_type": "stream", 77 | "text": [ 78 | " * Running on http://127.0.0.1:8082/ (Press CTRL+C to quit)\n", 79 | "127.0.0.1 - - [22/Jan/2023 22:11:54] \"\u001b[37mGET / HTTP/1.1\u001b[0m\" 200 -\n", 80 | "127.0.0.1 - - [22/Jan/2023 22:17:20] \"\u001b[37mGET / HTTP/1.1\u001b[0m\" 200 -\n", 81 | "127.0.0.1 - - [22/Jan/2023 22:17:20] \"\u001b[33mGET /favicon.ico HTTP/1.1\u001b[0m\" 404 -\n" 82 | ] 83 | }, 84 | { 85 | "name": "stdout", 86 | "output_type": "stream", 87 | "text": [ 88 | "13fcfd1807c1bb38ec723d6e3a651d89b62f7a4080b2cd7bcc224a15c0c12ce4\n" 89 | ] 90 | }, 91 | { 92 | "name": "stderr", 93 | "output_type": "stream", 94 | "text": [ 95 | "127.0.0.1 - - [22/Jan/2023 22:19:20] \"\u001b[37mPOST / HTTP/1.1\u001b[0m\" 200 -\n" 96 | ] 97 | } 98 | ], 99 | "source": [ 100 | "@app.route('/', methods=['GET', 'POST'])\n", 101 | "def login():\n", 102 | " if request.method=='POST':\n", 103 | " \n", 104 | " contract_address = request.form.to_dict(flat=False)['smart_contract_addr'][0] \n", 105 | " print(contract_address)\n", 106 | " # 블록 정보 호출\n", 107 | " headers = {'Content-Type' : 'application/json; charset=utf-8'}\n", 108 | " res = requests.get(\"http://localhost:5000/chain\", headers=headers)\n", 109 | " res_json = json.loads(res.content)\n", 110 | " nft_TF = False\n", 111 | " ## 스마트 컨트랙트를 호출 및 실행\n", 112 | " for _block in res_json['chain']:\n", 113 | " for _tx in _block['transactions']:\n", 114 | " if _tx['smart_contract']['contract_address'] == contract_address:\n", 115 | " exec( _tx['smart_contract']['contract_code']) \n", 116 | " nft_TF = True\n", 117 | " break\n", 118 | " if nft_TF:\n", 119 | "# print(myNFT)\n", 120 | " return render_template(\"NFT_Wallet.html\", \n", 121 | " nft_name = _tx['smart_contract']['contract_code'].split(\"'\")[3], \n", 122 | " nft_img_url = _tx['smart_contract']['contract_code'].split(\"'\")[7],\n", 123 | " nft_addresss = contract_address\n", 124 | " )\n", 125 | " else:\n", 126 | " return \"잘못된 지갑주소입니다.\"\n", 127 | "\n", 128 | " \n", 129 | " return render_template('NFT_Wallet_login.html')\n", 130 | "app.run(port=8082)" 131 | ] 132 | } 133 | ], 134 | "metadata": { 135 | "kernelspec": { 136 | "display_name": "Python 3", 137 | "language": "python", 138 | "name": "python3" 139 | } 140 | }, 141 | "nbformat": 4, 142 | "nbformat_minor": 5 143 | } 144 | -------------------------------------------------------------------------------- /chapter5/smart_contract/NFTWallet.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "d19da799", 6 | "metadata": {}, 7 | "source": [ 8 | "## Packages Import" 9 | ] 10 | }, 11 | { 12 | "cell_type": "code", 13 | "execution_count": 3, 14 | "id": "d205c77b", 15 | "metadata": {}, 16 | "outputs": [], 17 | "source": [ 18 | "from flask import Flask\n", 19 | "from flask import render_template\n", 20 | "from flask import request\n", 21 | "\n", 22 | "import requests\n", 23 | "import json\n", 24 | "import os" 25 | ] 26 | }, 27 | { 28 | "cell_type": "markdown", 29 | "id": "c86e64c8", 30 | "metadata": {}, 31 | "source": [ 32 | "## Flask app 선언" 33 | ] 34 | }, 35 | { 36 | "cell_type": "code", 37 | "execution_count": 4, 38 | "id": "d59038b5", 39 | "metadata": {}, 40 | "outputs": [], 41 | "source": [ 42 | "app = Flask(__name__, template_folder=os.getcwd())" 43 | ] 44 | }, 45 | { 46 | "cell_type": "markdown", 47 | "id": "39bad1cf", 48 | "metadata": {}, 49 | "source": [ 50 | "## NFT Wallet 사이트 \n", 51 | "#### 첫 접속시에는 NFT_Wallet_login.html 파일을 랜더링\n", 52 | "#### POST 방식으로 NFT의 스마트컨트랙트 주소를 입력한 경우,\n", 53 | "#### > 알맞은 지갑주소일 경우 NFT_Wallet.html 파일 랜더링\n", 54 | "#### > 잘못된 지갑주소일 경우 에러 메시지 띄움" 55 | ] 56 | }, 57 | { 58 | "cell_type": "code", 59 | "execution_count": 5, 60 | "id": "124ddcc5", 61 | "metadata": {}, 62 | "outputs": [ 63 | { 64 | "name": "stdout", 65 | "output_type": "stream", 66 | "text": [ 67 | " * Serving Flask app \"__main__\" (lazy loading)\n", 68 | " * Environment: production\n", 69 | " WARNING: This is a development server. Do not use it in a production deployment.\n", 70 | " Use a production WSGI server instead.\n", 71 | " * Debug mode: off\n" 72 | ] 73 | }, 74 | { 75 | "name": "stderr", 76 | "output_type": "stream", 77 | "text": [ 78 | " * Running on http://127.0.0.1:8082/ (Press CTRL+C to quit)\n", 79 | "127.0.0.1 - - [22/Jan/2023 22:11:54] \"\u001b[37mGET / HTTP/1.1\u001b[0m\" 200 -\n", 80 | "127.0.0.1 - - [22/Jan/2023 22:17:20] \"\u001b[37mGET / HTTP/1.1\u001b[0m\" 200 -\n", 81 | "127.0.0.1 - - [22/Jan/2023 22:17:20] \"\u001b[33mGET /favicon.ico HTTP/1.1\u001b[0m\" 404 -\n" 82 | ] 83 | }, 84 | { 85 | "name": "stdout", 86 | "output_type": "stream", 87 | "text": [ 88 | "13fcfd1807c1bb38ec723d6e3a651d89b62f7a4080b2cd7bcc224a15c0c12ce4\n" 89 | ] 90 | }, 91 | { 92 | "name": "stderr", 93 | "output_type": "stream", 94 | "text": [ 95 | "127.0.0.1 - - [22/Jan/2023 22:19:20] \"\u001b[37mPOST / HTTP/1.1\u001b[0m\" 200 -\n" 96 | ] 97 | } 98 | ], 99 | "source": [ 100 | "@app.route('/', methods=['GET', 'POST'])\n", 101 | "def login():\n", 102 | " if request.method=='POST':\n", 103 | " \n", 104 | " contract_address = request.form.to_dict(flat=False)['smart_contract_addr'][0] \n", 105 | " print(contract_address)\n", 106 | " # 블록 정보 호출\n", 107 | " headers = {'Content-Type' : 'application/json; charset=utf-8'}\n", 108 | " res = requests.get(\"http://localhost:5000/chain\", headers=headers)\n", 109 | " res_json = json.loads(res.content)\n", 110 | " nft_TF = False\n", 111 | " ## 스마트 컨트랙트를 호출 및 실행\n", 112 | " for _block in res_json['chain']:\n", 113 | " for _tx in _block['transactions']:\n", 114 | " if _tx['smart_contract']['contract_address'] == contract_address:\n", 115 | " exec( _tx['smart_contract']['contract_code']) \n", 116 | " nft_TF = True\n", 117 | " break\n", 118 | " if nft_TF:\n", 119 | "# print(myNFT)\n", 120 | " return render_template(\"NFT_Wallet.html\", \n", 121 | " nft_name = _tx['smart_contract']['contract_code'].split(\"'\")[3], \n", 122 | " nft_img_url = _tx['smart_contract']['contract_code'].split(\"'\")[7],\n", 123 | " nft_addresss = contract_address\n", 124 | " )\n", 125 | " else:\n", 126 | " return \"잘못된 지갑주소입니다.\"\n", 127 | "\n", 128 | " \n", 129 | " return render_template('NFT_Wallet_login.html')\n", 130 | "app.run(port=8082)" 131 | ] 132 | } 133 | ], 134 | "metadata": { 135 | "kernelspec": { 136 | "display_name": "Python 3", 137 | "language": "python", 138 | "name": "python3" 139 | } 140 | }, 141 | "nbformat": 4, 142 | "nbformat_minor": 5 143 | } 144 | -------------------------------------------------------------------------------- /chapter2/flask_login.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "id": "d1259ae9", 7 | "metadata": {}, 8 | "outputs": [ 9 | { 10 | "name": "stdout", 11 | "output_type": "stream", 12 | "text": [ 13 | " * Serving Flask app \"__main__\" (lazy loading)\n", 14 | " * Environment: production\n", 15 | " WARNING: This is a development server. Do not use it in a production deployment.\n", 16 | " Use a production WSGI server instead.\n", 17 | " * Debug mode: off\n" 18 | ] 19 | }, 20 | { 21 | "name": "stderr", 22 | "output_type": "stream", 23 | "text": [ 24 | " * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)\n", 25 | "127.0.0.1 - - [30/Aug/2022 22:04:49] \"\u001b[37mGET / HTTP/1.1\u001b[0m\" 200 -\n", 26 | "127.0.0.1 - - [30/Aug/2022 22:04:52] \"\u001b[37mGET /login HTTP/1.1\u001b[0m\" 200 -\n", 27 | "127.0.0.1 - - [30/Aug/2022 22:04:53] \"\u001b[37mPOST /login HTTP/1.1\u001b[0m\" 200 -\n" 28 | ] 29 | }, 30 | { 31 | "name": "stdout", 32 | "output_type": "stream", 33 | "text": [ 34 | "login 버튼을 누름\n", 35 | "{'wallet_id': [''], 'password': ['']}\n" 36 | ] 37 | } 38 | ], 39 | "source": [ 40 | "from flask import Flask\n", 41 | "from datetime import datetime\n", 42 | "from flask import render_template\n", 43 | "from flask import request\n", 44 | "import requests \n", 45 | "\n", 46 | "app = Flask(__name__)\n", 47 | "\n", 48 | "test_name = \"pagongble\"\n", 49 | "test_pw = \"pagongble123\"\n", 50 | "test_coin_count = 10000\n", 51 | "\n", 52 | "@app.route('/')\n", 53 | "def index():\n", 54 | " return 'Flask 웹사이트다!'\n", 55 | "\n", 56 | "@app.route('/login', methods=['GET', 'POST'])\n", 57 | "def login(): \n", 58 | " if request.method=='POST':\n", 59 | " print(\"login 버튼을 누름\")\n", 60 | " input_value = request.form.to_dict(flat=False)\n", 61 | " print(input_value)\n", 62 | " if (input_value['wallet_id'][0] == test_name) & (input_value['password'][0] == test_pw) :\n", 63 | " return \"로그인성공!!!!!!\"\n", 64 | " else:\n", 65 | " return render_template('login.html')\n", 66 | " \n", 67 | " return render_template('login.html')\n", 68 | "\n", 69 | "app.run()\n" 70 | ] 71 | }, 72 | { 73 | "cell_type": "code", 74 | "execution_count": null, 75 | "id": "456bc1de", 76 | "metadata": {}, 77 | "outputs": [], 78 | "source": [] 79 | }, 80 | { 81 | "cell_type": "code", 82 | "execution_count": null, 83 | "id": "cee58aba", 84 | "metadata": {}, 85 | "outputs": [], 86 | "source": [] 87 | }, 88 | { 89 | "cell_type": "code", 90 | "execution_count": null, 91 | "id": "de637bb6", 92 | "metadata": {}, 93 | "outputs": [], 94 | "source": [] 95 | }, 96 | { 97 | "cell_type": "code", 98 | "execution_count": null, 99 | "id": "e63ca904", 100 | "metadata": {}, 101 | "outputs": [], 102 | "source": [] 103 | }, 104 | { 105 | "cell_type": "code", 106 | "execution_count": null, 107 | "id": "6d09c4d6", 108 | "metadata": {}, 109 | "outputs": [], 110 | "source": [] 111 | }, 112 | { 113 | "cell_type": "code", 114 | "execution_count": null, 115 | "id": "b5d4684f", 116 | "metadata": {}, 117 | "outputs": [], 118 | "source": [] 119 | }, 120 | { 121 | "cell_type": "code", 122 | "execution_count": null, 123 | "id": "96215580", 124 | "metadata": {}, 125 | "outputs": [], 126 | "source": [] 127 | }, 128 | { 129 | "cell_type": "code", 130 | "execution_count": null, 131 | "id": "e64d03e1", 132 | "metadata": {}, 133 | "outputs": [], 134 | "source": [ 135 | " if request.method=='POST':\n", 136 | " if (name == test_name)& (pw == test_pw) :\n", 137 | " return render_template('home.html', userName = name,savings = ,used = \"정보없음\")\n", 138 | " else:\n", 139 | " return render_template('home.html', userName = \"사용자정보없음 로그인해주세요!\",savings = \"정보없음\",used = \"정보없음\")\n", 140 | " " 141 | ] 142 | } 143 | ], 144 | "metadata": { 145 | "kernelspec": { 146 | "display_name": "Python 3", 147 | "language": "python", 148 | "name": "python3" 149 | }, 150 | "language_info": { 151 | "codemirror_mode": { 152 | "name": "ipython", 153 | "version": 3 154 | }, 155 | "file_extension": ".py", 156 | "mimetype": "text/x-python", 157 | "name": "python", 158 | "nbconvert_exporter": "python", 159 | "pygments_lexer": "ipython3", 160 | "version": "3.8.8" 161 | } 162 | }, 163 | "nbformat": 4, 164 | "nbformat_minor": 5 165 | } 166 | -------------------------------------------------------------------------------- /chapter2/templates/home.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {% with messages = get_flashed_messages() %} 6 | 7 | 8 | {% if messages %} 9 | 14 | 15 | {% endif %} 16 | 17 | {% endwith %} 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 파공블 Project 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 |

{{userName}} 님의 파공블 코인 현황

56 | 57 |

supported by 파공블 Network

58 | 59 |
60 | 61 | 62 |
63 | 64 | 65 |
66 |
67 |
68 |
69 |
70 |
71 | 현재 소유 코인
72 |
{{savings}}
73 |
74 |
75 | 76 |
77 |
78 |
79 |
80 |
81 | 82 | 83 |
84 |
85 |
86 |
87 |
88 |
89 | 현재까지 소비 코인
90 |
{{used}}
91 |
92 |
93 | 94 |
95 |
96 |
97 |
98 |
99 |
100 | 101 |
102 |
103 |
104 |
105 |
106 |
107 | 코인 보내기
108 |
109 | 110 |
111 | 112 | 보내는사람 지갑주소 :
113 | 받을사람 지갑주소 :
114 | 보낼 코인 : 115 | 116 | 117 | 118 |
119 | 120 | 121 |
122 |
123 |
124 | 125 |
126 |
127 |
128 |
129 |
130 | 131 | 132 | 133 | 134 | 135 |
136 |
137 |
138 |
139 |
140 | 141 | 142 | 143 | 144 | 149 | 150 |



151 | 152 | 153 | {% include "/footer.html" %} 154 | 155 | 156 | -------------------------------------------------------------------------------- /chapter3/node_network/node_network_Wallet.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "id": "bf6a8352", 7 | "metadata": { 8 | "scrolled": true 9 | }, 10 | "outputs": [ 11 | { 12 | "name": "stdout", 13 | "output_type": "stream", 14 | "text": [ 15 | " * Serving Flask app \"__main__\" (lazy loading)\n", 16 | " * Environment: production\n", 17 | " WARNING: This is a development server. Do not use it in a production deployment.\n", 18 | " Use a production WSGI server instead.\n", 19 | " * Debug mode: off\n" 20 | ] 21 | }, 22 | { 23 | "name": "stderr", 24 | "output_type": "stream", 25 | "text": [ 26 | " * Running on http://127.0.0.1:8081/ (Press CTRL+C to quit)\n", 27 | "127.0.0.1 - - [05/Dec/2022 01:30:21] \"\u001b[37mGET / HTTP/1.1\u001b[0m\" 200 -\n" 28 | ] 29 | } 30 | ], 31 | "source": [ 32 | "from flask import Flask\n", 33 | "from datetime import datetime\n", 34 | "from flask import render_template\n", 35 | "from flask import request\n", 36 | "from flask import url_for\n", 37 | "from flask import redirect\n", 38 | "\n", 39 | "import requests\n", 40 | "import json\n", 41 | "import os\n", 42 | "import pandas as pd\n", 43 | "import random\n", 44 | "\n", 45 | "app = Flask(__name__, template_folder=os.getcwd())\n", 46 | "node_port_list = ['5000','5001','5002']\n", 47 | "\n", 48 | "\n", 49 | "@app.route('/', methods=['GET', 'POST'])\n", 50 | "def login():\n", 51 | "\n", 52 | " if request.method=='POST':\n", 53 | " print(\"login 버튼을 누름\")\n", 54 | " input_value = request.form.to_dict(flat=False)\n", 55 | " print(\"login 지갑주소 : \" , input_value)\n", 56 | " \n", 57 | " ## 노드 주소 랜덤 선정\n", 58 | " node_id = random.choice(node_port_list)\n", 59 | " \n", 60 | " ### 기존 user 정보 확인\n", 61 | " headers = {'Content-Type' : 'application/json; charset=utf-8'}\n", 62 | " ## 선정된 노드 주소로 데이터 요청\n", 63 | " res = requests.get(\"http://localhost:\" +node_id + \"/chain\", headers=headers)\n", 64 | " print(\"*\"*8)\n", 65 | " status_json = json.loads(res.text)\n", 66 | " status_json['chain'] \n", 67 | " tx_amount_l = []\n", 68 | " tx_sender_l = []\n", 69 | " tx_reciv_l = []\n", 70 | " tx_time_l = []\n", 71 | "\n", 72 | " for chain_index in range(len(status_json['chain'])):\n", 73 | " chain_tx = status_json['chain'][chain_index]['transactions']\n", 74 | " for each_tx in range(len(chain_tx)):\n", 75 | " tx_amount_l.append(chain_tx[each_tx]['amount'])\n", 76 | " tx_sender_l.append(chain_tx[each_tx]['sender'])\n", 77 | " tx_reciv_l.append(chain_tx[each_tx]['recipient'])\n", 78 | " tx_time_l.append(chain_tx[each_tx]['timestamp'])\n", 79 | "\n", 80 | " df_tx = pd.DataFrame()\n", 81 | " df_tx['timestamp'] = tx_time_l \n", 82 | " df_tx['sender'] = tx_sender_l \n", 83 | " df_tx['recipient'] = tx_reciv_l\n", 84 | " df_tx['amount'] = tx_amount_l \n", 85 | " df_tx\n", 86 | "\n", 87 | "\n", 88 | "\n", 89 | " df_sended = pd.DataFrame(df_tx.groupby('sender')['amount'].sum()).reset_index()\n", 90 | " df_sended.columns = ['user','sended_amount']\n", 91 | " df_received= pd.DataFrame(df_tx.groupby('recipient')['amount'].sum()).reset_index()\n", 92 | " df_received.columns = ['user','received_amount']\n", 93 | " df_received\n", 94 | "\n", 95 | " df_status = pd.merge(df_received,df_sended, on ='user', how= 'outer').fillna(0)\n", 96 | " df_status['balance'] = df_status['received_amount'] - df_status['sended_amount'] \n", 97 | " df_status \n", 98 | " \n", 99 | " \n", 100 | " if (df_status['user']==input_value['wallet_id'][0] ).sum() == 1:\n", 101 | " print(\"로그인성공\")\n", 102 | " return render_template(\"wallet.html\", wallet_id = input_value['wallet_id'][0], \n", 103 | " wallet_value = df_status[df_status['user']== df_status['user'].iloc[0]]['balance'].iloc[0])\n", 104 | " else:\n", 105 | " return \"잘못된 지갑주소입니다.\"\n", 106 | " \n", 107 | " return render_template('login.html')\n", 108 | "\n", 109 | "@app.route('/wallet', methods=['GET', 'POST'])\n", 110 | "def wallet():\n", 111 | " if request.method=='POST':\n", 112 | " send_value = int(request.form.to_dict(flat=False)['send_value'][0] )\n", 113 | " send_target = request.form.to_dict(flat=False)['send_target'][0]\n", 114 | " send_from = request.form.to_dict(flat=False)['send_from'][0]\n", 115 | " \n", 116 | " if send_value > 0:\n", 117 | " print(send_value)\n", 118 | " ## transaction 입력하기\n", 119 | " headers = {'Content-Type' : 'application/json; charset=utf-8'}\n", 120 | " \n", 121 | " ## 노드 주소 랜덤 선정\n", 122 | " data = {\n", 123 | " \"sender\": send_from,\n", 124 | " \"recipient\": send_target,\n", 125 | " \"amount\": send_value,\n", 126 | " }\n", 127 | " \n", 128 | " ## 선정된 노드 주소로 데이터 요청\n", 129 | " requests.post(\"http://localhost:\" +node_id + \"/transactions/new\", headers=headers, data=json.dumps(data)).content\n", 130 | "\n", 131 | " return \"전송 완료!\"\n", 132 | "\n", 133 | " else:\n", 134 | " return \"0 pyBTC 이상 보내주세요!\"\n", 135 | "\n", 136 | " \n", 137 | " \n", 138 | " return render_template('wallet.html')\n", 139 | " \n", 140 | " \n", 141 | " \n", 142 | "app.run(port=8081)\n" 143 | ] 144 | }, 145 | { 146 | "cell_type": "code", 147 | "execution_count": null, 148 | "id": "43140b37", 149 | "metadata": {}, 150 | "outputs": [], 151 | "source": [] 152 | }, 153 | { 154 | "cell_type": "code", 155 | "execution_count": null, 156 | "id": "2b3651a0", 157 | "metadata": {}, 158 | "outputs": [], 159 | "source": [] 160 | } 161 | ], 162 | "metadata": { 163 | "kernelspec": { 164 | "display_name": "Python 3", 165 | "language": "python", 166 | "name": "python3" 167 | }, 168 | "language_info": { 169 | "codemirror_mode": { 170 | "name": "ipython", 171 | "version": 3 172 | }, 173 | "file_extension": ".py", 174 | "mimetype": "text/x-python", 175 | "name": "python", 176 | "nbconvert_exporter": "python", 177 | "pygments_lexer": "ipython3", 178 | "version": "3.8.8" 179 | } 180 | }, 181 | "nbformat": 4, 182 | "nbformat_minor": 5 183 | } 184 | -------------------------------------------------------------------------------- /chapter4/node_chainScan.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "id": "a4458c2a", 7 | "metadata": {}, 8 | "outputs": [ 9 | { 10 | "name": "stdout", 11 | "output_type": "stream", 12 | "text": [ 13 | " * Serving Flask app \"__main__\" (lazy loading)\n", 14 | " * Environment: production\n", 15 | " WARNING: This is a development server. Do not use it in a production deployment.\n", 16 | " Use a production WSGI server instead.\n", 17 | " * Debug mode: off\n" 18 | ] 19 | }, 20 | { 21 | "name": "stderr", 22 | "output_type": "stream", 23 | "text": [ 24 | " * Running on http://127.0.0.1:8080/ (Press CTRL+C to quit)\n", 25 | "127.0.0.1 - - [22/Jan/2023 20:14:44] \"\u001b[37mGET / HTTP/1.1\u001b[0m\" 200 -\n", 26 | "127.0.0.1 - - [22/Jan/2023 20:14:44] \"\u001b[33mGET /favicon.ico HTTP/1.1\u001b[0m\" 404 -\n", 27 | "127.0.0.1 - - [22/Jan/2023 23:04:58] \"\u001b[37mGET / HTTP/1.1\u001b[0m\" 200 -\n", 28 | "127.0.0.1 - - [22/Jan/2023 23:05:16] \"\u001b[37mGET / HTTP/1.1\u001b[0m\" 200 -\n" 29 | ] 30 | } 31 | ], 32 | "source": [ 33 | "from flask import Flask\n", 34 | "from datetime import datetime\n", 35 | "from flask import render_template\n", 36 | "import requests\n", 37 | "import os\n", 38 | "import json\n", 39 | "import pandas as pd\n", 40 | "\n", 41 | "app = Flask(__name__, template_folder=os.getcwd())\n", 42 | "\n", 43 | "@app.route('/')\n", 44 | "def index():\n", 45 | " headers = {'Content-Type' : 'application/json; charset=utf-8'}\n", 46 | " # 블록 체인 내 블록 정보를 제공하는 url(http://localhost:5000/chain)에 request 방식으로 데이터를 요청\n", 47 | " res = requests.get(\"http://localhost:5000/chain\", headers=headers)\n", 48 | " # 요청 결과 데이터(res.text)를 json 으로 로드\n", 49 | " status_json = json.loads(res.text)\n", 50 | " # 결과 데이터를 pandas의 dataframe(df_scan)으로 정리\n", 51 | " df_scan = pd.DataFrame(status_json['chain'] )\n", 52 | " # Front 구성내용이 담길 html(one_node_scan.html)파일에 Dataframe 정보(df_scan)과 블록의 길이(block_len)을 제공\n", 53 | " return render_template('/one_node_scan.html', df_scan = df_scan, block_len = len(df_scan))\n", 54 | "\n", 55 | "app.run(port=8080)" 56 | ] 57 | }, 58 | { 59 | "cell_type": "code", 60 | "execution_count": 3, 61 | "id": "5805dfb3", 62 | "metadata": {}, 63 | "outputs": [ 64 | { 65 | "data": { 66 | "text/html": [ 67 | "
\n", 68 | "\n", 81 | "\n", 82 | " \n", 83 | " \n", 84 | " \n", 85 | " \n", 86 | " \n", 87 | " \n", 88 | " \n", 89 | " \n", 90 | " \n", 91 | " \n", 92 | " \n", 93 | " \n", 94 | " \n", 95 | " \n", 96 | " \n", 97 | " \n", 98 | " \n", 99 | " \n", 100 | " \n", 101 | " \n", 102 | " \n", 103 | " \n", 104 | " \n", 105 | " \n", 106 | " \n", 107 | " \n", 108 | " \n", 109 | " \n", 110 | " \n", 111 | " \n", 112 | " \n", 113 | "
hashindexnonceprevious_hashtimestamptransactions
0c6c94b6b738d384a9b7b779959acc372399efac627c882...110011.674295e+09[]
1f3a6354bf86c2c2f7377db8f72ea518a87e87d2334d772...286502666ea92c04bab1cb71ffc5309df0b5fe7b3f13d11049d0b...1.674295e+09[{'amount': 0, 'recipient': 'smart_contract', ...
\n", 114 | "
" 115 | ], 116 | "text/plain": [ 117 | " hash index nonce \\\n", 118 | "0 c6c94b6b738d384a9b7b779959acc372399efac627c882... 1 100 \n", 119 | "1 f3a6354bf86c2c2f7377db8f72ea518a87e87d2334d772... 2 865026 \n", 120 | "\n", 121 | " previous_hash timestamp \\\n", 122 | "0 1 1.674295e+09 \n", 123 | "1 66ea92c04bab1cb71ffc5309df0b5fe7b3f13d11049d0b... 1.674295e+09 \n", 124 | "\n", 125 | " transactions \n", 126 | "0 [] \n", 127 | "1 [{'amount': 0, 'recipient': 'smart_contract', ... " 128 | ] 129 | }, 130 | "execution_count": 3, 131 | "metadata": {}, 132 | "output_type": "execute_result" 133 | } 134 | ], 135 | "source": [ 136 | "headers = {'Content-Type' : 'application/json; charset=utf-8'}\n", 137 | "# 블록 체인의 블록 정보를 제공하는 url(http://localhost:5000/chain)에 request 방식으로 데이터를 요청\n", 138 | "res = requests.get(\"http://localhost:5000/chain\", headers=headers)\n", 139 | "# 요청 결과 데이터(res.text)를 json 으로 로드\n", 140 | "status_json = json.loads(res.text)\n", 141 | "# 결과 데이터를 pandas의 dataframe(df_scan)으로 정리 \n", 142 | "df_scan = pd.DataFrame(status_json['chain'] )\n", 143 | "df_scan" 144 | ] 145 | }, 146 | { 147 | "cell_type": "code", 148 | "execution_count": 5, 149 | "id": "85d8f405", 150 | "metadata": {}, 151 | "outputs": [ 152 | { 153 | "data": { 154 | "text/plain": [ 155 | "'{\"chain\":[{\"hash\":\"c6c94b6b738d384a9b7b779959acc372399efac627c8822c93d7b964b13de7e2\",\"index\":1,\"nonce\":100,\"previous_hash\":1,\"timestamp\":1674294521.5864878,\"transactions\":[]},{\"hash\":\"f3a6354bf86c2c2f7377db8f72ea518a87e87d2334d7723502ac817915272551\",\"index\":2,\"nonce\":865026,\"previous_hash\":\"66ea92c04bab1cb71ffc5309df0b5fe7b3f13d11049d0b178b1c13b55d23a1b4\",\"timestamp\":1674294542.5710497,\"transactions\":[{\"amount\":0,\"recipient\":\"smart_contract\",\"sender\":\"test_from\",\"smart_contract\":{\"contract_address\":\"myaddress\",\"contract_code\":\"print(\\'hello smart contract\\')\"},\"timestamp\":1674294536.7494242},{\"amount\":0.1,\"recipient\":\"node_5000\",\"sender\":\"master\",\"smart_contract\":{\"contract_address\":\"mining_profit\"},\"timestamp\":1674294542.5710497}]}],\"length\":2}\\n'" 156 | ] 157 | }, 158 | "execution_count": 5, 159 | "metadata": {}, 160 | "output_type": "execute_result" 161 | } 162 | ], 163 | "source": [ 164 | "res.text" 165 | ] 166 | }, 167 | { 168 | "cell_type": "code", 169 | "execution_count": null, 170 | "id": "2f34e237", 171 | "metadata": {}, 172 | "outputs": [], 173 | "source": [ 174 | "\n", 175 | "\n", 176 | "\n", 177 | "@app.route('/')\n", 178 | "def index():\n", 179 | " ## 1번 노드 상태 받기!! >> ㅇㅋㅇㅋ\n", 180 | "\n", 181 | " headers = {'Content-Type' : 'application/json; charset=utf-8'}\n", 182 | " res = requests.get(\"http://localhost:5000/chain\", headers=headers)\n", 183 | " print(res.text )\n", 184 | " print(\"*\"*8)\n", 185 | " status_json = json.loads(res.text)\n", 186 | " status_json['chain'] \n", 187 | " tx_amount_l = []\n", 188 | " tx_sender_l = []\n", 189 | " tx_reciv_l = []\n", 190 | " tx_time_l = []\n", 191 | "\n", 192 | " for chain_index in range(len(status_json['chain'])):\n", 193 | " chain_tx = status_json['chain'][chain_index]['transactions']\n", 194 | " for each_tx in range(len(chain_tx)):\n", 195 | " tx_amount_l.append(chain_tx[each_tx]['amount'])\n", 196 | " tx_sender_l.append(chain_tx[each_tx]['sender'])\n", 197 | " tx_reciv_l.append(chain_tx[each_tx]['recipient'])\n", 198 | " tx_time_l.append(chain_tx[each_tx]['timestamp'])\n", 199 | "\n", 200 | " df_tx = pd.DataFrame()\n", 201 | " df_tx['timestamp'] = tx_time_l \n", 202 | " df_tx['sender'] = tx_sender_l \n", 203 | " df_tx['recipient'] = tx_reciv_l\n", 204 | " df_tx['amount'] = tx_amount_l \n", 205 | " df_tx\n", 206 | "\n", 207 | "\n", 208 | "\n", 209 | " df_sended = pd.DataFrame(df_tx.groupby('sender')['amount'].sum()).reset_index()\n", 210 | " df_sended.columns = ['user','sended_amount']\n", 211 | " df_received= pd.DataFrame(df_tx.groupby('recipient')['amount'].sum()).reset_index()\n", 212 | " df_received.columns = ['user','received_amount']\n", 213 | " df_received\n", 214 | "\n", 215 | " return render_template('one_node_scan.html', )\n", 216 | "\n", 217 | "\n", 218 | "app.run(port=8080)\n" 219 | ] 220 | } 221 | ], 222 | "metadata": { 223 | "kernelspec": { 224 | "display_name": "Python 3", 225 | "language": "python", 226 | "name": "python3" 227 | }, 228 | "language_info": { 229 | "codemirror_mode": { 230 | "name": "ipython", 231 | "version": 3 232 | }, 233 | "file_extension": ".py", 234 | "mimetype": "text/x-python", 235 | "name": "python", 236 | "nbconvert_exporter": "python", 237 | "pygments_lexer": "ipython3", 238 | "version": "3.8.8" 239 | } 240 | }, 241 | "nbformat": 4, 242 | "nbformat_minor": 5 243 | } 244 | -------------------------------------------------------------------------------- /chapter5/one_onde/one_node_chainScan.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "id": "a4458c2a", 7 | "metadata": {}, 8 | "outputs": [ 9 | { 10 | "name": "stdout", 11 | "output_type": "stream", 12 | "text": [ 13 | " * Serving Flask app \"__main__\" (lazy loading)\n", 14 | " * Environment: production\n", 15 | " WARNING: This is a development server. Do not use it in a production deployment.\n", 16 | " Use a production WSGI server instead.\n", 17 | " * Debug mode: off\n" 18 | ] 19 | }, 20 | { 21 | "name": "stderr", 22 | "output_type": "stream", 23 | "text": [ 24 | " * Running on http://127.0.0.1:8080/ (Press CTRL+C to quit)\n", 25 | "127.0.0.1 - - [23/Dec/2022 15:20:54] \"\u001b[37mGET / HTTP/1.1\u001b[0m\" 200 -\n", 26 | "127.0.0.1 - - [23/Dec/2022 15:20:54] \"\u001b[33mGET /favicon.ico HTTP/1.1\u001b[0m\" 404 -\n" 27 | ] 28 | } 29 | ], 30 | "source": [ 31 | "from flask import Flask\n", 32 | "from datetime import datetime\n", 33 | "from flask import render_template\n", 34 | "import requests\n", 35 | "import os\n", 36 | "import json\n", 37 | "import pandas as pd\n", 38 | "\n", 39 | "app = Flask(__name__, template_folder=os.getcwd())\n", 40 | "\n", 41 | "@app.route('/')\n", 42 | "def index():\n", 43 | " headers = {'Content-Type' : 'application/json; charset=utf-8'}\n", 44 | " # 블록 체인 내 블록 정보를 제공하는 url(http://localhost:5000/chain)에 request 방식으로 데이터를 요청\n", 45 | " res = requests.get(\"http://localhost:5000/chain\", headers=headers)\n", 46 | " # 요청 결과 데이터(res.text)를 json 으로 로드\n", 47 | " status_json = json.loads(res.text)\n", 48 | " # 결과 데이터를 pandas의 dataframe(df_scan)으로 정리\n", 49 | " df_scan = pd.DataFrame(status_json['chain'] )\n", 50 | " # Front 구성내용이 담길 html(one_node_scan.html)파일에 Dataframe 정보(df_scan)과 블록의 길이(block_len)을 제공\n", 51 | " return render_template('/one_node_scan.html', df_scan = df_scan, block_len = len(df_scan))\n", 52 | "\n", 53 | "app.run(port=8080)" 54 | ] 55 | }, 56 | { 57 | "cell_type": "code", 58 | "execution_count": 1, 59 | "id": "d4608377", 60 | "metadata": {}, 61 | "outputs": [ 62 | { 63 | "ename": "IndentationError", 64 | "evalue": "unexpected indent (, line 13)", 65 | "output_type": "error", 66 | "traceback": [ 67 | "\u001b[1;36m File \u001b[1;32m\"\"\u001b[1;36m, line \u001b[1;32m13\u001b[0m\n\u001b[1;33m return render_template('/one_node_scan.html', df_scan = df_scan, block_len = len(df_scan))\u001b[0m\n\u001b[1;37m ^\u001b[0m\n\u001b[1;31mIndentationError\u001b[0m\u001b[1;31m:\u001b[0m unexpected indent\n" 68 | ] 69 | } 70 | ], 71 | "source": [] 72 | }, 73 | { 74 | "cell_type": "code", 75 | "execution_count": 4, 76 | "id": "5805dfb3", 77 | "metadata": {}, 78 | "outputs": [ 79 | { 80 | "data": { 81 | "text/html": [ 82 | "
\n", 83 | "\n", 96 | "\n", 97 | " \n", 98 | " \n", 99 | " \n", 100 | " \n", 101 | " \n", 102 | " \n", 103 | " \n", 104 | " \n", 105 | " \n", 106 | " \n", 107 | " \n", 108 | " \n", 109 | " \n", 110 | " \n", 111 | " \n", 112 | " \n", 113 | " \n", 114 | " \n", 115 | " \n", 116 | " \n", 117 | " \n", 118 | " \n", 119 | " \n", 120 | " \n", 121 | " \n", 122 | " \n", 123 | " \n", 124 | " \n", 125 | " \n", 126 | " \n", 127 | " \n", 128 | " \n", 129 | " \n", 130 | " \n", 131 | " \n", 132 | " \n", 133 | "
indexnonceprevious_hashtimestamptransactions
0110011.670135e+09[]
12-8442977c62c824701f0dbccee9b6c621917e89a69fb8e24087bf...1.670135e+09[{'amount': 3, 'recipient': 'test_to', 'sender...
232041689c6be753a014410e60b8e1a41b988fe564382b9e369688...1.670135e+09[{'amount': 30, 'recipient': 'test_to2', 'send...
\n", 134 | "
" 135 | ], 136 | "text/plain": [ 137 | " index nonce previous_hash \\\n", 138 | "0 1 100 1 \n", 139 | "1 2 -844297 7c62c824701f0dbccee9b6c621917e89a69fb8e24087bf... \n", 140 | "2 3 204168 9c6be753a014410e60b8e1a41b988fe564382b9e369688... \n", 141 | "\n", 142 | " timestamp transactions \n", 143 | "0 1.670135e+09 [] \n", 144 | "1 1.670135e+09 [{'amount': 3, 'recipient': 'test_to', 'sender... \n", 145 | "2 1.670135e+09 [{'amount': 30, 'recipient': 'test_to2', 'send... " 146 | ] 147 | }, 148 | "execution_count": 4, 149 | "metadata": {}, 150 | "output_type": "execute_result" 151 | } 152 | ], 153 | "source": [ 154 | "headers = {'Content-Type' : 'application/json; charset=utf-8'}\n", 155 | "# 블록 체인의 블록 정보를 제공하는 url(http://localhost:5000/chain)에 request 방식으로 데이터를 요청\n", 156 | "res = requests.get(\"http://localhost:5000/chain\", headers=headers)\n", 157 | "# 요청 결과 데이터(res.text)를 json 으로 로드\n", 158 | "status_json = json.loads(res.text)\n", 159 | "# 결과 데이터를 pandas의 dataframe(df_scan)으로 정리 \n", 160 | "df_scan = pd.DataFrame(status_json['chain'] )\n", 161 | "df_scan" 162 | ] 163 | }, 164 | { 165 | "cell_type": "code", 166 | "execution_count": null, 167 | "id": "85d8f405", 168 | "metadata": {}, 169 | "outputs": [], 170 | "source": [] 171 | }, 172 | { 173 | "cell_type": "code", 174 | "execution_count": null, 175 | "id": "e15b51c3", 176 | "metadata": {}, 177 | "outputs": [], 178 | "source": [] 179 | }, 180 | { 181 | "cell_type": "code", 182 | "execution_count": null, 183 | "id": "0afab7dc", 184 | "metadata": {}, 185 | "outputs": [], 186 | "source": [] 187 | }, 188 | { 189 | "cell_type": "code", 190 | "execution_count": null, 191 | "id": "eddb61e6", 192 | "metadata": {}, 193 | "outputs": [], 194 | "source": [] 195 | }, 196 | { 197 | "cell_type": "code", 198 | "execution_count": null, 199 | "id": "2f34e237", 200 | "metadata": {}, 201 | "outputs": [], 202 | "source": [ 203 | "\n", 204 | "\n", 205 | "\n", 206 | "@app.route('/')\n", 207 | "def index():\n", 208 | " ## 1번 노드 상태 받기!! >> ㅇㅋㅇㅋ\n", 209 | "\n", 210 | " headers = {'Content-Type' : 'application/json; charset=utf-8'}\n", 211 | " res = requests.get(\"http://localhost:5000/chain\", headers=headers)\n", 212 | " print(res.text )\n", 213 | " print(\"*\"*8)\n", 214 | " status_json = json.loads(res.text)\n", 215 | " status_json['chain'] \n", 216 | " tx_amount_l = []\n", 217 | " tx_sender_l = []\n", 218 | " tx_reciv_l = []\n", 219 | " tx_time_l = []\n", 220 | "\n", 221 | " for chain_index in range(len(status_json['chain'])):\n", 222 | " chain_tx = status_json['chain'][chain_index]['transactions']\n", 223 | " for each_tx in range(len(chain_tx)):\n", 224 | " tx_amount_l.append(chain_tx[each_tx]['amount'])\n", 225 | " tx_sender_l.append(chain_tx[each_tx]['sender'])\n", 226 | " tx_reciv_l.append(chain_tx[each_tx]['recipient'])\n", 227 | " tx_time_l.append(chain_tx[each_tx]['timestamp'])\n", 228 | "\n", 229 | " df_tx = pd.DataFrame()\n", 230 | " df_tx['timestamp'] = tx_time_l \n", 231 | " df_tx['sender'] = tx_sender_l \n", 232 | " df_tx['recipient'] = tx_reciv_l\n", 233 | " df_tx['amount'] = tx_amount_l \n", 234 | " df_tx\n", 235 | "\n", 236 | "\n", 237 | "\n", 238 | " df_sended = pd.DataFrame(df_tx.groupby('sender')['amount'].sum()).reset_index()\n", 239 | " df_sended.columns = ['user','sended_amount']\n", 240 | " df_received= pd.DataFrame(df_tx.groupby('recipient')['amount'].sum()).reset_index()\n", 241 | " df_received.columns = ['user','received_amount']\n", 242 | " df_received\n", 243 | "\n", 244 | " return render_template('one_node_scan.html', )\n", 245 | "\n", 246 | "\n", 247 | "app.run(port=8080)\n" 248 | ] 249 | } 250 | ], 251 | "metadata": { 252 | "kernelspec": { 253 | "display_name": "Python 3", 254 | "language": "python", 255 | "name": "python3" 256 | }, 257 | "language_info": { 258 | "codemirror_mode": { 259 | "name": "ipython", 260 | "version": 3 261 | }, 262 | "file_extension": ".py", 263 | "mimetype": "text/x-python", 264 | "name": "python", 265 | "nbconvert_exporter": "python", 266 | "pygments_lexer": "ipython3", 267 | "version": "3.8.8" 268 | } 269 | }, 270 | "nbformat": 4, 271 | "nbformat_minor": 5 272 | } 273 | -------------------------------------------------------------------------------- /chapter3/one_node/one_node_chainScan.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "id": "a4458c2a", 7 | "metadata": {}, 8 | "outputs": [ 9 | { 10 | "name": "stdout", 11 | "output_type": "stream", 12 | "text": [ 13 | " * Serving Flask app \"__main__\" (lazy loading)\n", 14 | " * Environment: production\n", 15 | " WARNING: This is a development server. Do not use it in a production deployment.\n", 16 | " Use a production WSGI server instead.\n", 17 | " * Debug mode: off\n" 18 | ] 19 | }, 20 | { 21 | "name": "stderr", 22 | "output_type": "stream", 23 | "text": [ 24 | " * Running on http://127.0.0.1:8080/ (Press CTRL+C to quit)\n", 25 | "127.0.0.1 - - [04/Dec/2022 16:37:31] \"\u001b[37mGET / HTTP/1.1\u001b[0m\" 200 -\n", 26 | "127.0.0.1 - - [04/Dec/2022 18:36:32] \"\u001b[37mGET / HTTP/1.1\u001b[0m\" 200 -\n", 27 | "127.0.0.1 - - [04/Dec/2022 22:41:18] \"\u001b[37mGET / HTTP/1.1\u001b[0m\" 200 -\n" 28 | ] 29 | } 30 | ], 31 | "source": [ 32 | "from flask import Flask\n", 33 | "from datetime import datetime\n", 34 | "from flask import render_template\n", 35 | "import requests\n", 36 | "import os\n", 37 | "import json\n", 38 | "import pandas as pd\n", 39 | "\n", 40 | "app = Flask(__name__, template_folder=os.getcwd())\n", 41 | "\n", 42 | "@app.route('/')\n", 43 | "def index():\n", 44 | " headers = {'Content-Type' : 'application/json; charset=utf-8'}\n", 45 | " # 블록 체인 내 블록 정보를 제공하는 url(http://localhost:5000/chain)에 request 방식으로 데이터를 요청\n", 46 | " res = requests.get(\"http://localhost:5000/chain\", headers=headers)\n", 47 | " # 요청 결과 데이터(res.text)를 json 으로 로드\n", 48 | " status_json = json.loads(res.text)\n", 49 | " # 결과 데이터를 pandas의 dataframe(df_scan)으로 정리\n", 50 | " df_scan = pd.DataFrame(status_json['chain'] )\n", 51 | " # Front 구성내용이 담길 html(one_node_scan.html)파일에 Dataframe 정보(df_scan)과 블록의 길이(block_len)을 제공\n", 52 | " return render_template('/one_node_scan.html', df_scan = df_scan, block_len = len(df_scan))\n", 53 | "\n", 54 | "app.run(port=8080)" 55 | ] 56 | }, 57 | { 58 | "cell_type": "code", 59 | "execution_count": 1, 60 | "id": "d4608377", 61 | "metadata": {}, 62 | "outputs": [ 63 | { 64 | "ename": "IndentationError", 65 | "evalue": "unexpected indent (, line 13)", 66 | "output_type": "error", 67 | "traceback": [ 68 | "\u001b[1;36m File \u001b[1;32m\"\"\u001b[1;36m, line \u001b[1;32m13\u001b[0m\n\u001b[1;33m return render_template('/one_node_scan.html', df_scan = df_scan, block_len = len(df_scan))\u001b[0m\n\u001b[1;37m ^\u001b[0m\n\u001b[1;31mIndentationError\u001b[0m\u001b[1;31m:\u001b[0m unexpected indent\n" 69 | ] 70 | } 71 | ], 72 | "source": [] 73 | }, 74 | { 75 | "cell_type": "code", 76 | "execution_count": 4, 77 | "id": "5805dfb3", 78 | "metadata": {}, 79 | "outputs": [ 80 | { 81 | "data": { 82 | "text/html": [ 83 | "
\n", 84 | "\n", 97 | "\n", 98 | " \n", 99 | " \n", 100 | " \n", 101 | " \n", 102 | " \n", 103 | " \n", 104 | " \n", 105 | " \n", 106 | " \n", 107 | " \n", 108 | " \n", 109 | " \n", 110 | " \n", 111 | " \n", 112 | " \n", 113 | " \n", 114 | " \n", 115 | " \n", 116 | " \n", 117 | " \n", 118 | " \n", 119 | " \n", 120 | " \n", 121 | " \n", 122 | " \n", 123 | " \n", 124 | " \n", 125 | " \n", 126 | " \n", 127 | " \n", 128 | " \n", 129 | " \n", 130 | " \n", 131 | " \n", 132 | " \n", 133 | " \n", 134 | "
indexnonceprevious_hashtimestamptransactions
0110011.670135e+09[]
12-8442977c62c824701f0dbccee9b6c621917e89a69fb8e24087bf...1.670135e+09[{'amount': 3, 'recipient': 'test_to', 'sender...
232041689c6be753a014410e60b8e1a41b988fe564382b9e369688...1.670135e+09[{'amount': 30, 'recipient': 'test_to2', 'send...
\n", 135 | "
" 136 | ], 137 | "text/plain": [ 138 | " index nonce previous_hash \\\n", 139 | "0 1 100 1 \n", 140 | "1 2 -844297 7c62c824701f0dbccee9b6c621917e89a69fb8e24087bf... \n", 141 | "2 3 204168 9c6be753a014410e60b8e1a41b988fe564382b9e369688... \n", 142 | "\n", 143 | " timestamp transactions \n", 144 | "0 1.670135e+09 [] \n", 145 | "1 1.670135e+09 [{'amount': 3, 'recipient': 'test_to', 'sender... \n", 146 | "2 1.670135e+09 [{'amount': 30, 'recipient': 'test_to2', 'send... " 147 | ] 148 | }, 149 | "execution_count": 4, 150 | "metadata": {}, 151 | "output_type": "execute_result" 152 | } 153 | ], 154 | "source": [ 155 | "headers = {'Content-Type' : 'application/json; charset=utf-8'}\n", 156 | "# 블록 체인의 블록 정보를 제공하는 url(http://localhost:5000/chain)에 request 방식으로 데이터를 요청\n", 157 | "res = requests.get(\"http://localhost:5000/chain\", headers=headers)\n", 158 | "# 요청 결과 데이터(res.text)를 json 으로 로드\n", 159 | "status_json = json.loads(res.text)\n", 160 | "# 결과 데이터를 pandas의 dataframe(df_scan)으로 정리 \n", 161 | "df_scan = pd.DataFrame(status_json['chain'] )\n", 162 | "df_scan" 163 | ] 164 | }, 165 | { 166 | "cell_type": "code", 167 | "execution_count": null, 168 | "id": "85d8f405", 169 | "metadata": {}, 170 | "outputs": [], 171 | "source": [] 172 | }, 173 | { 174 | "cell_type": "code", 175 | "execution_count": null, 176 | "id": "e15b51c3", 177 | "metadata": {}, 178 | "outputs": [], 179 | "source": [] 180 | }, 181 | { 182 | "cell_type": "code", 183 | "execution_count": null, 184 | "id": "0afab7dc", 185 | "metadata": {}, 186 | "outputs": [], 187 | "source": [] 188 | }, 189 | { 190 | "cell_type": "code", 191 | "execution_count": null, 192 | "id": "eddb61e6", 193 | "metadata": {}, 194 | "outputs": [], 195 | "source": [] 196 | }, 197 | { 198 | "cell_type": "code", 199 | "execution_count": null, 200 | "id": "2f34e237", 201 | "metadata": {}, 202 | "outputs": [], 203 | "source": [ 204 | "\n", 205 | "\n", 206 | "\n", 207 | "@app.route('/')\n", 208 | "def index():\n", 209 | " ## 1번 노드 상태 받기!! >> ㅇㅋㅇㅋ\n", 210 | "\n", 211 | " headers = {'Content-Type' : 'application/json; charset=utf-8'}\n", 212 | " res = requests.get(\"http://localhost:5000/chain\", headers=headers)\n", 213 | " print(res.text )\n", 214 | " print(\"*\"*8)\n", 215 | " status_json = json.loads(res.text)\n", 216 | " status_json['chain'] \n", 217 | " tx_amount_l = []\n", 218 | " tx_sender_l = []\n", 219 | " tx_reciv_l = []\n", 220 | " tx_time_l = []\n", 221 | "\n", 222 | " for chain_index in range(len(status_json['chain'])):\n", 223 | " chain_tx = status_json['chain'][chain_index]['transactions']\n", 224 | " for each_tx in range(len(chain_tx)):\n", 225 | " tx_amount_l.append(chain_tx[each_tx]['amount'])\n", 226 | " tx_sender_l.append(chain_tx[each_tx]['sender'])\n", 227 | " tx_reciv_l.append(chain_tx[each_tx]['recipient'])\n", 228 | " tx_time_l.append(chain_tx[each_tx]['timestamp'])\n", 229 | "\n", 230 | " df_tx = pd.DataFrame()\n", 231 | " df_tx['timestamp'] = tx_time_l \n", 232 | " df_tx['sender'] = tx_sender_l \n", 233 | " df_tx['recipient'] = tx_reciv_l\n", 234 | " df_tx['amount'] = tx_amount_l \n", 235 | " df_tx\n", 236 | "\n", 237 | "\n", 238 | "\n", 239 | " df_sended = pd.DataFrame(df_tx.groupby('sender')['amount'].sum()).reset_index()\n", 240 | " df_sended.columns = ['user','sended_amount']\n", 241 | " df_received= pd.DataFrame(df_tx.groupby('recipient')['amount'].sum()).reset_index()\n", 242 | " df_received.columns = ['user','received_amount']\n", 243 | " df_received\n", 244 | "\n", 245 | " return render_template('one_node_scan.html', )\n", 246 | "\n", 247 | "\n", 248 | "app.run(port=8080)\n" 249 | ] 250 | } 251 | ], 252 | "metadata": { 253 | "kernelspec": { 254 | "display_name": "Python 3", 255 | "language": "python", 256 | "name": "python3" 257 | }, 258 | "language_info": { 259 | "codemirror_mode": { 260 | "name": "ipython", 261 | "version": 3 262 | }, 263 | "file_extension": ".py", 264 | "mimetype": "text/x-python", 265 | "name": "python", 266 | "nbconvert_exporter": "python", 267 | "pygments_lexer": "ipython3", 268 | "version": "3.8.8" 269 | } 270 | }, 271 | "nbformat": 4, 272 | "nbformat_minor": 5 273 | } 274 | -------------------------------------------------------------------------------- /chapter3/node_network/node_network_chainScan.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "id": "43bbd8ed", 7 | "metadata": {}, 8 | "outputs": [ 9 | { 10 | "name": "stdout", 11 | "output_type": "stream", 12 | "text": [ 13 | " * Serving Flask app \"__main__\" (lazy loading)\n", 14 | " * Environment: production\n", 15 | " WARNING: This is a development server. Do not use it in a production deployment.\n", 16 | " Use a production WSGI server instead.\n", 17 | " * Debug mode: off\n" 18 | ] 19 | }, 20 | { 21 | "name": "stderr", 22 | "output_type": "stream", 23 | "text": [ 24 | " * Running on http://127.0.0.1:8080/ (Press CTRL+C to quit)\n", 25 | "127.0.0.1 - - [05/Dec/2022 01:44:25] \"\u001b[37mGET / HTTP/1.1\u001b[0m\" 200 -\n" 26 | ] 27 | }, 28 | { 29 | "name": "stdout", 30 | "output_type": "stream", 31 | "text": [ 32 | "Selected Node : 5001\n" 33 | ] 34 | }, 35 | { 36 | "name": "stderr", 37 | "output_type": "stream", 38 | "text": [ 39 | "127.0.0.1 - - [05/Dec/2022 01:44:28] \"\u001b[37mGET / HTTP/1.1\u001b[0m\" 200 -\n" 40 | ] 41 | }, 42 | { 43 | "name": "stdout", 44 | "output_type": "stream", 45 | "text": [ 46 | "Selected Node : 5001\n" 47 | ] 48 | }, 49 | { 50 | "name": "stderr", 51 | "output_type": "stream", 52 | "text": [ 53 | "127.0.0.1 - - [05/Dec/2022 01:44:32] \"\u001b[37mGET / HTTP/1.1\u001b[0m\" 200 -\n" 54 | ] 55 | }, 56 | { 57 | "name": "stdout", 58 | "output_type": "stream", 59 | "text": [ 60 | "Selected Node : 5000\n" 61 | ] 62 | }, 63 | { 64 | "name": "stderr", 65 | "output_type": "stream", 66 | "text": [ 67 | "127.0.0.1 - - [05/Dec/2022 02:27:09] \"\u001b[37mGET / HTTP/1.1\u001b[0m\" 200 -\n" 68 | ] 69 | }, 70 | { 71 | "name": "stdout", 72 | "output_type": "stream", 73 | "text": [ 74 | "Selected Node : 5002\n" 75 | ] 76 | } 77 | ], 78 | "source": [ 79 | "from flask import Flask\n", 80 | "from datetime import datetime\n", 81 | "from flask import render_template\n", 82 | "import requests\n", 83 | "import os\n", 84 | "import json\n", 85 | "import pandas as pd\n", 86 | "import random\n", 87 | "\n", 88 | "app = Flask(__name__, template_folder=os.getcwd())\n", 89 | "\n", 90 | "node_port_list = ['5000','5001','5002']\n", 91 | "\n", 92 | "@app.route('/')\n", 93 | "def index():\n", 94 | " headers = {'Content-Type' : 'application/json; charset=utf-8'}\n", 95 | " # 블록 체인 내 블록 정보를 제공하는 url(http://localhost:5000/chain)에 request 방식으로 데이터를 요청\n", 96 | " node_id = random.choice(node_port_list)\n", 97 | " res = requests.get(\"http://localhost:\" + node_id + \"/chain\", headers=headers)\n", 98 | " print(\"Selected Node : \", node_id)\n", 99 | " # 요청 결과 데이터(res.text)를 json 으로 로드\n", 100 | " status_json = json.loads(res.text)\n", 101 | " # 결과 데이터를 pandas의 dataframe(df_scan)으로 정리\n", 102 | " df_scan = pd.DataFrame(status_json['chain'] )\n", 103 | " # Front 구성내용이 담길 html(one_node_scan.html)파일에 Dataframe 정보(df_scan)과 블록의 길이(block_len)을 제공\n", 104 | " return render_template('/node_network_scan.html', df_scan = df_scan, block_len = len(df_scan))\n", 105 | "\n", 106 | "app.run(port=8080)" 107 | ] 108 | }, 109 | { 110 | "cell_type": "code", 111 | "execution_count": 4, 112 | "id": "5805dfb3", 113 | "metadata": {}, 114 | "outputs": [ 115 | { 116 | "data": { 117 | "text/html": [ 118 | "
\n", 119 | "\n", 132 | "\n", 133 | " \n", 134 | " \n", 135 | " \n", 136 | " \n", 137 | " \n", 138 | " \n", 139 | " \n", 140 | " \n", 141 | " \n", 142 | " \n", 143 | " \n", 144 | " \n", 145 | " \n", 146 | " \n", 147 | " \n", 148 | " \n", 149 | " \n", 150 | " \n", 151 | " \n", 152 | " \n", 153 | " \n", 154 | " \n", 155 | " \n", 156 | " \n", 157 | " \n", 158 | " \n", 159 | " \n", 160 | " \n", 161 | " \n", 162 | " \n", 163 | " \n", 164 | " \n", 165 | " \n", 166 | " \n", 167 | " \n", 168 | " \n", 169 | "
indexnonceprevious_hashtimestamptransactions
0110011.670135e+09[]
12-8442977c62c824701f0dbccee9b6c621917e89a69fb8e24087bf...1.670135e+09[{'amount': 3, 'recipient': 'test_to', 'sender...
232041689c6be753a014410e60b8e1a41b988fe564382b9e369688...1.670135e+09[{'amount': 30, 'recipient': 'test_to2', 'send...
\n", 170 | "
" 171 | ], 172 | "text/plain": [ 173 | " index nonce previous_hash \\\n", 174 | "0 1 100 1 \n", 175 | "1 2 -844297 7c62c824701f0dbccee9b6c621917e89a69fb8e24087bf... \n", 176 | "2 3 204168 9c6be753a014410e60b8e1a41b988fe564382b9e369688... \n", 177 | "\n", 178 | " timestamp transactions \n", 179 | "0 1.670135e+09 [] \n", 180 | "1 1.670135e+09 [{'amount': 3, 'recipient': 'test_to', 'sender... \n", 181 | "2 1.670135e+09 [{'amount': 30, 'recipient': 'test_to2', 'send... " 182 | ] 183 | }, 184 | "execution_count": 4, 185 | "metadata": {}, 186 | "output_type": "execute_result" 187 | } 188 | ], 189 | "source": [ 190 | "headers = {'Content-Type' : 'application/json; charset=utf-8'}\n", 191 | "# 블록 체인의 블록 정보를 제공하는 url(http://localhost:5000/chain)에 request 방식으로 데이터를 요청\n", 192 | "res = requests.get(\"http://localhost:5000/chain\", headers=headers)\n", 193 | "# 요청 결과 데이터(res.text)를 json 으로 로드\n", 194 | "status_json = json.loads(res.text)\n", 195 | "# 결과 데이터를 pandas의 dataframe(df_scan)으로 정리 \n", 196 | "df_scan = pd.DataFrame(status_json['chain'] )\n", 197 | "df_scan" 198 | ] 199 | }, 200 | { 201 | "cell_type": "code", 202 | "execution_count": null, 203 | "id": "85d8f405", 204 | "metadata": {}, 205 | "outputs": [], 206 | "source": [] 207 | }, 208 | { 209 | "cell_type": "code", 210 | "execution_count": null, 211 | "id": "e15b51c3", 212 | "metadata": {}, 213 | "outputs": [], 214 | "source": [] 215 | }, 216 | { 217 | "cell_type": "code", 218 | "execution_count": null, 219 | "id": "0afab7dc", 220 | "metadata": {}, 221 | "outputs": [], 222 | "source": [] 223 | }, 224 | { 225 | "cell_type": "code", 226 | "execution_count": null, 227 | "id": "eddb61e6", 228 | "metadata": {}, 229 | "outputs": [], 230 | "source": [] 231 | }, 232 | { 233 | "cell_type": "code", 234 | "execution_count": null, 235 | "id": "2f34e237", 236 | "metadata": {}, 237 | "outputs": [], 238 | "source": [ 239 | "\n", 240 | "\n", 241 | "\n", 242 | "@app.route('/')\n", 243 | "def index():\n", 244 | " ## 1번 노드 상태 받기!! >> ㅇㅋㅇㅋ\n", 245 | "\n", 246 | " headers = {'Content-Type' : 'application/json; charset=utf-8'}\n", 247 | " res = requests.get(\"http://localhost:5000/chain\", headers=headers)\n", 248 | " print(res.text )\n", 249 | " print(\"*\"*8)\n", 250 | " status_json = json.loads(res.text)\n", 251 | " status_json['chain'] \n", 252 | " tx_amount_l = []\n", 253 | " tx_sender_l = []\n", 254 | " tx_reciv_l = []\n", 255 | " tx_time_l = []\n", 256 | "\n", 257 | " for chain_index in range(len(status_json['chain'])):\n", 258 | " chain_tx = status_json['chain'][chain_index]['transactions']\n", 259 | " for each_tx in range(len(chain_tx)):\n", 260 | " tx_amount_l.append(chain_tx[each_tx]['amount'])\n", 261 | " tx_sender_l.append(chain_tx[each_tx]['sender'])\n", 262 | " tx_reciv_l.append(chain_tx[each_tx]['recipient'])\n", 263 | " tx_time_l.append(chain_tx[each_tx]['timestamp'])\n", 264 | "\n", 265 | " df_tx = pd.DataFrame()\n", 266 | " df_tx['timestamp'] = tx_time_l \n", 267 | " df_tx['sender'] = tx_sender_l \n", 268 | " df_tx['recipient'] = tx_reciv_l\n", 269 | " df_tx['amount'] = tx_amount_l \n", 270 | " df_tx\n", 271 | "\n", 272 | "\n", 273 | "\n", 274 | " df_sended = pd.DataFrame(df_tx.groupby('sender')['amount'].sum()).reset_index()\n", 275 | " df_sended.columns = ['user','sended_amount']\n", 276 | " df_received= pd.DataFrame(df_tx.groupby('recipient')['amount'].sum()).reset_index()\n", 277 | " df_received.columns = ['user','received_amount']\n", 278 | " df_received\n", 279 | "\n", 280 | " return render_template('one_node_scan.html', )\n", 281 | "\n", 282 | "\n", 283 | "app.run(port=8080)\n" 284 | ] 285 | } 286 | ], 287 | "metadata": { 288 | "kernelspec": { 289 | "display_name": "Python 3", 290 | "language": "python", 291 | "name": "python3" 292 | }, 293 | "language_info": { 294 | "codemirror_mode": { 295 | "name": "ipython", 296 | "version": 3 297 | }, 298 | "file_extension": ".py", 299 | "mimetype": "text/x-python", 300 | "name": "python", 301 | "nbconvert_exporter": "python", 302 | "pygments_lexer": "ipython3", 303 | "version": "3.8.8" 304 | } 305 | }, 306 | "nbformat": 4, 307 | "nbformat_minor": 5 308 | } 309 | -------------------------------------------------------------------------------- /chapter2/basic_code.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "4b161b89", 6 | "metadata": {}, 7 | "source": [ 8 | "## Functions" 9 | ] 10 | }, 11 | { 12 | "cell_type": "code", 13 | "execution_count": 1, 14 | "id": "b42a923e", 15 | "metadata": {}, 16 | "outputs": [], 17 | "source": [ 18 | "def f(x):\n", 19 | " return 2*x + 1" 20 | ] 21 | }, 22 | { 23 | "cell_type": "code", 24 | "execution_count": 2, 25 | "id": "88d7549a", 26 | "metadata": {}, 27 | "outputs": [], 28 | "source": [ 29 | "def f(seller_id, buyer_id):\n", 30 | " return seller_id +\"-\"+ buyer_id" 31 | ] 32 | }, 33 | { 34 | "cell_type": "code", 35 | "execution_count": 3, 36 | "id": "9cac0d9a", 37 | "metadata": {}, 38 | "outputs": [], 39 | "source": [ 40 | "## 블록 해시 함수\n", 41 | "def hash(block):\n", 42 | " # Hashes a Block\n", 43 | " block_string = json.dumps(block, sort_keys=True).encode()\n", 44 | " # hash 라이브러리로 sha256 사용\n", 45 | " return hashlib.sha256(block_string).hexdigest()\n", 46 | "\n", 47 | "## 거래내역 저장함수\n", 48 | "def new_transaction(self, sender, recipient, amount):\n", 49 | " # Adds a new transaction to the list of transaction\n", 50 | " self.current_transaction.append(\n", 51 | " {\n", 52 | " 'sender' : sender, # 송신자\n", 53 | " 'recipient' : recipient, # 수신자\n", 54 | " 'amount' : amount, # 금액\n", 55 | " 'timestamp':time()\n", 56 | " }\n", 57 | " )\n", 58 | " return self.last_block['index'] + 1 \n", 59 | "\n", 60 | "\n", 61 | "# 채굴함수\n", 62 | "def mine():\n", 63 | " \n", 64 | " last_block = blockchain.last_block\n", 65 | " last_proof = last_block['proof']\n", 66 | "\n", 67 | " proof = blockchain.pow(last_proof) ## 여기가 진정한 채굴단계!!!! \n", 68 | "\n", 69 | " blockchain.new_transaction(\n", 70 | " sender=mine_owner, # 채굴시 생성되는 transaction (0 = 운영자)\n", 71 | " recipient=node_identifier, # 지갑 주소처럼 사용\n", 72 | " amount=mine_profit # coinbase transaction 코인 1개를 줄게!!\n", 73 | " )\n", 74 | "\n", 75 | "## 등록된 노드들을 함께 업데이트\n", 76 | " for node in blockchain.nodes:\n", 77 | " \n", 78 | " headers = {'Content-Type' : 'application/json; charset=utf-8'}\n", 79 | " data = {\n", 80 | " \"sender\": mine_owner,\n", 81 | " \"recipient\": node_identifier,\n", 82 | " \"amount\": mine_profit\n", 83 | " }\n", 84 | " requests.post(\"http://\" + node + \"/transactions/new\", headers=headers, data=json.dumps(data))\n", 85 | " \n", 86 | " # Forge the new Block by adding it to the chain\n", 87 | " # 전 블록에 대한 hash를 떠놓고\n", 88 | " previous_hash = blockchain.hash(last_block)\n", 89 | " # 검증하는 걸 넣어서 블록을 새로 생성\n", 90 | " print(\"MINING STARTED\")\n", 91 | " block = blockchain.new_block(proof, previous_hash)\n", 92 | " print(\"MINING FINISHED\")\n", 93 | "\n", 94 | " ## 채굴 성공 후 동료 노드들에게 새로운 블록정보를 업데이트\n", 95 | " ## 그렇게 검증도 받아야하고\n", 96 | " ################\n", 97 | " for node in blockchain.nodes:\n", 98 | " headers = {'Content-Type' : 'application/json; charset=utf-8'}\n", 99 | " data = {\n", 100 | " \"miner_node\": 'http://' + my_ip + \":\" + my_port,\n", 101 | " }\n", 102 | " \n", 103 | " a = requests.get(\"http://\" + node + \"/nodes/resolve\", headers=headers, data =json.dumps(data) ) \n", 104 | "# print(a.text)\n", 105 | " # 이상이 없으면 정상배출\n", 106 | " if \"ERROR\" not in a.text :\n", 107 | " print(\"다른노드가 내블록 검증, 결과 정상!!!!!!\")\n", 108 | " # block 이 제대로 mine 되었다는 정보를 json 형태로 띄워줌\n", 109 | " response = {\n", 110 | " 'message' : 'new block found',\n", 111 | " 'index' : block['index'],\n", 112 | " 'transactions' : block['transactions'],\n", 113 | " 'proof' : block['proof'],\n", 114 | " 'previous_hash' : block['previous_hash']\n", 115 | " }\n", 116 | " \n", 117 | " #이상 발생시\n", 118 | " else:\n", 119 | " 1==1\n", 120 | " print(\"다른노드가 내블록 검증, 에러발생!!!!!!\")\n", 121 | " #문제가 있음 전파\n", 122 | " return jsonify(response), 200\n" 123 | ] 124 | }, 125 | { 126 | "cell_type": "code", 127 | "execution_count": 4, 128 | "id": "7f993198", 129 | "metadata": {}, 130 | "outputs": [], 131 | "source": [ 132 | "## 포유류 객채를 선언!!\n", 133 | "class mammals(object):\n", 134 | " ## 포유류의 형태적 특징\n", 135 | " def __init__(self):\n", 136 | " self.number_of_legs = 4 # 다리가 4개\n", 137 | " self.number_of_mouth = 1 # 입이 1개\n", 138 | " self.number_of_ears = 2 # 귀가 2개\n", 139 | " self.gender = \"MALE\" # 남성 (혹은 \"FEMALE\")\n", 140 | " \n", 141 | "## 포유류의 습성 : 잠잔다\n", 142 | " def sleep(self, sleeping_time):\n", 143 | " time.sleep(sleeping_time) ## sleeping_time 만큼 움직이지 않고 잔다\n", 144 | " \n", 145 | "## 포유류의 습성 : 먹는다\n", 146 | " def eat(self, food):\n", 147 | " \tdigest(food)\n", 148 | " \n", 149 | "## 포유류의 습성 : 소화시킨다\n", 150 | " def digest(self, food):\n", 151 | " \tfood = food / 2 # 음식을 반으로 나눈다!! \n", 152 | " \n", 153 | "## 포유류의 습성 : 배변한다\n", 154 | " def dump(self, food):\n", 155 | " \tfood = 0 # 음식이 다 나가고 0이 된다!!\n" 156 | ] 157 | }, 158 | { 159 | "cell_type": "code", 160 | "execution_count": 6, 161 | "id": "d9e724cd", 162 | "metadata": {}, 163 | "outputs": [], 164 | "source": [ 165 | "## 포유류 객체를 활용하여 강아지를 선언\n", 166 | "dog = mammals()\n", 167 | "\n", 168 | "## 포유류 객체를 활용하여 고양이를 선언\n", 169 | "cat = mammals()\n", 170 | "\n", 171 | "## 포유류 객체를 활용하여 고양이를 선언\n", 172 | "cow = mammals()\n" 173 | ] 174 | }, 175 | { 176 | "cell_type": "code", 177 | "execution_count": 7, 178 | "id": "46da52f7", 179 | "metadata": {}, 180 | "outputs": [ 181 | { 182 | "data": { 183 | "text/plain": [ 184 | "4" 185 | ] 186 | }, 187 | "execution_count": 7, 188 | "metadata": {}, 189 | "output_type": "execute_result" 190 | } 191 | ], 192 | "source": [ 193 | "## 강아지의 다리 개수 확인하기\n", 194 | "dog.number_of_legs" 195 | ] 196 | }, 197 | { 198 | "cell_type": "code", 199 | "execution_count": 9, 200 | "id": "595a3e64", 201 | "metadata": {}, 202 | "outputs": [ 203 | { 204 | "data": { 205 | "text/plain": [ 206 | "4" 207 | ] 208 | }, 209 | "execution_count": 9, 210 | "metadata": {}, 211 | "output_type": "execute_result" 212 | } 213 | ], 214 | "source": [ 215 | "## 고양이의 다리 개수 확인하기\n", 216 | "cat.number_of_legs" 217 | ] 218 | }, 219 | { 220 | "cell_type": "code", 221 | "execution_count": 10, 222 | "id": "b7102653", 223 | "metadata": {}, 224 | "outputs": [], 225 | "source": [ 226 | "## 사고발생: 강아지의 다리가 3개가 됨\n", 227 | "dog.number_of_legs = 3\n" 228 | ] 229 | }, 230 | { 231 | "cell_type": "code", 232 | "execution_count": 11, 233 | "id": "5bb66082", 234 | "metadata": {}, 235 | "outputs": [ 236 | { 237 | "data": { 238 | "text/plain": [ 239 | "3" 240 | ] 241 | }, 242 | "execution_count": 11, 243 | "metadata": {}, 244 | "output_type": "execute_result" 245 | } 246 | ], 247 | "source": [ 248 | "## (사건 후) 강아지의 다리개수 확인하기\n", 249 | "dog.number_of_legs\n" 250 | ] 251 | }, 252 | { 253 | "cell_type": "code", 254 | "execution_count": 23, 255 | "id": "e9ea10ca", 256 | "metadata": {}, 257 | "outputs": [], 258 | "source": [ 259 | "import datetime\n", 260 | "\n", 261 | "## 블록체인이란 객체 선언!\n", 262 | "class Blockchain(object):\n", 263 | " ## 블록체인의 기본 특징 선언\n", 264 | " def __init__(self):\n", 265 | " self.chain = [] # 블록을 연결하는 체인. 처음에는 빈 리스트이다!\n", 266 | " self.current_transaction = [] # 블록내에 기록되는 transaction. 처음에는 빈 리스트이다\n", 267 | "\n", 268 | " # transaction이 추가된다 \n", 269 | " def new_transaction(self, sender, recipient, amount):\n", 270 | " # 거래내역을 추가하기\n", 271 | " ## 현재의 transaction 리스트에 송신자, 수신자 등의 거래내역을 입력한다\n", 272 | " self.current_transaction.append(\n", 273 | " {\n", 274 | " 'sender' : sender, # 송신자\n", 275 | " 'recipient' : recipient, # 수신자\n", 276 | " 'amount' : amount, # 금액\n", 277 | " 'timestamp': datetime.datetime.now().timestamp() # 시간\n", 278 | " }\n", 279 | " )\n", 280 | " return self.last_block['index'] + 1\n", 281 | "\n", 282 | " # 새로운 블록을 만드는 함수\n", 283 | " def new_block(self, proof, previous_hash=None):\n", 284 | " # 지금의 블록에 이어질 새로운 블록을 만든다\n", 285 | " block = {\n", 286 | " 'index' : len(self.chain)+1, ## 지금까지의 체인의 숫자 +1 = 새로운 블록의 인덱스\n", 287 | " 'timestamp' : datetime . datetime .now().timestamp(), # 지금 시간 넣기\n", 288 | " 'transactions' : self.current_transaction, ## 지금까지의 transaction을 넣기\n", 289 | " }\n", 290 | "\n", 291 | " self.current_transaction = [] # 새로 블록이 생겼으니 이제 transaction 은 다시 초기화 \n", 292 | " self.chain.append(block) # 기존 체인에 블록을 넣어 연결!! 블록체인 \n", 293 | " return block\n", 294 | "\n", 295 | " @property\n", 296 | " def last_block(self):\n", 297 | " # 체인의 마지막 블록 가져오기!!\n", 298 | " return self.chain[-1]\n" 299 | ] 300 | }, 301 | { 302 | "cell_type": "code", 303 | "execution_count": 24, 304 | "id": "e8b1e724", 305 | "metadata": {}, 306 | "outputs": [ 307 | { 308 | "data": { 309 | "text/plain": [ 310 | "[{'index': 1, 'timestamp': 1675867229.174079, 'transactions': []}]" 311 | ] 312 | }, 313 | "execution_count": 24, 314 | "metadata": {}, 315 | "output_type": "execute_result" 316 | } 317 | ], 318 | "source": [ 319 | "## 블록체인 객체 선언하기\n", 320 | "sample_blockchain = Blockchain()\n", 321 | "\n", 322 | "## 1. 블록에 새로운 블록 만들기\n", 323 | "sample_blockchain.new_block(proof = \"1\")\n", 324 | "\n", 325 | "sample_blockchain.chain\n" 326 | ] 327 | }, 328 | { 329 | "cell_type": "code", 330 | "execution_count": 25, 331 | "id": "b246f660", 332 | "metadata": {}, 333 | "outputs": [ 334 | { 335 | "data": { 336 | "text/plain": [ 337 | "{'index': 2, 'timestamp': 1675867236.758494, 'transactions': []}" 338 | ] 339 | }, 340 | "execution_count": 25, 341 | "metadata": {}, 342 | "output_type": "execute_result" 343 | } 344 | ], 345 | "source": [ 346 | "sample_blockchain.new_block(proof = \"1\")" 347 | ] 348 | }, 349 | { 350 | "cell_type": "code", 351 | "execution_count": 26, 352 | "id": "765babe5", 353 | "metadata": {}, 354 | "outputs": [ 355 | { 356 | "data": { 357 | "text/plain": [ 358 | "[{'index': 1, 'timestamp': 1675867229.174079, 'transactions': []},\n", 359 | " {'index': 2, 'timestamp': 1675867236.758494, 'transactions': []}]" 360 | ] 361 | }, 362 | "execution_count": 26, 363 | "metadata": {}, 364 | "output_type": "execute_result" 365 | } 366 | ], 367 | "source": [ 368 | "sample_blockchain.chain" 369 | ] 370 | }, 371 | { 372 | "cell_type": "code", 373 | "execution_count": 27, 374 | "id": "6ba53bff", 375 | "metadata": {}, 376 | "outputs": [ 377 | { 378 | "data": { 379 | "text/plain": [ 380 | "3" 381 | ] 382 | }, 383 | "execution_count": 27, 384 | "metadata": {}, 385 | "output_type": "execute_result" 386 | } 387 | ], 388 | "source": [ 389 | "## 블록에 새로운 거래내역 입력하기\n", 390 | "sample_blockchain.new_transaction(sender = \"김민수\", recipient = \"박철수\" , amount = 10)\n" 391 | ] 392 | }, 393 | { 394 | "cell_type": "code", 395 | "execution_count": 28, 396 | "id": "1d7ddd5a", 397 | "metadata": {}, 398 | "outputs": [ 399 | { 400 | "data": { 401 | "text/plain": [ 402 | "{'index': 3,\n", 403 | " 'timestamp': 1675867254.542979,\n", 404 | " 'transactions': [{'sender': '김민수',\n", 405 | " 'recipient': '박철수',\n", 406 | " 'amount': 10,\n", 407 | " 'timestamp': 1675867250.905867}]}" 408 | ] 409 | }, 410 | "execution_count": 28, 411 | "metadata": {}, 412 | "output_type": "execute_result" 413 | } 414 | ], 415 | "source": [ 416 | "sample_blockchain.new_block(proof = \"1\")" 417 | ] 418 | }, 419 | { 420 | "cell_type": "code", 421 | "execution_count": 29, 422 | "id": "791c6295", 423 | "metadata": {}, 424 | "outputs": [ 425 | { 426 | "data": { 427 | "text/plain": [ 428 | "[{'index': 1, 'timestamp': 1675867229.174079, 'transactions': []},\n", 429 | " {'index': 2, 'timestamp': 1675867236.758494, 'transactions': []},\n", 430 | " {'index': 3,\n", 431 | " 'timestamp': 1675867254.542979,\n", 432 | " 'transactions': [{'sender': '김민수',\n", 433 | " 'recipient': '박철수',\n", 434 | " 'amount': 10,\n", 435 | " 'timestamp': 1675867250.905867}]}]" 436 | ] 437 | }, 438 | "execution_count": 29, 439 | "metadata": {}, 440 | "output_type": "execute_result" 441 | } 442 | ], 443 | "source": [ 444 | "sample_blockchain.chain" 445 | ] 446 | } 447 | ], 448 | "metadata": { 449 | "kernelspec": { 450 | "display_name": "Python 3", 451 | "language": "python", 452 | "name": "python3" 453 | }, 454 | "language_info": { 455 | "codemirror_mode": { 456 | "name": "ipython", 457 | "version": 3 458 | }, 459 | "file_extension": ".py", 460 | "mimetype": "text/x-python", 461 | "name": "python", 462 | "nbconvert_exporter": "python", 463 | "pygments_lexer": "ipython3", 464 | "version": "3.8.8" 465 | } 466 | }, 467 | "nbformat": 4, 468 | "nbformat_minor": 5 469 | } 470 | -------------------------------------------------------------------------------- /chapter5/one_onde/one_node.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "09d52509", 6 | "metadata": {}, 7 | "source": [ 8 | "## 관련 패키지 import" 9 | ] 10 | }, 11 | { 12 | "cell_type": "code", 13 | "execution_count": 2, 14 | "id": "6787859c", 15 | "metadata": {}, 16 | "outputs": [], 17 | "source": [ 18 | "import hashlib # hash 함수용 sha256 사용할 라이브러리\n", 19 | "import json\n", 20 | "import time\n", 21 | "import random\n", 22 | "import requests\n", 23 | "from flask import Flask, request, jsonify" 24 | ] 25 | }, 26 | { 27 | "cell_type": "markdown", 28 | "id": "16221830", 29 | "metadata": {}, 30 | "source": [ 31 | "## POS Blockchain 객채 생성" 32 | ] 33 | }, 34 | { 35 | "cell_type": "code", 36 | "execution_count": 3, 37 | "id": "455e3708", 38 | "metadata": {}, 39 | "outputs": [], 40 | "source": [ 41 | "class Blockchain(object):\n", 42 | " \n", 43 | " def __init__(self, account_name, account_weight):\n", 44 | " self.chain = [] # chain에 여러 block들 들어옴\n", 45 | " self.current_transaction = [] # 임시 transaction 넣어줌\n", 46 | " self.nodes = set() # Node 목록을 보관\n", 47 | " self.miner_wallet = {'account_name': account_name, 'weight': account_weight} # 지갑정보 생성\n", 48 | " self.new_block(previous_hash='genesis_block', address = account_name) # genesis block 생성\n", 49 | " self.account_name = account_name\n", 50 | " self.account_weight = account_weight\n", 51 | "\n", 52 | " \n", 53 | " @staticmethod\n", 54 | " def hash(block):\n", 55 | " block_string = json.dumps(block, sort_keys=True).encode() \n", 56 | " return hashlib.sha256(block_string).hexdigest() # hash 라이브러리로 sha256 사용\n", 57 | " \n", 58 | " @property\n", 59 | " def last_block(self):\n", 60 | " return self.chain[-1] # 체인의 마지막 블록 가져오기!!\n", 61 | "\n", 62 | "\n", 63 | " def pos(self):\n", 64 | " winner_list = [] # 각 노드에서 pick_winner 결과 뽑힌 winner 리스트\n", 65 | " time.sleep(1)\n", 66 | " my_winner = self.pick_winner(account_name = self.account_name, account_weight = self.account_weight) \n", 67 | " winner_list.append(my_winner) # winner 리스트에 내노드 결과 넣기\n", 68 | " time.sleep(1)\n", 69 | " \n", 70 | " for target_node in blockchain.nodes: # 다른 노드들도 pick_winner 진행 \n", 71 | " print(target_node)\n", 72 | " headers = {'Content-Type' : 'application/json; charset=utf-8'}\n", 73 | " res = requests.get('http://' + target_node + \"/nodes/pick_winner\", headers=headers)\n", 74 | " winner_info = json.loads(res.content) # 근처 노드들 선정결과 받아와서\n", 75 | " print(winner_info)\n", 76 | " winner_list.append(winner_info['winner']) \n", 77 | "\n", 78 | " final_winner = max(winner_list,key = winner_list.count) # 각 노드들의 pos 결과로 가장 많이 선정된 winner를 최종 winner 로 선정\n", 79 | " print(\"final_winner selected : \", final_winner)\n", 80 | " \n", 81 | " return final_winner\n", 82 | " \n", 83 | " \n", 84 | " def pick_winner(self,account_name, account_weight): ### 누가누가 블록 만들래!! 만들사람 뽑기\n", 85 | " candidate_list = [] # POS 대상자를 뽑을 전체 풀!!\n", 86 | " \n", 87 | " for w in range(account_weight): # 나의 노드들의 weight 수만큼 추가\n", 88 | " candidate_list.append(account_name)\n", 89 | " \n", 90 | " random.shuffle(candidate_list) # 랜덤으로 섞고!\n", 91 | " for x in candidate_list: # 첫번째 node를 winner로 선정\n", 92 | " winner = x\n", 93 | " print(\"WINNER SELECTED : \", winner)\n", 94 | " break\n", 95 | " \n", 96 | " return winner # winner 공개\n", 97 | " \n", 98 | " \n", 99 | " \n", 100 | " def new_transaction(self, sender, recipient, amount, smart_contract):\n", 101 | " self.current_transaction.append(\n", 102 | " {\n", 103 | " 'sender' : sender, # 송신자\n", 104 | " 'recipient' : recipient, # 수신자\n", 105 | " 'amount' : amount, # 금액\n", 106 | " 'timestamp':time.time(),\n", 107 | " 'smart_contract' : smart_contract\n", 108 | " }\n", 109 | " )\n", 110 | " return self.last_block['index'] + 1 \n", 111 | "\n", 112 | " def new_block(self, previous_hash=None, address = ''):\n", 113 | " block = {\n", 114 | " 'index' : len(self.chain)+1,\n", 115 | " 'timestamp' : time.time(), # timestamp from 1970\n", 116 | " 'transactions' : self.current_transaction,\n", 117 | " 'previous_hash' : previous_hash ,\n", 118 | " 'validator' : address\n", 119 | " }\n", 120 | " block[\"hash\"] = self.hash(block)\n", 121 | " self.current_transaction = []\n", 122 | " self.chain.append(block) \n", 123 | " return block\n", 124 | "\n", 125 | " " 126 | ] 127 | }, 128 | { 129 | "cell_type": "markdown", 130 | "id": "cccc5e32", 131 | "metadata": {}, 132 | "source": [ 133 | "## Blockchain 객채를 기반으로 노드 생성" 134 | ] 135 | }, 136 | { 137 | "cell_type": "code", 138 | "execution_count": 4, 139 | "id": "fdd2e589", 140 | "metadata": {}, 141 | "outputs": [], 142 | "source": [ 143 | "my_ip = '0.0.0.0'\n", 144 | "my_port = '5000'\n", 145 | "node_identifier = 'node_'+my_port\n", 146 | "mine_owner = 'master'\n", 147 | "mine_profit = 0.1\n", 148 | "\n", 149 | "blockchain = Blockchain(account_name=mine_owner, account_weight= 100)" 150 | ] 151 | }, 152 | { 153 | "cell_type": "code", 154 | "execution_count": 5, 155 | "id": "2228cdbe", 156 | "metadata": {}, 157 | "outputs": [ 158 | { 159 | "name": "stdout", 160 | "output_type": "stream", 161 | "text": [ 162 | " * Serving Flask app \"__main__\" (lazy loading)\n", 163 | " * Environment: production\n", 164 | " WARNING: This is a development server. Do not use it in a production deployment.\n", 165 | " Use a production WSGI server instead.\n", 166 | " * Debug mode: off\n" 167 | ] 168 | }, 169 | { 170 | "name": "stderr", 171 | "output_type": "stream", 172 | "text": [ 173 | " * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)\n", 174 | "127.0.0.1 - - [02/Feb/2023 19:06:36] \"\u001b[37mGET /chain HTTP/1.1\u001b[0m\" 200 -\n" 175 | ] 176 | }, 177 | { 178 | "name": "stdout", 179 | "output_type": "stream", 180 | "text": [ 181 | "chain info requested!!\n" 182 | ] 183 | }, 184 | { 185 | "name": "stderr", 186 | "output_type": "stream", 187 | "text": [ 188 | "127.0.0.1 - - [02/Feb/2023 19:06:39] \"\u001b[37mPOST /transactions/new HTTP/1.1\u001b[0m\" 201 -\n" 189 | ] 190 | }, 191 | { 192 | "name": "stdout", 193 | "output_type": "stream", 194 | "text": [ 195 | "transactions_new!!! : {'sender': 'test_from', 'recipient': 'test_to', 'amount': 3}\n" 196 | ] 197 | }, 198 | { 199 | "name": "stderr", 200 | "output_type": "stream", 201 | "text": [ 202 | "127.0.0.1 - - [02/Feb/2023 19:06:42] \"\u001b[37mGET /chain HTTP/1.1\u001b[0m\" 200 -\n" 203 | ] 204 | }, 205 | { 206 | "name": "stdout", 207 | "output_type": "stream", 208 | "text": [ 209 | "chain info requested!!\n", 210 | "MINING STARTED\n", 211 | "WINNER SELECTED : master\n" 212 | ] 213 | }, 214 | { 215 | "name": "stderr", 216 | "output_type": "stream", 217 | "text": [ 218 | "127.0.0.1 - - [02/Feb/2023 19:06:46] \"\u001b[37mGET /mine HTTP/1.1\u001b[0m\" 200 -\n" 219 | ] 220 | }, 221 | { 222 | "name": "stdout", 223 | "output_type": "stream", 224 | "text": [ 225 | "final_winner selected : master\n", 226 | "MY NODE IS SELECTED AS MINER NODE\n" 227 | ] 228 | }, 229 | { 230 | "name": "stderr", 231 | "output_type": "stream", 232 | "text": [ 233 | "127.0.0.1 - - [02/Feb/2023 19:06:55] \"\u001b[37mGET /chain HTTP/1.1\u001b[0m\" 200 -\n" 234 | ] 235 | }, 236 | { 237 | "name": "stdout", 238 | "output_type": "stream", 239 | "text": [ 240 | "chain info requested!!\n" 241 | ] 242 | }, 243 | { 244 | "name": "stderr", 245 | "output_type": "stream", 246 | "text": [ 247 | "127.0.0.1 - - [02/Feb/2023 19:07:04] \"\u001b[37mPOST /transactions/new HTTP/1.1\u001b[0m\" 201 -\n" 248 | ] 249 | }, 250 | { 251 | "name": "stdout", 252 | "output_type": "stream", 253 | "text": [ 254 | "transactions_new!!! : {'sender': 'test_from', 'recipient': 'test_to2', 'amount': 30}\n" 255 | ] 256 | }, 257 | { 258 | "name": "stderr", 259 | "output_type": "stream", 260 | "text": [ 261 | "127.0.0.1 - - [02/Feb/2023 19:07:06] \"\u001b[37mPOST /transactions/new HTTP/1.1\u001b[0m\" 201 -\n" 262 | ] 263 | }, 264 | { 265 | "name": "stdout", 266 | "output_type": "stream", 267 | "text": [ 268 | "transactions_new!!! : {'sender': 'test_from', 'recipient': 'test_to3', 'amount': 300}\n", 269 | "MINING STARTED\n", 270 | "WINNER SELECTED : master\n" 271 | ] 272 | }, 273 | { 274 | "name": "stderr", 275 | "output_type": "stream", 276 | "text": [ 277 | "127.0.0.1 - - [02/Feb/2023 19:07:10] \"\u001b[37mGET /mine HTTP/1.1\u001b[0m\" 200 -\n" 278 | ] 279 | }, 280 | { 281 | "name": "stdout", 282 | "output_type": "stream", 283 | "text": [ 284 | "final_winner selected : master\n", 285 | "MY NODE IS SELECTED AS MINER NODE\n" 286 | ] 287 | }, 288 | { 289 | "name": "stderr", 290 | "output_type": "stream", 291 | "text": [ 292 | "127.0.0.1 - - [02/Feb/2023 19:07:12] \"\u001b[37mGET /chain HTTP/1.1\u001b[0m\" 200 -\n" 293 | ] 294 | }, 295 | { 296 | "name": "stdout", 297 | "output_type": "stream", 298 | "text": [ 299 | "chain info requested!!\n" 300 | ] 301 | }, 302 | { 303 | "name": "stderr", 304 | "output_type": "stream", 305 | "text": [ 306 | "127.0.0.1 - - [02/Feb/2023 19:07:43] \"\u001b[37mGET /chain HTTP/1.1\u001b[0m\" 200 -\n" 307 | ] 308 | }, 309 | { 310 | "name": "stdout", 311 | "output_type": "stream", 312 | "text": [ 313 | "chain info requested!!\n" 314 | ] 315 | } 316 | ], 317 | "source": [ 318 | "app = Flask(__name__)\n", 319 | "\n", 320 | "@app.route('/chain', methods=['GET'])\n", 321 | "def full_chain():\n", 322 | " print(\"chain info requested!!\")\n", 323 | " response = {\n", 324 | " 'chain' : blockchain.chain, \n", 325 | " 'length' : len(blockchain.chain), \n", 326 | " }\n", 327 | " return jsonify(response), 200\n", 328 | "\n", 329 | "@app.route('/transactions/new', methods=['POST'])\n", 330 | "def new_transaction():\n", 331 | " values = request.get_json() \n", 332 | " print(\"transactions_new!!! : \", values)\n", 333 | " required = ['sender', 'recipient', 'amount'] \n", 334 | "\n", 335 | " if not all(k in values for k in required):\n", 336 | " return 'missing values', 400\n", 337 | " \n", 338 | " if 'smart_contract' not in values:\n", 339 | " values['smart_contract'] = 'empty'\n", 340 | "\n", 341 | " index = blockchain.new_transaction(values['sender'],values['recipient'],\n", 342 | "values['amount'], values['smart_contract'])\n", 343 | " \n", 344 | " response = {'message' : 'Transaction will be added to Block {%s}' % index}\n", 345 | " return jsonify(response), 201\n", 346 | "\n", 347 | "\n", 348 | "@app.route('/mine', methods=['GET'])\n", 349 | "def mine():\n", 350 | " print(\"MINING STARTED\") \n", 351 | " final_winner = blockchain.pos() \n", 352 | " \n", 353 | " if final_winner == blockchain.account_name: # 만약 본 노드가 winner로 선정되었으면 아래와 같이\n", 354 | "\n", 355 | " blockchain.new_transaction( # 나에게 보상을 주고\n", 356 | " sender=mine_owner, \n", 357 | " recipient=node_identifier, \n", 358 | " amount=mine_profit, # coinbase transaction \n", 359 | " smart_contract={\"contract_address\":\"mining_profit\"}, \n", 360 | " )\n", 361 | "\n", 362 | " previous_hash = blockchain.hash(blockchain.chain[-1])\n", 363 | " block = blockchain.new_block(previous_hash = previous_hash, address = mine_owner) # 신규 블록 생성\n", 364 | " print(\"MY NODE IS SELECTED AS MINER NODE\")\n", 365 | "\n", 366 | " response = {\n", 367 | " 'message' : 'new block found',\n", 368 | " 'index' : block['index'],\n", 369 | " 'transactions' : block['transactions'],\n", 370 | " 'nonce' : block['validator'],\n", 371 | " 'previous_hash' : block['previous_hash'],\n", 372 | " 'hash' : block['hash']\n", 373 | " }\n", 374 | "\n", 375 | " return jsonify(response), 200\n", 376 | " \n", 377 | " else : # isWinner = False : 본 노드가 winner가 아님\n", 378 | " print(\"MY NODE IS NOT SELECTED AS MINER NODE\")\n", 379 | "\n", 380 | " response = {\n", 381 | " 'message' : 'NOT SELECTED'\n", 382 | " }\n", 383 | "\n", 384 | " return jsonify(response), 200\n", 385 | " \n", 386 | "if __name__ == '__main__':\n", 387 | " app.run(host=my_ip, port=my_port)\n" 388 | ] 389 | }, 390 | { 391 | "cell_type": "code", 392 | "execution_count": null, 393 | "id": "6758863d", 394 | "metadata": {}, 395 | "outputs": [], 396 | "source": [] 397 | } 398 | ], 399 | "metadata": { 400 | "kernelspec": { 401 | "display_name": "Python 3", 402 | "language": "python", 403 | "name": "python3" 404 | }, 405 | "language_info": { 406 | "codemirror_mode": { 407 | "name": "ipython", 408 | "version": 3 409 | }, 410 | "file_extension": ".py", 411 | "mimetype": "text/x-python", 412 | "name": "python", 413 | "nbconvert_exporter": "python", 414 | "pygments_lexer": "ipython3", 415 | "version": "3.8.8" 416 | } 417 | }, 418 | "nbformat": 4, 419 | "nbformat_minor": 5 420 | } 421 | -------------------------------------------------------------------------------- /chapter3/one_node/one_node_command.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 13, 6 | "id": "e4397b59", 7 | "metadata": {}, 8 | "outputs": [], 9 | "source": [ 10 | "import requests\n", 11 | "import json\n", 12 | "import pandas as pd\n", 13 | "import hashlib # hash 함수용 sha256 사용할 라이브러리\n", 14 | "import random" 15 | ] 16 | }, 17 | { 18 | "cell_type": "markdown", 19 | "id": "4a2c3864", 20 | "metadata": {}, 21 | "source": [ 22 | "## 노드의 블록 정보 확인" 23 | ] 24 | }, 25 | { 26 | "cell_type": "code", 27 | "execution_count": 14, 28 | "id": "26d585ba", 29 | "metadata": {}, 30 | "outputs": [ 31 | { 32 | "data": { 33 | "text/plain": [ 34 | "{'chain': [{'index': 1,\n", 35 | " 'nonce': 100,\n", 36 | " 'previous_hash': 1,\n", 37 | " 'timestamp': 1671776350.615425,\n", 38 | " 'transactions': []}],\n", 39 | " 'length': 1}" 40 | ] 41 | }, 42 | "execution_count": 14, 43 | "metadata": {}, 44 | "output_type": "execute_result" 45 | } 46 | ], 47 | "source": [ 48 | "headers = {'Content-Type' : 'application/json; charset=utf-8'}\n", 49 | "res = requests.get(\"http://localhost:5000/chain\", headers=headers)\n", 50 | "json.loads(res.content)" 51 | ] 52 | }, 53 | { 54 | "cell_type": "markdown", 55 | "id": "22f62fb1", 56 | "metadata": {}, 57 | "source": [ 58 | "## transaction 입력하기" 59 | ] 60 | }, 61 | { 62 | "cell_type": "code", 63 | "execution_count": 15, 64 | "id": "fe3958ef", 65 | "metadata": {}, 66 | "outputs": [ 67 | { 68 | "data": { 69 | "text/plain": [ 70 | "b'{\"message\":\"Transaction will be added to Block {2}\"}\\n'" 71 | ] 72 | }, 73 | "execution_count": 15, 74 | "metadata": {}, 75 | "output_type": "execute_result" 76 | } 77 | ], 78 | "source": [ 79 | "headers = {'Content-Type' : 'application/json; charset=utf-8'}\n", 80 | "data = {\n", 81 | " \"sender\": \"test_from\",\n", 82 | " \"recipient\": \"test_to\",\n", 83 | " \"amount\": 3,\n", 84 | " \"smart_contract\": {\"contract_address\":\"myaddress\"}\n", 85 | "}\n", 86 | "requests.post(\"http://localhost:5000/transactions/new\", headers=headers, data=json.dumps(data)).content" 87 | ] 88 | }, 89 | { 90 | "cell_type": "markdown", 91 | "id": "8ffed183", 92 | "metadata": {}, 93 | "source": [ 94 | "## 노드의 블록정보 확인 - 2" 95 | ] 96 | }, 97 | { 98 | "cell_type": "code", 99 | "execution_count": 16, 100 | "id": "cd862740", 101 | "metadata": {}, 102 | "outputs": [ 103 | { 104 | "data": { 105 | "text/plain": [ 106 | "{'chain': [{'index': 1,\n", 107 | " 'nonce': 100,\n", 108 | " 'previous_hash': 1,\n", 109 | " 'timestamp': 1671776350.615425,\n", 110 | " 'transactions': []}],\n", 111 | " 'length': 1}" 112 | ] 113 | }, 114 | "execution_count": 16, 115 | "metadata": {}, 116 | "output_type": "execute_result" 117 | } 118 | ], 119 | "source": [ 120 | "headers = {'Content-Type' : 'application/json; charset=utf-8'}\n", 121 | "res = requests.get(\"http://localhost:5000/chain\", headers=headers)\n", 122 | "json.loads(res.content)" 123 | ] 124 | }, 125 | { 126 | "cell_type": "markdown", 127 | "id": "de078099", 128 | "metadata": {}, 129 | "source": [ 130 | "## 채굴하기 - 1" 131 | ] 132 | }, 133 | { 134 | "cell_type": "code", 135 | "execution_count": 17, 136 | "id": "ca6e8f84", 137 | "metadata": {}, 138 | "outputs": [ 139 | { 140 | "name": "stdout", 141 | "output_type": "stream", 142 | "text": [ 143 | "\n" 144 | ] 145 | } 146 | ], 147 | "source": [ 148 | "headers = {'Content-Type' : 'application/json; charset=utf-8'}\n", 149 | "res = requests.get(\"http://localhost:5000/mine\")\n", 150 | "print(res)" 151 | ] 152 | }, 153 | { 154 | "cell_type": "markdown", 155 | "id": "a8f956fe", 156 | "metadata": {}, 157 | "source": [ 158 | "## 노드의 블록정보 확인 - 3" 159 | ] 160 | }, 161 | { 162 | "cell_type": "code", 163 | "execution_count": 18, 164 | "id": "17f93c4a", 165 | "metadata": {}, 166 | "outputs": [ 167 | { 168 | "data": { 169 | "text/plain": [ 170 | "{'chain': [{'index': 1,\n", 171 | " 'nonce': 100,\n", 172 | " 'previous_hash': 1,\n", 173 | " 'timestamp': 1671776350.615425,\n", 174 | " 'transactions': []},\n", 175 | " {'index': 2,\n", 176 | " 'nonce': 331633,\n", 177 | " 'previous_hash': '25a5c3d5d8db13f7be45610326e64bcff88923f17d917abb7a7dfc962fd9dd7f',\n", 178 | " 'timestamp': 1671776421.2989593,\n", 179 | " 'transactions': [{'amount': 3,\n", 180 | " 'recipient': 'test_to',\n", 181 | " 'sender': 'test_from',\n", 182 | " 'smart_contract': {'contract_address': 'myaddress'},\n", 183 | " 'timestamp': 1671776417.166413},\n", 184 | " {'amount': 0.1,\n", 185 | " 'recipient': 'node_5000',\n", 186 | " 'sender': 'master',\n", 187 | " 'smart_contract': {'contract_address': 'mining_profit'},\n", 188 | " 'timestamp': 1671776421.2989593}]}],\n", 189 | " 'length': 2}" 190 | ] 191 | }, 192 | "execution_count": 18, 193 | "metadata": {}, 194 | "output_type": "execute_result" 195 | } 196 | ], 197 | "source": [ 198 | "headers = {'Content-Type' : 'application/json; charset=utf-8'}\n", 199 | "res = requests.get(\"http://localhost:5000/chain\", headers=headers)\n", 200 | "json.loads(res.content)" 201 | ] 202 | }, 203 | { 204 | "cell_type": "markdown", 205 | "id": "cff38e24", 206 | "metadata": {}, 207 | "source": [ 208 | "## transaction 입력하기 -2 / 채굴 / 블록정보 확인" 209 | ] 210 | }, 211 | { 212 | "cell_type": "code", 213 | "execution_count": 25, 214 | "id": "a1355a99", 215 | "metadata": { 216 | "scrolled": false 217 | }, 218 | "outputs": [ 219 | { 220 | "name": "stdout", 221 | "output_type": "stream", 222 | "text": [ 223 | "\n" 224 | ] 225 | } 226 | ], 227 | "source": [ 228 | "## transaction 입력하기 -2\n", 229 | "headers = {'Content-Type' : 'application/json; charset=utf-8'}\n", 230 | "data = {\n", 231 | " \"sender\": \"test_from\",\n", 232 | " \"recipient\": \"test_to2\",\n", 233 | " \"amount\": 30,\n", 234 | "}\n", 235 | "requests.post(\"http://localhost:5000/transactions/new\", headers=headers, data=json.dumps(data)).content\n", 236 | "\n", 237 | "## transaction3 입력하기\n", 238 | "headers = {'Content-Type' : 'application/json; charset=utf-8'}\n", 239 | "data = {\n", 240 | " \"sender\": \"test_from\",\n", 241 | " \"recipient\": \"test_to3\",\n", 242 | " \"amount\": 300,\n", 243 | "}\n", 244 | "requests.post(\"http://localhost:5000/transactions/new\", headers=headers, data=json.dumps(data)).content\n", 245 | "\n", 246 | "## 채굴하기\n", 247 | "headers = {'Content-Type' : 'application/json; charset=utf-8'}\n", 248 | "res = requests.get(\"http://localhost:5000/mine\")\n", 249 | "print(res)\n", 250 | "\n", 251 | "## 노드의 블록정보 확인 - 4\n", 252 | "headers = {'Content-Type' : 'application/json; charset=utf-8'}\n", 253 | "res = requests.get(\"http://localhost:5000/chain\", headers=headers)" 254 | ] 255 | }, 256 | { 257 | "cell_type": "code", 258 | "execution_count": 26, 259 | "id": "748b946c", 260 | "metadata": {}, 261 | "outputs": [ 262 | { 263 | "data": { 264 | "text/plain": [ 265 | "'{\"chain\":[{\"index\":1,\"nonce\":100,\"previous_hash\":1,\"timestamp\":1670135208.6944222,\"transactions\":[]},{\"index\":2,\"nonce\":-844297,\"previous_hash\":\"7c62c824701f0dbccee9b6c621917e89a69fb8e24087bfaf1054a3a465b5db84\",\"timestamp\":1670135305.4202778,\"transactions\":[{\"amount\":3,\"recipient\":\"test_to\",\"sender\":\"test_from\",\"timestamp\":1670135301.2886367},{\"amount\":0.1,\"recipient\":\"node_5000\",\"sender\":\"master\",\"timestamp\":1670135305.4202778}]},{\"index\":3,\"nonce\":204168,\"previous_hash\":\"9c6be753a014410e60b8e1a41b988fe564382b9e369688ee714f17a3f54d74e4\",\"timestamp\":1670135313.691937,\"transactions\":[{\"amount\":30,\"recipient\":\"test_to2\",\"sender\":\"test_from\",\"timestamp\":1670135309.5391397},{\"amount\":300,\"recipient\":\"test_to3\",\"sender\":\"test_from\",\"timestamp\":1670135311.602241},{\"amount\":0.1,\"recipient\":\"node_5000\",\"sender\":\"master\",\"timestamp\":1670135313.691937}]}],\"length\":3}\\n'" 266 | ] 267 | }, 268 | "execution_count": 26, 269 | "metadata": {}, 270 | "output_type": "execute_result" 271 | } 272 | ], 273 | "source": [ 274 | "res.text" 275 | ] 276 | }, 277 | { 278 | "cell_type": "markdown", 279 | "id": "6d68c4a3", 280 | "metadata": {}, 281 | "source": [ 282 | "## Pandas 를 활용한 거래내역 조회" 283 | ] 284 | }, 285 | { 286 | "cell_type": "code", 287 | "execution_count": 27, 288 | "id": "fb5a1c13", 289 | "metadata": {}, 290 | "outputs": [ 291 | { 292 | "data": { 293 | "text/html": [ 294 | "
\n", 295 | "\n", 308 | "\n", 309 | " \n", 310 | " \n", 311 | " \n", 312 | " \n", 313 | " \n", 314 | " \n", 315 | " \n", 316 | " \n", 317 | " \n", 318 | " \n", 319 | " \n", 320 | " \n", 321 | " \n", 322 | " \n", 323 | " \n", 324 | " \n", 325 | " \n", 326 | " \n", 327 | " \n", 328 | " \n", 329 | " \n", 330 | " \n", 331 | " \n", 332 | " \n", 333 | " \n", 334 | " \n", 335 | " \n", 336 | " \n", 337 | " \n", 338 | " \n", 339 | " \n", 340 | " \n", 341 | " \n", 342 | " \n", 343 | " \n", 344 | " \n", 345 | " \n", 346 | " \n", 347 | " \n", 348 | " \n", 349 | " \n", 350 | " \n", 351 | " \n", 352 | " \n", 353 | " \n", 354 | " \n", 355 | "
timestampsenderrecipientamount
01.670135e+09test_fromtest_to3.0
11.670135e+09masternode_50000.1
21.670135e+09test_fromtest_to230.0
31.670135e+09test_fromtest_to3300.0
41.670135e+09masternode_50000.1
\n", 356 | "
" 357 | ], 358 | "text/plain": [ 359 | " timestamp sender recipient amount\n", 360 | "0 1.670135e+09 test_from test_to 3.0\n", 361 | "1 1.670135e+09 master node_5000 0.1\n", 362 | "2 1.670135e+09 test_from test_to2 30.0\n", 363 | "3 1.670135e+09 test_from test_to3 300.0\n", 364 | "4 1.670135e+09 master node_5000 0.1" 365 | ] 366 | }, 367 | "execution_count": 27, 368 | "metadata": {}, 369 | "output_type": "execute_result" 370 | } 371 | ], 372 | "source": [ 373 | "status_json = json.loads(res.text)\n", 374 | "status_json['chain'] \n", 375 | "tx_amount_l = []\n", 376 | "tx_sender_l = []\n", 377 | "tx_reciv_l = []\n", 378 | "tx_time_l = []\n", 379 | "\n", 380 | "for chain_index in range(len(status_json['chain'])):\n", 381 | " chain_tx = status_json['chain'][chain_index]['transactions']\n", 382 | " for each_tx in range(len(chain_tx)):\n", 383 | " tx_amount_l.append(chain_tx[each_tx]['amount'])\n", 384 | " tx_sender_l.append(chain_tx[each_tx]['sender'])\n", 385 | " tx_reciv_l.append(chain_tx[each_tx]['recipient'])\n", 386 | " tx_time_l.append(chain_tx[each_tx]['timestamp'])\n", 387 | "\n", 388 | "df_tx = pd.DataFrame()\n", 389 | "df_tx['timestamp'] = tx_time_l \n", 390 | "df_tx['sender'] = tx_sender_l \n", 391 | "df_tx['recipient'] = tx_reciv_l\n", 392 | "df_tx['amount'] = tx_amount_l \n", 393 | "df_tx" 394 | ] 395 | }, 396 | { 397 | "cell_type": "markdown", 398 | "id": "39389b1c", 399 | "metadata": {}, 400 | "source": [ 401 | "## 거래내역 기반 계정별 잔액 조회" 402 | ] 403 | }, 404 | { 405 | "cell_type": "code", 406 | "execution_count": 16, 407 | "id": "ff011825", 408 | "metadata": {}, 409 | "outputs": [ 410 | { 411 | "data": { 412 | "text/html": [ 413 | "
\n", 414 | "\n", 427 | "\n", 428 | " \n", 429 | " \n", 430 | " \n", 431 | " \n", 432 | " \n", 433 | " \n", 434 | " \n", 435 | " \n", 436 | " \n", 437 | " \n", 438 | " \n", 439 | " \n", 440 | " \n", 441 | " \n", 442 | " \n", 443 | " \n", 444 | " \n", 445 | " \n", 446 | " \n", 447 | " \n", 448 | " \n", 449 | " \n", 450 | " \n", 451 | " \n", 452 | " \n", 453 | " \n", 454 | " \n", 455 | " \n", 456 | " \n", 457 | " \n", 458 | " \n", 459 | " \n", 460 | " \n", 461 | " \n", 462 | " \n", 463 | " \n", 464 | " \n", 465 | " \n", 466 | " \n", 467 | " \n", 468 | " \n", 469 | " \n", 470 | " \n", 471 | " \n", 472 | " \n", 473 | " \n", 474 | " \n", 475 | " \n", 476 | " \n", 477 | " \n", 478 | " \n", 479 | " \n", 480 | " \n", 481 | "
userreceived_amountsended_amountbalance
0node_50000.30.00.3
1test_to6.00.06.0
2test_to230.00.030.0
3test_to3300.00.0300.0
4master0.00.3-0.3
5test_from0.0336.0-336.0
\n", 482 | "
" 483 | ], 484 | "text/plain": [ 485 | " user received_amount sended_amount balance\n", 486 | "0 node_5000 0.3 0.0 0.3\n", 487 | "1 test_to 6.0 0.0 6.0\n", 488 | "2 test_to2 30.0 0.0 30.0\n", 489 | "3 test_to3 300.0 0.0 300.0\n", 490 | "4 master 0.0 0.3 -0.3\n", 491 | "5 test_from 0.0 336.0 -336.0" 492 | ] 493 | }, 494 | "execution_count": 16, 495 | "metadata": {}, 496 | "output_type": "execute_result" 497 | } 498 | ], 499 | "source": [ 500 | "df_sended = pd.DataFrame(df_tx.groupby('sender')['amount'].sum()).reset_index()\n", 501 | "df_sended.columns = ['user','sended_amount']\n", 502 | "df_received= pd.DataFrame(df_tx.groupby('recipient')['amount'].sum()).reset_index()\n", 503 | "df_received.columns = ['user','received_amount']\n", 504 | "df_received\n", 505 | "\n", 506 | "df_status = pd.merge(df_received,df_sended, on ='user', how= 'outer').fillna(0)\n", 507 | "df_status['balance'] = df_status['received_amount'] - df_status['sended_amount'] \n", 508 | "df_status" 509 | ] 510 | } 511 | ], 512 | "metadata": { 513 | "kernelspec": { 514 | "display_name": "Python 3", 515 | "language": "python", 516 | "name": "python3" 517 | }, 518 | "language_info": { 519 | "codemirror_mode": { 520 | "name": "ipython", 521 | "version": 3 522 | }, 523 | "file_extension": ".py", 524 | "mimetype": "text/x-python", 525 | "name": "python", 526 | "nbconvert_exporter": "python", 527 | "pygments_lexer": "ipython3", 528 | "version": "3.8.8" 529 | } 530 | }, 531 | "nbformat": 4, 532 | "nbformat_minor": 5 533 | } 534 | -------------------------------------------------------------------------------- /chapter4/node.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "09d52509", 6 | "metadata": {}, 7 | "source": [ 8 | "## 관련 패키지 import" 9 | ] 10 | }, 11 | { 12 | "cell_type": "code", 13 | "execution_count": 1, 14 | "id": "6787859c", 15 | "metadata": {}, 16 | "outputs": [], 17 | "source": [ 18 | "import hashlib # hash 함수용 sha256 사용할 라이브러리\n", 19 | "import json\n", 20 | "from time import time\n", 21 | "import random\n", 22 | "import requests\n", 23 | "from flask import Flask, request, jsonify\n", 24 | "import datetime" 25 | ] 26 | }, 27 | { 28 | "cell_type": "markdown", 29 | "id": "16221830", 30 | "metadata": {}, 31 | "source": [ 32 | "## Blockchain 객채 생성" 33 | ] 34 | }, 35 | { 36 | "cell_type": "code", 37 | "execution_count": 2, 38 | "id": "455e3708", 39 | "metadata": {}, 40 | "outputs": [], 41 | "source": [ 42 | "class Blockchain(object):\n", 43 | " \n", 44 | " def __init__(self):\n", 45 | " self.chain = [] # chain에 여러 block들 들어옴\n", 46 | " self.current_transaction = [] # 임시 transaction 넣어줌\n", 47 | " self.nodes = set() # Node 목록을 보관\n", 48 | " self.new_block(previous_hash=1, proof=100) # genesis block 생성\n", 49 | "\n", 50 | " @staticmethod\n", 51 | " def hash(block):\n", 52 | " block_string = json.dumps(block, sort_keys=True).encode() \n", 53 | " return hashlib.sha256(block_string).hexdigest() # hash 라이브러리로 sha256 사용\n", 54 | " @property\n", 55 | " def last_block(self):\n", 56 | " return self.chain[-1] # 체인의 마지막 블록 가져오기!!\n", 57 | "\n", 58 | " @staticmethod\n", 59 | " def valid_proof(last_proof, proof):\n", 60 | " guess = str(last_proof + proof).encode() # 전 proof와 구할 proof 문자열 연결\n", 61 | " guess_hash = hashlib.sha256(guess).hexdigest() # 이 hash 값 저장\n", 62 | " return guess_hash[:4] == \"0000\" # 앞 4자리가 0000 이면 True (알맞은 nonce값을 찾음)\n", 63 | "\n", 64 | " def pow(self, last_proof):\n", 65 | " proof = random.randint(-1000000,1000000)\n", 66 | " while self.valid_proof(last_proof, proof) is False: # valid proof 함수 활용(아래 나옴), 맞을 때까지 반복적으로 검증\n", 67 | " proof = random.randint(-1000000,1000000)\n", 68 | " return proof\n", 69 | "\n", 70 | " def new_transaction(self, sender, recipient, amount, smart_contract):\n", 71 | " self.current_transaction.append(\n", 72 | " {\n", 73 | " 'sender' : sender, # 송신자\n", 74 | " 'recipient' : recipient, # 수신자\n", 75 | " 'amount' : amount, # 금액\n", 76 | " 'timestamp':time(),\n", 77 | " 'smart_contract' : smart_contract\n", 78 | " }\n", 79 | " )\n", 80 | " return self.last_block['index'] + 1 \n", 81 | "\n", 82 | " def new_block(self, proof, previous_hash=None):\n", 83 | " block = {\n", 84 | " 'index' : len(self.chain)+1,\n", 85 | " 'timestamp' : time(), # timestamp from 1970\n", 86 | " 'transactions' : self.current_transaction,\n", 87 | " 'nonce' : proof,\n", 88 | " 'previous_hash' : previous_hash or self.hash(self.chain[-1]),\n", 89 | " }\n", 90 | " block['hash'] = self.hash(block)\n", 91 | " self.current_transaction = []\n", 92 | " self.chain.append(block) \n", 93 | " return block\n", 94 | "\n", 95 | " def valid_chain(self, chain):\n", 96 | " last_block = chain[0] \n", 97 | " current_index = 1\n", 98 | "\n", 99 | " while current_index < len(chain): \n", 100 | " block = chain[current_index]\n", 101 | " \n", 102 | " print('%s' % last_block)\n", 103 | " print('%s' % block)\n", 104 | " print(\"\\n--------\\n\")\n", 105 | " if block['previous_hash'] != self.hash(last_block):\n", 106 | " return False\n", 107 | " \n", 108 | " block_copy = block.copy()\n", 109 | " block_copy.pop('hash')\n", 110 | " if block['hash'] != self.hash(block_copy):\n", 111 | " return False\n", 112 | " \n", 113 | " last_block = block\n", 114 | " current_index += 1\n", 115 | " return True\n" 116 | ] 117 | }, 118 | { 119 | "cell_type": "markdown", 120 | "id": "cccc5e32", 121 | "metadata": {}, 122 | "source": [ 123 | "## Blockchain 객채를 기반으로 노드 생성" 124 | ] 125 | }, 126 | { 127 | "cell_type": "code", 128 | "execution_count": 3, 129 | "id": "fdd2e589", 130 | "metadata": {}, 131 | "outputs": [], 132 | "source": [ 133 | "blockchain = Blockchain()\n", 134 | "my_ip = '0.0.0.0'\n", 135 | "my_port = '5000'\n", 136 | "node_identifier = 'node_'+my_port\n", 137 | "mine_owner = 'master'\n", 138 | "mine_profit = 0.1" 139 | ] 140 | }, 141 | { 142 | "cell_type": "code", 143 | "execution_count": null, 144 | "id": "2228cdbe", 145 | "metadata": {}, 146 | "outputs": [ 147 | { 148 | "name": "stdout", 149 | "output_type": "stream", 150 | "text": [ 151 | " * Serving Flask app \"__main__\" (lazy loading)\n", 152 | " * Environment: production\n", 153 | " WARNING: This is a development server. Do not use it in a production deployment.\n", 154 | " Use a production WSGI server instead.\n", 155 | " * Debug mode: off\n" 156 | ] 157 | }, 158 | { 159 | "name": "stderr", 160 | "output_type": "stream", 161 | "text": [ 162 | " * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)\n", 163 | "127.0.0.1 - - [24/Jan/2023 18:21:43] \"\u001b[37mPOST /transactions/new HTTP/1.1\u001b[0m\" 201 -\n" 164 | ] 165 | }, 166 | { 167 | "name": "stdout", 168 | "output_type": "stream", 169 | "text": [ 170 | "transactions_new!!! : {'sender': 'test_from', 'recipient': 'smart_contract', 'amount': 0, 'smart_contract': {'contract_code': 'calculate_result = {}{}{}'}}\n" 171 | ] 172 | }, 173 | { 174 | "name": "stderr", 175 | "output_type": "stream", 176 | "text": [ 177 | "127.0.0.1 - - [24/Jan/2023 18:21:45] \"\u001b[37mGET /mine HTTP/1.1\u001b[0m\" 200 -\n" 178 | ] 179 | }, 180 | { 181 | "name": "stdout", 182 | "output_type": "stream", 183 | "text": [ 184 | "MINING STARTED\n", 185 | "MINING FINISHED\n" 186 | ] 187 | }, 188 | { 189 | "name": "stderr", 190 | "output_type": "stream", 191 | "text": [ 192 | "127.0.0.1 - - [24/Jan/2023 18:21:47] \"\u001b[37mGET /chain HTTP/1.1\u001b[0m\" 200 -\n" 193 | ] 194 | }, 195 | { 196 | "name": "stdout", 197 | "output_type": "stream", 198 | "text": [ 199 | "chain info requested!!\n" 200 | ] 201 | }, 202 | { 203 | "name": "stderr", 204 | "output_type": "stream", 205 | "text": [ 206 | "127.0.0.1 - - [24/Jan/2023 18:21:50] \"\u001b[37mGET /chain HTTP/1.1\u001b[0m\" 200 -\n" 207 | ] 208 | }, 209 | { 210 | "name": "stdout", 211 | "output_type": "stream", 212 | "text": [ 213 | "chain info requested!!\n" 214 | ] 215 | }, 216 | { 217 | "name": "stderr", 218 | "output_type": "stream", 219 | "text": [ 220 | "127.0.0.1 - - [24/Jan/2023 18:21:52] \"\u001b[37mGET /chain HTTP/1.1\u001b[0m\" 200 -\n" 221 | ] 222 | }, 223 | { 224 | "name": "stdout", 225 | "output_type": "stream", 226 | "text": [ 227 | "chain info requested!!\n" 228 | ] 229 | }, 230 | { 231 | "name": "stderr", 232 | "output_type": "stream", 233 | "text": [ 234 | "127.0.0.1 - - [24/Jan/2023 18:22:00] \"\u001b[37mPOST /transactions/new HTTP/1.1\u001b[0m\" 201 -\n" 235 | ] 236 | }, 237 | { 238 | "name": "stdout", 239 | "output_type": "stream", 240 | "text": [ 241 | "transactions_new!!! : {'sender': 'test_from', 'recipient': 'smart_contract', 'amount': 0, 'smart_contract': {'contract_code': '\\ndef Lottery():\\n lottery_number = random.sample(range(1,46),6)\\n lottery_number = sorted(lottery_number, key=lambda x: x)\\n lottery_number\\n print(lottery_number) \\n return lottery_number\\n '}}\n", 242 | "MINING STARTED\n" 243 | ] 244 | }, 245 | { 246 | "name": "stderr", 247 | "output_type": "stream", 248 | "text": [ 249 | "127.0.0.1 - - [24/Jan/2023 18:22:03] \"\u001b[37mGET /mine HTTP/1.1\u001b[0m\" 200 -\n" 250 | ] 251 | }, 252 | { 253 | "name": "stdout", 254 | "output_type": "stream", 255 | "text": [ 256 | "MINING FINISHED\n" 257 | ] 258 | }, 259 | { 260 | "name": "stderr", 261 | "output_type": "stream", 262 | "text": [ 263 | "127.0.0.1 - - [24/Jan/2023 18:22:05] \"\u001b[37mGET /chain HTTP/1.1\u001b[0m\" 200 -\n" 264 | ] 265 | }, 266 | { 267 | "name": "stdout", 268 | "output_type": "stream", 269 | "text": [ 270 | "chain info requested!!\n" 271 | ] 272 | }, 273 | { 274 | "name": "stderr", 275 | "output_type": "stream", 276 | "text": [ 277 | "127.0.0.1 - - [24/Jan/2023 18:22:07] \"\u001b[37mPOST /transactions/new HTTP/1.1\u001b[0m\" 201 -\n" 278 | ] 279 | }, 280 | { 281 | "name": "stdout", 282 | "output_type": "stream", 283 | "text": [ 284 | "transactions_new!!! : {'sender': 'test_from', 'recipient': 'test_to', 'amount': 3, 'smart_contract': {'contract_code': \"token_name = 'pySTAKINGTOKEN' \\ntoken_total_volume = 100000\\ntoken_owner = {'token_maker' : 10000}\", 'contract_function_getBalance': \"\\ndef get_balance(user_id):\\n print('{} Balance is : '.format(user_id), token_owner[user_id])\\n return token_owner[user_id]\\n\", 'contract_function_sendToken': '\\ndef send_token(sender,recipent,amount):\\n if sender in token_owner.keys():\\n if get_balance(sender) > amount:\\n token_owner[sender] = token_owner[sender] - amount\\n if recipent in token_owner.keys():\\n token_owner[recipent] = token_owner[recipent] + amount\\n else :\\n token_owner[recipent] = amount\\n print(\"Transaction Completed\")\\n get_balance(sender) \\n get_balance(recipent) \\n\\n else:\\n return \"Insufficient Balance\"\\n else:\\n return \"Unavailable Sender id\"\\n', 'contract_function_token_staking': '\\ndef token_staking(staker,amount):\\n if staker in token_owner.keys():\\n if get_balance(staker) > amount:\\n token_owner[staker] = token_owner[staker] - amount\\n staking_status [len(staking_status)] = {\\'staker\\':staker,\\'amount\\':amount}\\n print(\"Staing Completed\")\\n get_balance(staker) \\n \\n else:\\n return \"Insufficient Balance\"\\n else:\\n return \"Unavailable Staker id\"\\n', 'contract_function_staking_yield': \"\\ndef staking_yield(staking_status):\\n for t in staking_status:\\n print(staking_status[t])\\n staking_status[t]['amount'] = staking_status[t]['amount'] * 1.1\\n return staking_status\\n\"}}\n" 285 | ] 286 | }, 287 | { 288 | "name": "stderr", 289 | "output_type": "stream", 290 | "text": [ 291 | "127.0.0.1 - - [24/Jan/2023 18:22:09] \"\u001b[37mGET /mine HTTP/1.1\u001b[0m\" 200 -\n" 292 | ] 293 | }, 294 | { 295 | "name": "stdout", 296 | "output_type": "stream", 297 | "text": [ 298 | "MINING STARTED\n", 299 | "MINING FINISHED\n" 300 | ] 301 | }, 302 | { 303 | "name": "stderr", 304 | "output_type": "stream", 305 | "text": [ 306 | "127.0.0.1 - - [24/Jan/2023 18:22:11] \"\u001b[37mGET /chain HTTP/1.1\u001b[0m\" 200 -\n" 307 | ] 308 | }, 309 | { 310 | "name": "stdout", 311 | "output_type": "stream", 312 | "text": [ 313 | "chain info requested!!\n" 314 | ] 315 | }, 316 | { 317 | "name": "stderr", 318 | "output_type": "stream", 319 | "text": [ 320 | "127.0.0.1 - - [24/Jan/2023 18:30:08] \"\u001b[37mPOST /transactions/new HTTP/1.1\u001b[0m\" 201 -\n" 321 | ] 322 | }, 323 | { 324 | "name": "stdout", 325 | "output_type": "stream", 326 | "text": [ 327 | "transactions_new!!! : {'sender': 'test_from', 'recipient': 'test_to', 'amount': 3, 'smart_contract': {'contract_code': \"token_name = 'pySTAKINGTOKEN' \\ntoken_total_volume = 100000\\ntoken_owner = {'token_maker' : 10000}\\nstaking_status = {}\", 'contract_function_getBalance': \"\\ndef get_balance(user_id):\\n print('{} Balance is : '.format(user_id), token_owner[user_id])\\n return token_owner[user_id]\\n\", 'contract_function_sendToken': '\\ndef send_token(sender,recipent,amount):\\n if sender in token_owner.keys():\\n if get_balance(sender) > amount:\\n token_owner[sender] = token_owner[sender] - amount\\n if recipent in token_owner.keys():\\n token_owner[recipent] = token_owner[recipent] + amount\\n else :\\n token_owner[recipent] = amount\\n print(\"Transaction Completed\")\\n get_balance(sender) \\n get_balance(recipent) \\n\\n else:\\n return \"Insufficient Balance\"\\n else:\\n return \"Unavailable Sender id\"\\n', 'contract_function_token_staking': '\\ndef token_staking(staker,amount):\\n if staker in token_owner.keys():\\n if get_balance(staker) > amount:\\n token_owner[staker] = token_owner[staker] - amount\\n staking_status [len(staking_status)] = {\\'staker\\':staker,\\'amount\\':amount}\\n print(\"Staing Completed\")\\n get_balance(staker) \\n \\n else:\\n return \"Insufficient Balance\"\\n else:\\n return \"Unavailable Staker id\"\\n', 'contract_function_staking_yield': \"\\ndef staking_yield(staking_status):\\n for t in staking_status:\\n print(staking_status[t])\\n staking_status[t]['amount'] = staking_status[t]['amount'] * 1.1\\n return staking_status\\n\"}}\n" 328 | ] 329 | }, 330 | { 331 | "name": "stderr", 332 | "output_type": "stream", 333 | "text": [ 334 | "127.0.0.1 - - [24/Jan/2023 18:30:11] \"\u001b[37mGET /mine HTTP/1.1\u001b[0m\" 200 -\n" 335 | ] 336 | }, 337 | { 338 | "name": "stdout", 339 | "output_type": "stream", 340 | "text": [ 341 | "MINING STARTED\n", 342 | "MINING FINISHED\n" 343 | ] 344 | }, 345 | { 346 | "name": "stderr", 347 | "output_type": "stream", 348 | "text": [ 349 | "127.0.0.1 - - [24/Jan/2023 18:30:14] \"\u001b[37mGET /chain HTTP/1.1\u001b[0m\" 200 -\n" 350 | ] 351 | }, 352 | { 353 | "name": "stdout", 354 | "output_type": "stream", 355 | "text": [ 356 | "chain info requested!!\n" 357 | ] 358 | } 359 | ], 360 | "source": [ 361 | "app = Flask(__name__)\n", 362 | "\n", 363 | "@app.route('/chain', methods=['GET'])\n", 364 | "def full_chain():\n", 365 | " print(\"chain info requested!!\")\n", 366 | " response = {\n", 367 | " 'chain' : blockchain.chain, \n", 368 | " 'length' : len(blockchain.chain), \n", 369 | " }\n", 370 | " return jsonify(response), 200\n", 371 | "\n", 372 | "@app.route('/transactions/new', methods=['POST'])\n", 373 | "def new_transaction():\n", 374 | " values = request.get_json() \n", 375 | " print(\"transactions_new!!! : \", values)\n", 376 | " required = ['sender', 'recipient', 'amount','smart_contract'] ## 송신자(sender), 수신자(recipient), 금액(amount),의 존재여부 만 확인에 더하여 스마트컨트랙트(smart_contract)의 존재여부 확인\n", 377 | "\n", 378 | " if not all(k in values for k in required):\n", 379 | " return 'missing values', 400\n", 380 | " contract_address = hashlib.sha256(str(datetime.datetime.now()).encode() ).hexdigest()\n", 381 | " values['smart_contract'][\"contract_address\"] = contract_address ## 거래의 스마트 컨트랙트에 대한 컨트랙트 주소(contract_address)를 부여\n", 382 | "\n", 383 | "\n", 384 | " index = blockchain.new_transaction(values['sender'],values['recipient'],\n", 385 | "values['amount'], values['smart_contract']) \n", 386 | " \n", 387 | " response = {'message' : 'Transaction will be added to Block {%s}' % index, \"contract_address\":contract_address}\n", 388 | " return jsonify(response), 201\n", 389 | "\n", 390 | "\n", 391 | "@app.route('/mine', methods=['GET'])\n", 392 | "def mine():\n", 393 | " print(\"MINING STARTED\") \n", 394 | " last_block = blockchain.last_block\n", 395 | " last_proof = last_block['nonce']\n", 396 | " proof = blockchain.pow(last_proof) \n", 397 | "\n", 398 | " blockchain.new_transaction(\n", 399 | " sender=mine_owner, \n", 400 | " recipient=node_identifier, \n", 401 | " amount=mine_profit, # coinbase transaction \n", 402 | " smart_contract={\"contract_address\":\"mining_profit\"}, \n", 403 | " )\n", 404 | " \n", 405 | " previous_hash = blockchain.hash(last_block)\n", 406 | " block = blockchain.new_block(proof, previous_hash)\n", 407 | " print(\"MINING FINISHED\")\n", 408 | "\n", 409 | " response = {\n", 410 | " 'message' : 'new block found',\n", 411 | " 'index' : block['index'],\n", 412 | " 'transactions' : block['transactions'],\n", 413 | " 'nonce' : block['nonce'],\n", 414 | " 'previous_hash' : block['previous_hash'],\n", 415 | " 'hash' : block['hash']\n", 416 | " }\n", 417 | " \n", 418 | " return jsonify(response), 200\n", 419 | "\n", 420 | "\n", 421 | "if __name__ == '__main__':\n", 422 | " app.run(host=my_ip, port=my_port)\n" 423 | ] 424 | }, 425 | { 426 | "cell_type": "code", 427 | "execution_count": null, 428 | "id": "0ea982d0", 429 | "metadata": {}, 430 | "outputs": [], 431 | "source": [] 432 | } 433 | ], 434 | "metadata": { 435 | "kernelspec": { 436 | "display_name": "Python 3", 437 | "language": "python", 438 | "name": "python3" 439 | }, 440 | "language_info": { 441 | "codemirror_mode": { 442 | "name": "ipython", 443 | "version": 3 444 | }, 445 | "file_extension": ".py", 446 | "mimetype": "text/x-python", 447 | "name": "python", 448 | "nbconvert_exporter": "python", 449 | "pygments_lexer": "ipython3", 450 | "version": "3.8.8" 451 | } 452 | }, 453 | "nbformat": 4, 454 | "nbformat_minor": 5 455 | } 456 | -------------------------------------------------------------------------------- /chapter4/node_command_DApp.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "id": "e4397b59", 7 | "metadata": {}, 8 | "outputs": [], 9 | "source": [ 10 | "import requests\n", 11 | "import json\n", 12 | "import pandas as pd\n", 13 | "import hashlib # hash 함수용 sha256 사용할 라이브러리|\n", 14 | "import random" 15 | ] 16 | }, 17 | { 18 | "cell_type": "markdown", 19 | "id": "9c71ae70", 20 | "metadata": {}, 21 | "source": [ 22 | "## DApp 제작 -1 계산기" 23 | ] 24 | }, 25 | { 26 | "cell_type": "code", 27 | "execution_count": 2, 28 | "id": "5d7a7601", 29 | "metadata": {}, 30 | "outputs": [ 31 | { 32 | "name": "stdout", 33 | "output_type": "stream", 34 | "text": [ 35 | "\n" 36 | ] 37 | } 38 | ], 39 | "source": [ 40 | "headers = {'Content-Type' : 'application/json; charset=utf-8'}\n", 41 | "data = {\n", 42 | " \"sender\": \"test_from\",\n", 43 | " \"recipient\": \"smart_contract\",\n", 44 | " \"amount\": 0,\n", 45 | " \"smart_contract\": {\n", 46 | " \"contract_code\" : \"calculate_result = {}{}{}\"}\n", 47 | "}\n", 48 | "result = requests.post(\"http://localhost:5000/transactions/new\", headers=headers, data=json.dumps(data)).content\n", 49 | "contract_address = json.loads(result)['contract_address']\n", 50 | "\n", 51 | "headers = {'Content-Type' : 'application/json; charset=utf-8'}\n", 52 | "res = requests.get(\"http://localhost:5000/mine\")\n", 53 | "print(res)" 54 | ] 55 | }, 56 | { 57 | "cell_type": "markdown", 58 | "id": "e71cdac6", 59 | "metadata": {}, 60 | "source": [ 61 | "### 120 + 360 " 62 | ] 63 | }, 64 | { 65 | "cell_type": "code", 66 | "execution_count": 3, 67 | "id": "9adbadb1", 68 | "metadata": {}, 69 | "outputs": [ 70 | { 71 | "name": "stdout", 72 | "output_type": "stream", 73 | "text": [ 74 | "480\n" 75 | ] 76 | } 77 | ], 78 | "source": [ 79 | "headers = {'Content-Type' : 'application/json; charset=utf-8'}\n", 80 | "res = requests.get(\"http://localhost:5000/chain\", headers=headers)\n", 81 | "res_json = json.loads(res.content)\n", 82 | "\n", 83 | "for _block in res_json['chain']:\n", 84 | " for _tx in _block['transactions']:\n", 85 | " if _tx['smart_contract']['contract_address'] ==contract_address:\n", 86 | " exec( _tx['smart_contract']['contract_code'].format(120,\"+\",360))\n", 87 | " break\n", 88 | "print(calculate_result) " 89 | ] 90 | }, 91 | { 92 | "cell_type": "markdown", 93 | "id": "51473770", 94 | "metadata": {}, 95 | "source": [ 96 | "### 3 X 5" 97 | ] 98 | }, 99 | { 100 | "cell_type": "code", 101 | "execution_count": 4, 102 | "id": "2bb2b25d", 103 | "metadata": {}, 104 | "outputs": [ 105 | { 106 | "name": "stdout", 107 | "output_type": "stream", 108 | "text": [ 109 | "15\n" 110 | ] 111 | } 112 | ], 113 | "source": [ 114 | "headers = {'Content-Type' : 'application/json; charset=utf-8'}\n", 115 | "res = requests.get(\"http://localhost:5000/chain\", headers=headers)\n", 116 | "res_json = json.loads(res.content)\n", 117 | "\n", 118 | "for _block in res_json['chain']:\n", 119 | " for _tx in _block['transactions']:\n", 120 | " if _tx['smart_contract']['contract_address'] ==contract_address:\n", 121 | " exec( _tx['smart_contract']['contract_code'].format(3,\"*\",5))\n", 122 | " break\n", 123 | "print(calculate_result) " 124 | ] 125 | }, 126 | { 127 | "cell_type": "markdown", 128 | "id": "91e0e392", 129 | "metadata": {}, 130 | "source": [ 131 | "### 12000 / 12" 132 | ] 133 | }, 134 | { 135 | "cell_type": "code", 136 | "execution_count": 5, 137 | "id": "7b697e1d", 138 | "metadata": {}, 139 | "outputs": [ 140 | { 141 | "name": "stdout", 142 | "output_type": "stream", 143 | "text": [ 144 | "1000.0\n" 145 | ] 146 | } 147 | ], 148 | "source": [ 149 | "headers = {'Content-Type' : 'application/json; charset=utf-8'}\n", 150 | "res = requests.get(\"http://localhost:5000/chain\", headers=headers)\n", 151 | "res_json = json.loads(res.content)\n", 152 | "\n", 153 | "for _block in res_json['chain']:\n", 154 | " for _tx in _block['transactions']:\n", 155 | " if _tx['smart_contract']['contract_address'] ==contract_address:\n", 156 | " exec( _tx['smart_contract']['contract_code'].format(12000,\"/\",12))\n", 157 | " break\n", 158 | "print(calculate_result) " 159 | ] 160 | }, 161 | { 162 | "cell_type": "markdown", 163 | "id": "75f9cf55", 164 | "metadata": {}, 165 | "source": [ 166 | "## DApp 제작 -2 복권" 167 | ] 168 | }, 169 | { 170 | "cell_type": "code", 171 | "execution_count": 6, 172 | "id": "bba03700", 173 | "metadata": {}, 174 | "outputs": [ 175 | { 176 | "name": "stdout", 177 | "output_type": "stream", 178 | "text": [ 179 | "\n" 180 | ] 181 | } 182 | ], 183 | "source": [ 184 | "headers = {'Content-Type' : 'application/json; charset=utf-8'}\n", 185 | "data = {\n", 186 | " \"sender\": \"test_from\",\n", 187 | " \"recipient\": \"smart_contract\",\n", 188 | " \"amount\": 0,\n", 189 | " \"smart_contract\": {\n", 190 | " \"contract_code\" : \"\"\"\n", 191 | "def Lottery():\n", 192 | " lottery_number = random.sample(range(1,46),6)\n", 193 | " lottery_number = sorted(lottery_number, key=lambda x: x)\n", 194 | " lottery_number\n", 195 | " print(lottery_number) \n", 196 | " return lottery_number\n", 197 | " \"\"\"}\n", 198 | "}\n", 199 | "result = requests.post(\"http://localhost:5000/transactions/new\", headers=headers, data=json.dumps(data)).content\n", 200 | "contract_address = json.loads(result)['contract_address']\n", 201 | "\n", 202 | "headers = {'Content-Type' : 'application/json; charset=utf-8'}\n", 203 | "res = requests.get(\"http://localhost:5000/mine\")\n", 204 | "print(res)" 205 | ] 206 | }, 207 | { 208 | "cell_type": "code", 209 | "execution_count": 7, 210 | "id": "d56f755c", 211 | "metadata": {}, 212 | "outputs": [ 213 | { 214 | "name": "stdout", 215 | "output_type": "stream", 216 | "text": [ 217 | "[7, 8, 9, 14, 22, 44]\n", 218 | "[7, 8, 9, 14, 22, 44]\n" 219 | ] 220 | } 221 | ], 222 | "source": [ 223 | "headers = {'Content-Type' : 'application/json; charset=utf-8'}\n", 224 | "res = requests.get(\"http://localhost:5000/chain\", headers=headers)\n", 225 | "res_json = json.loads(res.content)\n", 226 | "\n", 227 | "for _block in res_json['chain']:\n", 228 | " for _tx in _block['transactions']:\n", 229 | " if _tx['smart_contract']['contract_address'] ==contract_address:\n", 230 | " exec( _tx['smart_contract']['contract_code'])\n", 231 | " break \n", 232 | "print(Lottery()) " 233 | ] 234 | }, 235 | { 236 | "cell_type": "markdown", 237 | "id": "84aa0092", 238 | "metadata": {}, 239 | "source": [ 240 | "## DApp 제작 -3 Defi" 241 | ] 242 | }, 243 | { 244 | "cell_type": "code", 245 | "execution_count": 8, 246 | "id": "b2d8fb47", 247 | "metadata": {}, 248 | "outputs": [], 249 | "source": [ 250 | "token_name = 'pyTOKEN' \n", 251 | "token_total_volume = 100000\n", 252 | "token_owner = {'token_maker' : 10000}\n", 253 | "staking_status = {}" 254 | ] 255 | }, 256 | { 257 | "cell_type": "code", 258 | "execution_count": 9, 259 | "id": "4e862b3d", 260 | "metadata": {}, 261 | "outputs": [], 262 | "source": [ 263 | "def get_balance(user_id):\n", 264 | " print('{} Balance is : '.format(user_id), token_owner[user_id])\n", 265 | " return token_owner[user_id]\n", 266 | "\n", 267 | "def send_token(sender,recipent,amount):\n", 268 | " if sender in token_owner.keys():\n", 269 | " if get_balance(sender) > amount:\n", 270 | " token_owner[sender] = token_owner[sender] - amount\n", 271 | " if recipent in token_owner.keys():\n", 272 | " token_owner[recipent] = token_owner[recipent] + amount\n", 273 | " else :\n", 274 | " token_owner[recipent] = amount\n", 275 | " print(\"Transaction Completed\")\n", 276 | " get_balance(sender) \n", 277 | " get_balance(recipent) \n", 278 | "\n", 279 | " else:\n", 280 | " return \"Insufficient Balance\"\n", 281 | " else:\n", 282 | " return \"Unavailable Sender id\"" 283 | ] 284 | }, 285 | { 286 | "cell_type": "code", 287 | "execution_count": 10, 288 | "id": "0d09c317", 289 | "metadata": {}, 290 | "outputs": [], 291 | "source": [ 292 | "def token_staking(staker,amount):\n", 293 | " if staker in token_owner.keys():\n", 294 | " if get_balance(staker) > amount:\n", 295 | " token_owner[staker] = token_owner[staker] - amount\n", 296 | " staking_status [len(staking_status)] = {'staker':staker,'amount':amount}\n", 297 | " print(\"Staing Completed\")\n", 298 | " get_balance(staker) \n", 299 | " \n", 300 | " else:\n", 301 | " return \"Insufficient Balance\"\n", 302 | " else:\n", 303 | " return \"Unavailable Staker id\"\n", 304 | " \n", 305 | "def staking_yield(staking_status):\n", 306 | " for t in staking_status:\n", 307 | " print(staking_status[t])\n", 308 | " staking_status[t]['amount'] = staking_status[t]['amount'] * 1.1\n", 309 | " return staking_status" 310 | ] 311 | }, 312 | { 313 | "cell_type": "code", 314 | "execution_count": 20, 315 | "id": "3755c30a", 316 | "metadata": {}, 317 | "outputs": [ 318 | { 319 | "name": "stdout", 320 | "output_type": "stream", 321 | "text": [ 322 | "\n" 323 | ] 324 | } 325 | ], 326 | "source": [ 327 | "headers = {'Content-Type' : 'application/json; charset=utf-8'}\n", 328 | "data = {\n", 329 | " \"sender\": \"test_from\",\n", 330 | " \"recipient\": \"test_to\",\n", 331 | " \"amount\": 3,\n", 332 | " \"smart_contract\": {\n", 333 | " \"contract_code\" :\"token_name = 'pySTAKINGTOKEN' \\ntoken_total_volume = 100000\\ntoken_owner = {'token_maker' : 10000}\\nstaking_status = {}\",\n", 334 | " \"contract_function_getBalance\" :\"\"\"\n", 335 | "def get_balance(user_id):\n", 336 | " print('{} Balance is : '.format(user_id), token_owner[user_id])\n", 337 | " return token_owner[user_id]\n", 338 | "\"\"\",\n", 339 | " \"contract_function_sendToken\" :\"\"\"\n", 340 | "def send_token(sender,recipent,amount):\n", 341 | " if sender in token_owner.keys(): \n", 342 | " if get_balance(sender) > amount:\n", 343 | " token_owner[sender] = token_owner[sender] - amount\n", 344 | " if recipent in token_owner.keys():\n", 345 | " token_owner[recipent] = token_owner[recipent] + amount\n", 346 | " else :\n", 347 | " token_owner[recipent] = amount\n", 348 | " print(\"Transaction Completed\")\n", 349 | " get_balance(sender) \n", 350 | " get_balance(recipent) \n", 351 | "\n", 352 | " else:\n", 353 | " return \"Insufficient Balance\"\n", 354 | " else:\n", 355 | " return \"Unavailable Sender id\"\n", 356 | "\"\"\",\n", 357 | " \"contract_function_token_staking\" :\"\"\"\n", 358 | "def token_staking(staker,amount):\n", 359 | " if staker in token_owner.keys(): ## 예치자(staker)가 실제 존재하는 사용자인지 확인\n", 360 | " if get_balance(staker) > amount: ## 예치자(staker)의 잔고가 예치 금액보다 많은지 확인\n", 361 | " token_owner[staker] = token_owner[staker] - amount ## 예치자(staker)의 잔고에서 예치 금액 제외\n", 362 | " staking_status [len(staking_status)] = {'staker':staker,'amount':amount} \n", 363 | " ## 예치 정보(staking_status)에 예치자(staker)의 예치내역 저장\n", 364 | " print(\"Staing Completed\")\n", 365 | " get_balance(staker) \n", 366 | " \n", 367 | " else:\n", 368 | " return \"Insufficient Balance\"\n", 369 | " else:\n", 370 | " return \"Unavailable Staker id\"\n", 371 | "\"\"\",\n", 372 | " \"contract_function_staking_yield\" :\"\"\"\n", 373 | "def staking_yield(staking_status): ## 예치 이자 지급함수\n", 374 | " for t in staking_status:\n", 375 | " print(staking_status[t])\n", 376 | " staking_status[t]['amount'] = staking_status[t]['amount'] * (1+0.1) ## 예치 이자가 10% 지급된 금액으로 예치금 변경\n", 377 | " return staking_status\n", 378 | "\"\"\" \n", 379 | "\n", 380 | " }\n", 381 | " }\n", 382 | "result = requests.post(\"http://localhost:5000/transactions/new\", headers=headers, data=json.dumps(data)).content\n", 383 | "contract_address = json.loads(result)['contract_address']\n", 384 | "\n", 385 | "\n", 386 | "headers = {'Content-Type' : 'application/json; charset=utf-8'}\n", 387 | "res = requests.get(\"http://localhost:5000/mine\")\n", 388 | "print(res)" 389 | ] 390 | }, 391 | { 392 | "cell_type": "markdown", 393 | "id": "307a82ed", 394 | "metadata": {}, 395 | "source": [ 396 | "### pySTAKINGTOKEN 생성여부 확인" 397 | ] 398 | }, 399 | { 400 | "cell_type": "code", 401 | "execution_count": 21, 402 | "id": "2803f649", 403 | "metadata": {}, 404 | "outputs": [], 405 | "source": [ 406 | "headers = {'Content-Type' : 'application/json; charset=utf-8'}\n", 407 | "res = requests.get(\"http://localhost:5000/chain\", headers=headers)\n", 408 | "res_json = json.loads(res.content)\n", 409 | "\n", 410 | "for _block in res_json['chain']:\n", 411 | " for _tx in _block['transactions']:\n", 412 | " if _tx['smart_contract']['contract_address'] == contract_address:\n", 413 | " exec( _tx['smart_contract']['contract_code'])\n", 414 | " break" 415 | ] 416 | }, 417 | { 418 | "cell_type": "code", 419 | "execution_count": 22, 420 | "id": "b99b43d4", 421 | "metadata": {}, 422 | "outputs": [ 423 | { 424 | "data": { 425 | "text/plain": [ 426 | "'pySTAKINGTOKEN'" 427 | ] 428 | }, 429 | "execution_count": 22, 430 | "metadata": {}, 431 | "output_type": "execute_result" 432 | } 433 | ], 434 | "source": [ 435 | "token_name " 436 | ] 437 | }, 438 | { 439 | "cell_type": "code", 440 | "execution_count": 23, 441 | "id": "5b90fcfb", 442 | "metadata": {}, 443 | "outputs": [ 444 | { 445 | "data": { 446 | "text/plain": [ 447 | "100000" 448 | ] 449 | }, 450 | "execution_count": 23, 451 | "metadata": {}, 452 | "output_type": "execute_result" 453 | } 454 | ], 455 | "source": [ 456 | "token_total_volume" 457 | ] 458 | }, 459 | { 460 | "cell_type": "code", 461 | "execution_count": 24, 462 | "id": "b912f165", 463 | "metadata": {}, 464 | "outputs": [ 465 | { 466 | "name": "stdout", 467 | "output_type": "stream", 468 | "text": [ 469 | "token_maker Balance is : 10000\n" 470 | ] 471 | }, 472 | { 473 | "data": { 474 | "text/plain": [ 475 | "10000" 476 | ] 477 | }, 478 | "execution_count": 24, 479 | "metadata": {}, 480 | "output_type": "execute_result" 481 | } 482 | ], 483 | "source": [ 484 | "exec(_tx['smart_contract']['contract_function_getBalance'])\n", 485 | "get_balance('token_maker')" 486 | ] 487 | }, 488 | { 489 | "cell_type": "markdown", 490 | "id": "db80aa69", 491 | "metadata": {}, 492 | "source": [ 493 | "### pySTAKINGTOKEN 예치 진행" 494 | ] 495 | }, 496 | { 497 | "cell_type": "code", 498 | "execution_count": 25, 499 | "id": "2e43e5f8", 500 | "metadata": {}, 501 | "outputs": [ 502 | { 503 | "name": "stdout", 504 | "output_type": "stream", 505 | "text": [ 506 | "token_maker Balance is : 10000\n", 507 | "Staing Completed\n", 508 | "token_maker Balance is : 9900\n" 509 | ] 510 | } 511 | ], 512 | "source": [ 513 | "exec(_tx['smart_contract']['contract_function_token_staking'])\n", 514 | "token_staking('token_maker',100)" 515 | ] 516 | }, 517 | { 518 | "cell_type": "markdown", 519 | "id": "5519c427", 520 | "metadata": {}, 521 | "source": [ 522 | "### pySTAKINGTOKEN 예치자에게 이자 지급" 523 | ] 524 | }, 525 | { 526 | "cell_type": "code", 527 | "execution_count": 26, 528 | "id": "d0555d6f", 529 | "metadata": {}, 530 | "outputs": [ 531 | { 532 | "name": "stdout", 533 | "output_type": "stream", 534 | "text": [ 535 | "{'staker': 'token_maker', 'amount': 100}\n" 536 | ] 537 | }, 538 | { 539 | "data": { 540 | "text/plain": [ 541 | "{0: {'staker': 'token_maker', 'amount': 110.00000000000001}}" 542 | ] 543 | }, 544 | "execution_count": 26, 545 | "metadata": {}, 546 | "output_type": "execute_result" 547 | } 548 | ], 549 | "source": [ 550 | "exec(_tx['smart_contract']['contract_function_staking_yield'])\n", 551 | " staking_yield(staking_status)" 552 | ] 553 | }, 554 | { 555 | "cell_type": "code", 556 | "execution_count": 27, 557 | "id": "341e2d2f", 558 | "metadata": {}, 559 | "outputs": [ 560 | { 561 | "name": "stdout", 562 | "output_type": "stream", 563 | "text": [ 564 | "{'staker': 'token_maker', 'amount': 110.00000000000001}\n" 565 | ] 566 | }, 567 | { 568 | "data": { 569 | "text/plain": [ 570 | "{0: {'staker': 'token_maker', 'amount': 121.00000000000003}}" 571 | ] 572 | }, 573 | "execution_count": 27, 574 | "metadata": {}, 575 | "output_type": "execute_result" 576 | } 577 | ], 578 | "source": [ 579 | "exec(_tx['smart_contract']['contract_function_staking_yield'])\n", 580 | "staking_yield(staking_status)" 581 | ] 582 | }, 583 | { 584 | "cell_type": "code", 585 | "execution_count": 28, 586 | "id": "116113c8", 587 | "metadata": {}, 588 | "outputs": [ 589 | { 590 | "name": "stdout", 591 | "output_type": "stream", 592 | "text": [ 593 | "{'staker': 'token_maker', 'amount': 121.00000000000003}\n" 594 | ] 595 | }, 596 | { 597 | "data": { 598 | "text/plain": [ 599 | "{0: {'staker': 'token_maker', 'amount': 133.10000000000005}}" 600 | ] 601 | }, 602 | "execution_count": 28, 603 | "metadata": {}, 604 | "output_type": "execute_result" 605 | } 606 | ], 607 | "source": [ 608 | "exec(_tx['smart_contract']['contract_function_staking_yield'])\n", 609 | "staking_yield(staking_status)" 610 | ] 611 | }, 612 | { 613 | "cell_type": "code", 614 | "execution_count": null, 615 | "id": "417f61fb", 616 | "metadata": {}, 617 | "outputs": [], 618 | "source": [] 619 | } 620 | ], 621 | "metadata": { 622 | "kernelspec": { 623 | "display_name": "Python 3", 624 | "language": "python", 625 | "name": "python3" 626 | }, 627 | "language_info": { 628 | "codemirror_mode": { 629 | "name": "ipython", 630 | "version": 3 631 | }, 632 | "file_extension": ".py", 633 | "mimetype": "text/x-python", 634 | "name": "python", 635 | "nbconvert_exporter": "python", 636 | "pygments_lexer": "ipython3", 637 | "version": "3.8.8" 638 | } 639 | }, 640 | "nbformat": 4, 641 | "nbformat_minor": 5 642 | } 643 | -------------------------------------------------------------------------------- /chapter5/smart_contract/node_command_DApp.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "id": "e4397b59", 7 | "metadata": {}, 8 | "outputs": [], 9 | "source": [ 10 | "import requests\n", 11 | "import json\n", 12 | "import pandas as pd\n", 13 | "import hashlib # hash 함수용 sha256 사용할 라이브러리|\n", 14 | "import random" 15 | ] 16 | }, 17 | { 18 | "cell_type": "markdown", 19 | "id": "9c71ae70", 20 | "metadata": {}, 21 | "source": [ 22 | "## DApp 제작 -1 계산기" 23 | ] 24 | }, 25 | { 26 | "cell_type": "code", 27 | "execution_count": 2, 28 | "id": "5d7a7601", 29 | "metadata": {}, 30 | "outputs": [ 31 | { 32 | "name": "stdout", 33 | "output_type": "stream", 34 | "text": [ 35 | "\n" 36 | ] 37 | } 38 | ], 39 | "source": [ 40 | "headers = {'Content-Type' : 'application/json; charset=utf-8'}\n", 41 | "data = {\n", 42 | " \"sender\": \"test_from\",\n", 43 | " \"recipient\": \"smart_contract\",\n", 44 | " \"amount\": 0,\n", 45 | " \"smart_contract\": {\n", 46 | " \"contract_code\" : \"calculate_result = {}{}{}\"}\n", 47 | "}\n", 48 | "result = requests.post(\"http://localhost:5000/transactions/new\", headers=headers, data=json.dumps(data)).content\n", 49 | "contract_address = json.loads(result)['contract_address']\n", 50 | "\n", 51 | "headers = {'Content-Type' : 'application/json; charset=utf-8'}\n", 52 | "res = requests.get(\"http://localhost:5000/mine\")\n", 53 | "print(res)" 54 | ] 55 | }, 56 | { 57 | "cell_type": "markdown", 58 | "id": "e71cdac6", 59 | "metadata": {}, 60 | "source": [ 61 | "### 120 + 360 " 62 | ] 63 | }, 64 | { 65 | "cell_type": "code", 66 | "execution_count": 3, 67 | "id": "9adbadb1", 68 | "metadata": {}, 69 | "outputs": [ 70 | { 71 | "name": "stdout", 72 | "output_type": "stream", 73 | "text": [ 74 | "480\n" 75 | ] 76 | } 77 | ], 78 | "source": [ 79 | "headers = {'Content-Type' : 'application/json; charset=utf-8'}\n", 80 | "res = requests.get(\"http://localhost:5000/chain\", headers=headers)\n", 81 | "res_json = json.loads(res.content)\n", 82 | "\n", 83 | "for _block in res_json['chain']:\n", 84 | " for _tx in _block['transactions']:\n", 85 | " if _tx['smart_contract']['contract_address'] ==contract_address:\n", 86 | " exec( _tx['smart_contract']['contract_code'].format(120,\"+\",360))\n", 87 | " break\n", 88 | "print(calculate_result) " 89 | ] 90 | }, 91 | { 92 | "cell_type": "markdown", 93 | "id": "51473770", 94 | "metadata": {}, 95 | "source": [ 96 | "### 3 X 5" 97 | ] 98 | }, 99 | { 100 | "cell_type": "code", 101 | "execution_count": 4, 102 | "id": "2bb2b25d", 103 | "metadata": {}, 104 | "outputs": [ 105 | { 106 | "name": "stdout", 107 | "output_type": "stream", 108 | "text": [ 109 | "15\n" 110 | ] 111 | } 112 | ], 113 | "source": [ 114 | "headers = {'Content-Type' : 'application/json; charset=utf-8'}\n", 115 | "res = requests.get(\"http://localhost:5000/chain\", headers=headers)\n", 116 | "res_json = json.loads(res.content)\n", 117 | "\n", 118 | "for _block in res_json['chain']:\n", 119 | " for _tx in _block['transactions']:\n", 120 | " if _tx['smart_contract']['contract_address'] ==contract_address:\n", 121 | " exec( _tx['smart_contract']['contract_code'].format(3,\"*\",5))\n", 122 | " break\n", 123 | "print(calculate_result) " 124 | ] 125 | }, 126 | { 127 | "cell_type": "markdown", 128 | "id": "91e0e392", 129 | "metadata": {}, 130 | "source": [ 131 | "### 12000 / 12" 132 | ] 133 | }, 134 | { 135 | "cell_type": "code", 136 | "execution_count": 5, 137 | "id": "7b697e1d", 138 | "metadata": {}, 139 | "outputs": [ 140 | { 141 | "name": "stdout", 142 | "output_type": "stream", 143 | "text": [ 144 | "1000.0\n" 145 | ] 146 | } 147 | ], 148 | "source": [ 149 | "headers = {'Content-Type' : 'application/json; charset=utf-8'}\n", 150 | "res = requests.get(\"http://localhost:5000/chain\", headers=headers)\n", 151 | "res_json = json.loads(res.content)\n", 152 | "\n", 153 | "for _block in res_json['chain']:\n", 154 | " for _tx in _block['transactions']:\n", 155 | " if _tx['smart_contract']['contract_address'] ==contract_address:\n", 156 | " exec( _tx['smart_contract']['contract_code'].format(12000,\"/\",12))\n", 157 | " break\n", 158 | "print(calculate_result) " 159 | ] 160 | }, 161 | { 162 | "cell_type": "markdown", 163 | "id": "75f9cf55", 164 | "metadata": {}, 165 | "source": [ 166 | "## DApp 제작 -2 복권" 167 | ] 168 | }, 169 | { 170 | "cell_type": "code", 171 | "execution_count": 6, 172 | "id": "bba03700", 173 | "metadata": {}, 174 | "outputs": [ 175 | { 176 | "name": "stdout", 177 | "output_type": "stream", 178 | "text": [ 179 | "\n" 180 | ] 181 | } 182 | ], 183 | "source": [ 184 | "headers = {'Content-Type' : 'application/json; charset=utf-8'}\n", 185 | "data = {\n", 186 | " \"sender\": \"test_from\",\n", 187 | " \"recipient\": \"smart_contract\",\n", 188 | " \"amount\": 0,\n", 189 | " \"smart_contract\": {\n", 190 | " \"contract_code\" : \"\"\"\n", 191 | "def Lottery():\n", 192 | " lottery_number = random.sample(range(1,46),6)\n", 193 | " lottery_number = sorted(lottery_number, key=lambda x: x)\n", 194 | " lottery_number\n", 195 | " print(lottery_number) \n", 196 | " return lottery_number\n", 197 | " \"\"\"}\n", 198 | "}\n", 199 | "result = requests.post(\"http://localhost:5000/transactions/new\", headers=headers, data=json.dumps(data)).content\n", 200 | "contract_address = json.loads(result)['contract_address']\n", 201 | "\n", 202 | "headers = {'Content-Type' : 'application/json; charset=utf-8'}\n", 203 | "res = requests.get(\"http://localhost:5000/mine\")\n", 204 | "print(res)" 205 | ] 206 | }, 207 | { 208 | "cell_type": "code", 209 | "execution_count": 7, 210 | "id": "d56f755c", 211 | "metadata": {}, 212 | "outputs": [ 213 | { 214 | "name": "stdout", 215 | "output_type": "stream", 216 | "text": [ 217 | "[5, 9, 17, 21, 26, 35]\n", 218 | "[5, 9, 17, 21, 26, 35]\n" 219 | ] 220 | } 221 | ], 222 | "source": [ 223 | "headers = {'Content-Type' : 'application/json; charset=utf-8'}\n", 224 | "res = requests.get(\"http://localhost:5000/chain\", headers=headers)\n", 225 | "res_json = json.loads(res.content)\n", 226 | "\n", 227 | "for _block in res_json['chain']:\n", 228 | " for _tx in _block['transactions']:\n", 229 | " if _tx['smart_contract']['contract_address'] ==contract_address:\n", 230 | " exec( _tx['smart_contract']['contract_code'])\n", 231 | " break \n", 232 | "print(Lottery()) " 233 | ] 234 | }, 235 | { 236 | "cell_type": "markdown", 237 | "id": "84aa0092", 238 | "metadata": {}, 239 | "source": [ 240 | "## DApp 제작 -3 Defi" 241 | ] 242 | }, 243 | { 244 | "cell_type": "code", 245 | "execution_count": 8, 246 | "id": "b2d8fb47", 247 | "metadata": {}, 248 | "outputs": [], 249 | "source": [ 250 | "token_name = 'pyTOKEN' \n", 251 | "token_total_volume = 100000\n", 252 | "token_owner = {'token_maker' : 10000}\n", 253 | "staking_status = {}" 254 | ] 255 | }, 256 | { 257 | "cell_type": "code", 258 | "execution_count": 9, 259 | "id": "4e862b3d", 260 | "metadata": {}, 261 | "outputs": [], 262 | "source": [ 263 | "def get_balance(user_id):\n", 264 | " print('{} Balance is : '.format(user_id), token_owner[user_id])\n", 265 | " return token_owner[user_id]\n", 266 | "\n", 267 | "def send_token(sender,recipent,amount):\n", 268 | " if sender in token_owner.keys():\n", 269 | " if get_balance(sender) > amount:\n", 270 | " token_owner[sender] = token_owner[sender] - amount\n", 271 | " if recipent in token_owner.keys():\n", 272 | " token_owner[recipent] = token_owner[recipent] + amount\n", 273 | " else :\n", 274 | " token_owner[recipent] = amount\n", 275 | " print(\"Transaction Completed\")\n", 276 | " get_balance(sender) \n", 277 | " get_balance(recipent) \n", 278 | "\n", 279 | " else:\n", 280 | " return \"Insufficient Balance\"\n", 281 | " else:\n", 282 | " return \"Unavailable Sender id\"" 283 | ] 284 | }, 285 | { 286 | "cell_type": "code", 287 | "execution_count": 10, 288 | "id": "0d09c317", 289 | "metadata": {}, 290 | "outputs": [], 291 | "source": [ 292 | "def token_staking(staker,amount):\n", 293 | " if staker in token_owner.keys():\n", 294 | " if get_balance(staker) > amount:\n", 295 | " token_owner[staker] = token_owner[staker] - amount\n", 296 | " staking_status [len(staking_status)] = {'staker':staker,'amount':amount}\n", 297 | " print(\"Staing Completed\")\n", 298 | " get_balance(staker) \n", 299 | " \n", 300 | " else:\n", 301 | " return \"Insufficient Balance\"\n", 302 | " else:\n", 303 | " return \"Unavailable Staker id\"\n", 304 | " \n", 305 | "def staking_yield(staking_status):\n", 306 | " for t in staking_status:\n", 307 | " print(staking_status[t])\n", 308 | " staking_status[t]['amount'] = staking_status[t]['amount'] * 1.1\n", 309 | " return staking_status" 310 | ] 311 | }, 312 | { 313 | "cell_type": "code", 314 | "execution_count": 23, 315 | "id": "3755c30a", 316 | "metadata": {}, 317 | "outputs": [ 318 | { 319 | "name": "stdout", 320 | "output_type": "stream", 321 | "text": [ 322 | "\n" 323 | ] 324 | } 325 | ], 326 | "source": [ 327 | "headers = {'Content-Type' : 'application/json; charset=utf-8'}\n", 328 | "data = {\n", 329 | " \"sender\": \"test_from\",\n", 330 | " \"recipient\": \"test_to\",\n", 331 | " \"amount\": 3,\n", 332 | " \"smart_contract\": {\n", 333 | " \"contract_code\" :\"token_name = 'pySTAKINGTOKEN' \\ntoken_total_volume = 100000\\ntoken_owner = {'token_maker' : 10000}\\nstaking_status = {}\",\n", 334 | " \"contract_function_getBalance\" :\"\"\"\n", 335 | "def get_balance(user_id):\n", 336 | " print('{} Balance is : '.format(user_id), token_owner[user_id])\n", 337 | " return token_owner[user_id]\n", 338 | "\"\"\",\n", 339 | " \"contract_function_sendToken\" :\"\"\"\n", 340 | "def send_token(sender,recipent,amount):\n", 341 | " if sender in token_owner.keys(): \n", 342 | " if get_balance(sender) > amount:\n", 343 | " token_owner[sender] = token_owner[sender] - amount\n", 344 | " if recipent in token_owner.keys():\n", 345 | " token_owner[recipent] = token_owner[recipent] + amount\n", 346 | " else :\n", 347 | " token_owner[recipent] = amount\n", 348 | " print(\"Transaction Completed\")\n", 349 | " get_balance(sender) \n", 350 | " get_balance(recipent) \n", 351 | "\n", 352 | " else:\n", 353 | " return \"Insufficient Balance\"\n", 354 | " else:\n", 355 | " return \"Unavailable Sender id\"\n", 356 | "\"\"\",\n", 357 | " \"contract_function_token_staking\" :\"\"\"\n", 358 | "def token_staking(staker,amount):\n", 359 | " if staker in token_owner.keys(): ## 예치자(staker)가 실제 존재하는 사용자인지 확인\n", 360 | " if get_balance(staker) > amount: ## 예치자(staker)의 잔고가 예치 금액보다 많은지 확인\n", 361 | " token_owner[staker] = token_owner[staker] - amount ## 예치자(staker)의 잔고에서 예치 금액 제외\n", 362 | " staking_status [len(staking_status)] = {'staker':staker,'amount':amount} \n", 363 | " ## 예치 정보(staking_status)에 예치자(staker)의 예치내역 저장\n", 364 | " print(\"Staing Completed\")\n", 365 | " get_balance(staker) \n", 366 | " \n", 367 | " else:\n", 368 | " return \"Insufficient Balance\"\n", 369 | " else:\n", 370 | " return \"Unavailable Staker id\"\n", 371 | "\"\"\",\n", 372 | " \"contract_function_staking_yield\" :\"\"\"\n", 373 | "def staking_yield(staking_status): ## 예치 이자 지급함수\n", 374 | " for t in staking_status:\n", 375 | " print(staking_status[t])\n", 376 | " staking_status[t]['amount'] = staking_status[t]['amount'] * (1+0.1) ## 예치 이자가 10% 지급된 금액으로 예치금 변경\n", 377 | " return staking_status\n", 378 | "\"\"\" \n", 379 | "\n", 380 | " }\n", 381 | " }\n", 382 | "result = requests.post(\"http://localhost:5000/transactions/new\", headers=headers, data=json.dumps(data)).content\n", 383 | "contract_address = json.loads(result)['contract_address']\n", 384 | "\n", 385 | "\n", 386 | "headers = {'Content-Type' : 'application/json; charset=utf-8'}\n", 387 | "res = requests.get(\"http://localhost:5000/mine\")\n", 388 | "print(res)" 389 | ] 390 | }, 391 | { 392 | "cell_type": "markdown", 393 | "id": "307a82ed", 394 | "metadata": {}, 395 | "source": [ 396 | "### pySTAKINGTOKEN 생성여부 확인" 397 | ] 398 | }, 399 | { 400 | "cell_type": "code", 401 | "execution_count": 24, 402 | "id": "2803f649", 403 | "metadata": {}, 404 | "outputs": [], 405 | "source": [ 406 | "headers = {'Content-Type' : 'application/json; charset=utf-8'}\n", 407 | "res = requests.get(\"http://localhost:5000/chain\", headers=headers)\n", 408 | "res_json = json.loads(res.content)\n", 409 | "\n", 410 | "for _block in res_json['chain']:\n", 411 | " for _tx in _block['transactions']:\n", 412 | " if _tx['smart_contract']['contract_address'] == contract_address:\n", 413 | " exec( _tx['smart_contract']['contract_code'])\n", 414 | " break" 415 | ] 416 | }, 417 | { 418 | "cell_type": "code", 419 | "execution_count": 25, 420 | "id": "b99b43d4", 421 | "metadata": {}, 422 | "outputs": [ 423 | { 424 | "data": { 425 | "text/plain": [ 426 | "'pySTAKINGTOKEN'" 427 | ] 428 | }, 429 | "execution_count": 25, 430 | "metadata": {}, 431 | "output_type": "execute_result" 432 | } 433 | ], 434 | "source": [ 435 | "token_name " 436 | ] 437 | }, 438 | { 439 | "cell_type": "code", 440 | "execution_count": 26, 441 | "id": "5b90fcfb", 442 | "metadata": {}, 443 | "outputs": [ 444 | { 445 | "data": { 446 | "text/plain": [ 447 | "100000" 448 | ] 449 | }, 450 | "execution_count": 26, 451 | "metadata": {}, 452 | "output_type": "execute_result" 453 | } 454 | ], 455 | "source": [ 456 | "token_total_volume" 457 | ] 458 | }, 459 | { 460 | "cell_type": "code", 461 | "execution_count": 27, 462 | "id": "b912f165", 463 | "metadata": {}, 464 | "outputs": [ 465 | { 466 | "name": "stdout", 467 | "output_type": "stream", 468 | "text": [ 469 | "token_maker Balance is : 10000\n" 470 | ] 471 | }, 472 | { 473 | "data": { 474 | "text/plain": [ 475 | "10000" 476 | ] 477 | }, 478 | "execution_count": 27, 479 | "metadata": {}, 480 | "output_type": "execute_result" 481 | } 482 | ], 483 | "source": [ 484 | "exec(_tx['smart_contract']['contract_function_getBalance'])\n", 485 | "get_balance('token_maker')" 486 | ] 487 | }, 488 | { 489 | "cell_type": "markdown", 490 | "id": "db80aa69", 491 | "metadata": {}, 492 | "source": [ 493 | "### pySTAKINGTOKEN 예치 진행" 494 | ] 495 | }, 496 | { 497 | "cell_type": "code", 498 | "execution_count": 28, 499 | "id": "2e43e5f8", 500 | "metadata": {}, 501 | "outputs": [ 502 | { 503 | "name": "stdout", 504 | "output_type": "stream", 505 | "text": [ 506 | "token_maker Balance is : 10000\n", 507 | "Staing Completed\n", 508 | "token_maker Balance is : 9900\n" 509 | ] 510 | } 511 | ], 512 | "source": [ 513 | "exec(_tx['smart_contract']['contract_function_token_staking'])\n", 514 | "token_staking('token_maker',100)" 515 | ] 516 | }, 517 | { 518 | "cell_type": "markdown", 519 | "id": "5519c427", 520 | "metadata": {}, 521 | "source": [ 522 | "### pySTAKINGTOKEN 예치자에게 이자 지급" 523 | ] 524 | }, 525 | { 526 | "cell_type": "code", 527 | "execution_count": 29, 528 | "id": "d0555d6f", 529 | "metadata": {}, 530 | "outputs": [ 531 | { 532 | "name": "stdout", 533 | "output_type": "stream", 534 | "text": [ 535 | "{'staker': 'token_maker', 'amount': 100}\n" 536 | ] 537 | }, 538 | { 539 | "data": { 540 | "text/plain": [ 541 | "{0: {'staker': 'token_maker', 'amount': 110.00000000000001}}" 542 | ] 543 | }, 544 | "execution_count": 29, 545 | "metadata": {}, 546 | "output_type": "execute_result" 547 | } 548 | ], 549 | "source": [ 550 | "exec(_tx['smart_contract']['contract_function_staking_yield'])\n", 551 | "staking_yield(staking_status)" 552 | ] 553 | }, 554 | { 555 | "cell_type": "code", 556 | "execution_count": 30, 557 | "id": "341e2d2f", 558 | "metadata": {}, 559 | "outputs": [ 560 | { 561 | "name": "stdout", 562 | "output_type": "stream", 563 | "text": [ 564 | "{'staker': 'token_maker', 'amount': 110.00000000000001}\n" 565 | ] 566 | }, 567 | { 568 | "data": { 569 | "text/plain": [ 570 | "{0: {'staker': 'token_maker', 'amount': 121.00000000000003}}" 571 | ] 572 | }, 573 | "execution_count": 30, 574 | "metadata": {}, 575 | "output_type": "execute_result" 576 | } 577 | ], 578 | "source": [ 579 | "exec(_tx['smart_contract']['contract_function_staking_yield'])\n", 580 | "staking_yield(staking_status)" 581 | ] 582 | }, 583 | { 584 | "cell_type": "code", 585 | "execution_count": 31, 586 | "id": "116113c8", 587 | "metadata": {}, 588 | "outputs": [ 589 | { 590 | "name": "stdout", 591 | "output_type": "stream", 592 | "text": [ 593 | "{'staker': 'token_maker', 'amount': 121.00000000000003}\n" 594 | ] 595 | }, 596 | { 597 | "data": { 598 | "text/plain": [ 599 | "{0: {'staker': 'token_maker', 'amount': 133.10000000000005}}" 600 | ] 601 | }, 602 | "execution_count": 31, 603 | "metadata": {}, 604 | "output_type": "execute_result" 605 | } 606 | ], 607 | "source": [ 608 | "exec(_tx['smart_contract']['contract_function_staking_yield'])\n", 609 | "staking_yield(staking_status)" 610 | ] 611 | }, 612 | { 613 | "cell_type": "code", 614 | "execution_count": null, 615 | "id": "2eb7d62f", 616 | "metadata": {}, 617 | "outputs": [], 618 | "source": [] 619 | } 620 | ], 621 | "metadata": { 622 | "kernelspec": { 623 | "display_name": "Python 3", 624 | "language": "python", 625 | "name": "python3" 626 | }, 627 | "language_info": { 628 | "codemirror_mode": { 629 | "name": "ipython", 630 | "version": 3 631 | }, 632 | "file_extension": ".py", 633 | "mimetype": "text/x-python", 634 | "name": "python", 635 | "nbconvert_exporter": "python", 636 | "pygments_lexer": "ipython3", 637 | "version": "3.8.8" 638 | } 639 | }, 640 | "nbformat": 4, 641 | "nbformat_minor": 5 642 | } 643 | -------------------------------------------------------------------------------- /chapter5/smart_contract/node.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "09d52509", 6 | "metadata": {}, 7 | "source": [ 8 | "## 관련 패키지 import" 9 | ] 10 | }, 11 | { 12 | "cell_type": "code", 13 | "execution_count": 1, 14 | "id": "6787859c", 15 | "metadata": {}, 16 | "outputs": [], 17 | "source": [ 18 | "import hashlib # hash 함수용 sha256 사용할 라이브러리\n", 19 | "import json\n", 20 | "import time\n", 21 | "import random\n", 22 | "import requests\n", 23 | "import datetime\n", 24 | "from flask import Flask, request, jsonify" 25 | ] 26 | }, 27 | { 28 | "cell_type": "markdown", 29 | "id": "16221830", 30 | "metadata": {}, 31 | "source": [ 32 | "## Blockchain 객채 생성" 33 | ] 34 | }, 35 | { 36 | "cell_type": "code", 37 | "execution_count": 2, 38 | "id": "455e3708", 39 | "metadata": {}, 40 | "outputs": [], 41 | "source": [ 42 | "class Blockchain(object):\n", 43 | " \n", 44 | " def __init__(self, account_name, account_weight):\n", 45 | " self.chain = [] # chain에 여러 block들 들어옴\n", 46 | " self.current_transaction = [] # 임시 transaction 넣어줌\n", 47 | " self.nodes = set() # Node 목록을 보관\n", 48 | " self.miner_wallet = {'account_name': account_name, 'weight': account_weight} # 지갑정보 생성\n", 49 | " self.new_block(previous_hash='genesis_block', address = account_name) # genesis block 생성\n", 50 | " self.account_name = account_name\n", 51 | " self.account_weight = account_weight\n", 52 | "\n", 53 | " \n", 54 | " @staticmethod\n", 55 | " def hash(block):\n", 56 | " block_string = json.dumps(block, sort_keys=True).encode() \n", 57 | " return hashlib.sha256(block_string).hexdigest() # hash 라이브러리로 sha256 사용\n", 58 | " \n", 59 | " @property\n", 60 | " def last_block(self):\n", 61 | " return self.chain[-1] # 체인의 마지막 블록 가져오기!!\n", 62 | "\n", 63 | "\n", 64 | " def pos(self):\n", 65 | " winner_list = [] # 각 노드에서 pick_winner 결과 뽑힌 winner 리스트\n", 66 | " time.sleep(1)\n", 67 | " my_winner = self.pick_winner(account_name = self.account_name, account_weight = self.account_weight) \n", 68 | " winner_list.append(my_winner) # winner 리스트에 내노드 결과 넣기\n", 69 | " time.sleep(1)\n", 70 | " \n", 71 | " for target_node in blockchain.nodes: # 다른 노드들도 pick_winner 진행 \n", 72 | " print(target_node)\n", 73 | " headers = {'Content-Type' : 'application/json; charset=utf-8'}\n", 74 | " res = requests.get('http://' + target_node + \"/nodes/pick_winner\", headers=headers)\n", 75 | " winner_info = json.loads(res.content) # 근처 노드들 선정결과 받아와서\n", 76 | " print(winner_info)\n", 77 | " winner_list.append(winner_info['winner']) \n", 78 | "\n", 79 | " final_winner = max(winner_list,key = winner_list.count) # 각 노드들의 pos 결과로 가장 많이 선정된 winner를 최종 winner 로 선정\n", 80 | " print(\"final_winner selected : \", final_winner)\n", 81 | " \n", 82 | " return final_winner\n", 83 | " \n", 84 | " \n", 85 | " def pick_winner(self,account_name, account_weight): ### 누가누가 블록 만들래!! 만들사람 뽑기\n", 86 | " candidate_list = [] # POS 대상자를 뽑을 전체 풀!!\n", 87 | " \n", 88 | " for w in range(account_weight): # 나의 노드들의 weight 수만큼 추가\n", 89 | " candidate_list.append(account_name)\n", 90 | " \n", 91 | " random.shuffle(candidate_list) # 랜덤으로 섞고!\n", 92 | " for x in candidate_list: # 첫번째 node를 winner로 선정\n", 93 | " winner = x\n", 94 | " print(\"WINNER SELECTED : \", winner)\n", 95 | " break\n", 96 | " \n", 97 | " return winner # winner 공개\n", 98 | " \n", 99 | " \n", 100 | " \n", 101 | " def new_transaction(self, sender, recipient, amount, smart_contract):\n", 102 | " self.current_transaction.append(\n", 103 | " {\n", 104 | " 'sender' : sender, # 송신자\n", 105 | " 'recipient' : recipient, # 수신자\n", 106 | " 'amount' : amount, # 금액\n", 107 | " 'timestamp':time.time(),\n", 108 | " 'smart_contract' : smart_contract\n", 109 | " }\n", 110 | " )\n", 111 | " return self.last_block['index'] + 1 \n", 112 | "\n", 113 | " def new_block(self, previous_hash=None, address = ''):\n", 114 | " block = {\n", 115 | " 'index' : len(self.chain)+1,\n", 116 | " 'timestamp' : time.time(), # timestamp from 1970\n", 117 | " 'transactions' : self.current_transaction,\n", 118 | " 'previous_hash' : previous_hash ,\n", 119 | " 'validator' : address\n", 120 | " }\n", 121 | " block[\"hash\"] = self.hash(block)\n", 122 | " self.current_transaction = []\n", 123 | " self.chain.append(block) \n", 124 | " return block\n", 125 | "\n", 126 | " " 127 | ] 128 | }, 129 | { 130 | "cell_type": "markdown", 131 | "id": "cccc5e32", 132 | "metadata": {}, 133 | "source": [ 134 | "## Blockchain 객채를 기반으로 노드 생성" 135 | ] 136 | }, 137 | { 138 | "cell_type": "code", 139 | "execution_count": 3, 140 | "id": "fdd2e589", 141 | "metadata": {}, 142 | "outputs": [], 143 | "source": [ 144 | "my_ip = '0.0.0.0'\n", 145 | "my_port = '5000'\n", 146 | "node_identifier = 'node_'+my_port\n", 147 | "mine_owner = 'master'\n", 148 | "mine_profit = 0.1\n", 149 | "\n", 150 | "blockchain = Blockchain(account_name=mine_owner, account_weight= 100)" 151 | ] 152 | }, 153 | { 154 | "cell_type": "code", 155 | "execution_count": 4, 156 | "id": "2228cdbe", 157 | "metadata": {}, 158 | "outputs": [ 159 | { 160 | "name": "stdout", 161 | "output_type": "stream", 162 | "text": [ 163 | " * Serving Flask app \"__main__\" (lazy loading)\n", 164 | " * Environment: production\n", 165 | " WARNING: This is a development server. Do not use it in a production deployment.\n", 166 | " Use a production WSGI server instead.\n", 167 | " * Debug mode: off\n" 168 | ] 169 | }, 170 | { 171 | "name": "stderr", 172 | "output_type": "stream", 173 | "text": [ 174 | " * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)\n", 175 | "127.0.0.1 - - [06/Mar/2023 00:30:15] \"\u001b[37mPOST /transactions/new HTTP/1.1\u001b[0m\" 201 -\n" 176 | ] 177 | }, 178 | { 179 | "name": "stdout", 180 | "output_type": "stream", 181 | "text": [ 182 | "transactions_new!!! : {'sender': 'test_from', 'recipient': 'smart_contract', 'amount': 0, 'smart_contract': {'contract_code': 'calculate_result = {}{}{}'}}\n", 183 | "MINING STARTED\n", 184 | "WINNER SELECTED : master\n" 185 | ] 186 | }, 187 | { 188 | "name": "stderr", 189 | "output_type": "stream", 190 | "text": [ 191 | "127.0.0.1 - - [06/Mar/2023 00:30:19] \"\u001b[37mGET /mine HTTP/1.1\u001b[0m\" 200 -\n" 192 | ] 193 | }, 194 | { 195 | "name": "stdout", 196 | "output_type": "stream", 197 | "text": [ 198 | "final_winner selected : master\n", 199 | "MY NODE IS SELECTED AS MINER NODE\n" 200 | ] 201 | }, 202 | { 203 | "name": "stderr", 204 | "output_type": "stream", 205 | "text": [ 206 | "127.0.0.1 - - [06/Mar/2023 00:30:21] \"\u001b[37mGET /chain HTTP/1.1\u001b[0m\" 200 -\n" 207 | ] 208 | }, 209 | { 210 | "name": "stdout", 211 | "output_type": "stream", 212 | "text": [ 213 | "chain info requested!!\n" 214 | ] 215 | }, 216 | { 217 | "name": "stderr", 218 | "output_type": "stream", 219 | "text": [ 220 | "127.0.0.1 - - [06/Mar/2023 00:30:23] \"\u001b[37mGET /chain HTTP/1.1\u001b[0m\" 200 -\n" 221 | ] 222 | }, 223 | { 224 | "name": "stdout", 225 | "output_type": "stream", 226 | "text": [ 227 | "chain info requested!!\n" 228 | ] 229 | }, 230 | { 231 | "name": "stderr", 232 | "output_type": "stream", 233 | "text": [ 234 | "127.0.0.1 - - [06/Mar/2023 00:30:26] \"\u001b[37mGET /chain HTTP/1.1\u001b[0m\" 200 -\n" 235 | ] 236 | }, 237 | { 238 | "name": "stdout", 239 | "output_type": "stream", 240 | "text": [ 241 | "chain info requested!!\n" 242 | ] 243 | }, 244 | { 245 | "name": "stderr", 246 | "output_type": "stream", 247 | "text": [ 248 | "127.0.0.1 - - [06/Mar/2023 00:31:29] \"\u001b[37mPOST /transactions/new HTTP/1.1\u001b[0m\" 201 -\n" 249 | ] 250 | }, 251 | { 252 | "name": "stdout", 253 | "output_type": "stream", 254 | "text": [ 255 | "transactions_new!!! : {'sender': 'test_from', 'recipient': 'smart_contract', 'amount': 0, 'smart_contract': {'contract_code': '\\ndef Lottery():\\n lottery_number = random.sample(range(1,46),6)\\n lottery_number = sorted(lottery_number, key=lambda x: x)\\n lottery_number\\n print(lottery_number) \\n return lottery_number\\n '}}\n", 256 | "MINING STARTED\n", 257 | "WINNER SELECTED : master\n" 258 | ] 259 | }, 260 | { 261 | "name": "stderr", 262 | "output_type": "stream", 263 | "text": [ 264 | "127.0.0.1 - - [06/Mar/2023 00:31:33] \"\u001b[37mGET /mine HTTP/1.1\u001b[0m\" 200 -\n" 265 | ] 266 | }, 267 | { 268 | "name": "stdout", 269 | "output_type": "stream", 270 | "text": [ 271 | "final_winner selected : master\n", 272 | "MY NODE IS SELECTED AS MINER NODE\n" 273 | ] 274 | }, 275 | { 276 | "name": "stderr", 277 | "output_type": "stream", 278 | "text": [ 279 | "127.0.0.1 - - [06/Mar/2023 00:31:35] \"\u001b[37mGET /chain HTTP/1.1\u001b[0m\" 200 -\n" 280 | ] 281 | }, 282 | { 283 | "name": "stdout", 284 | "output_type": "stream", 285 | "text": [ 286 | "chain info requested!!\n" 287 | ] 288 | }, 289 | { 290 | "name": "stderr", 291 | "output_type": "stream", 292 | "text": [ 293 | "127.0.0.1 - - [06/Mar/2023 00:31:37] \"\u001b[37mPOST /transactions/new HTTP/1.1\u001b[0m\" 201 -\n" 294 | ] 295 | }, 296 | { 297 | "name": "stdout", 298 | "output_type": "stream", 299 | "text": [ 300 | "transactions_new!!! : {'sender': 'test_from', 'recipient': 'test_to', 'amount': 3, 'smart_contract': {'contract_code': \"token_name = 'pySTAKINGTOKEN' \\ntoken_total_volume = 100000\\ntoken_owner = {'token_maker' : 10000}\\nstaking_status = {}\", 'contract_function_getBalance': \"\\ndef get_balance(user_id):\\n print('{} Balance is : '.format(user_id), token_owner[user_id])\\n return token_owner[user_id]\\n\", 'contract_function_sendToken': '\\ndef send_token(sender,recipent,amount):\\n if sender in token_owner.keys(): \\n if get_balance(sender) > amount:\\n token_owner[sender] = token_owner[sender] - amount\\n if recipent in token_owner.keys():\\n token_owner[recipent] = token_owner[recipent] + amount\\n else :\\n token_owner[recipent] = amount\\n print(\"Transaction Completed\")\\n get_balance(sender) \\n get_balance(recipent) \\n\\n else:\\n return \"Insufficient Balance\"\\n else:\\n return \"Unavailable Sender id\"\\n', 'contract_function_token_staking': '\\ndef token_staking(staker,amount):\\n if staker in token_owner.keys(): ## 예치자(staker)가 실제 존재하는 사용자인지 확인\\n if get_balance(staker) > amount: ## 예치자(staker)의 잔고가 예치 금액보다 많은지 확인\\n token_owner[staker] = token_owner[staker] - amount ## 예치자(staker)의 잔고에서 예치 금액 제외\\n staking_status [len(staking_status)] = {\\'staker\\':staker,\\'amount\\':amount} \\n ## 예치 정보(staking_status)에 예치자(staker)의 예치내역 저장\\n print(\"Staing Completed\")\\n get_balance(staker) \\n \\n else:\\n return \"Insufficient Balance\"\\n else:\\n return \"Unavailable Staker id\"\\n', 'contract_function_staking_yield': \"\\ndef staking_yield(staking_status): ## 예치 이자 지급함수\\n for t in staking_status:\\n print(staking_status[t])\\n staking_status[t]['amount'] = staking_status[t]['amount'] * (1+0.1) ## 예치 이자가 10% 지급된 금액으로 예치금 변경\\n return staking_status\\n\"}}\n", 301 | "MINING STARTED\n", 302 | "WINNER SELECTED : master\n" 303 | ] 304 | }, 305 | { 306 | "name": "stderr", 307 | "output_type": "stream", 308 | "text": [ 309 | "127.0.0.1 - - [06/Mar/2023 00:31:42] \"\u001b[37mGET /mine HTTP/1.1\u001b[0m\" 200 -\n" 310 | ] 311 | }, 312 | { 313 | "name": "stdout", 314 | "output_type": "stream", 315 | "text": [ 316 | "final_winner selected : master\n", 317 | "MY NODE IS SELECTED AS MINER NODE\n" 318 | ] 319 | }, 320 | { 321 | "name": "stderr", 322 | "output_type": "stream", 323 | "text": [ 324 | "127.0.0.1 - - [06/Mar/2023 00:31:44] \"\u001b[37mGET /chain HTTP/1.1\u001b[0m\" 200 -\n" 325 | ] 326 | }, 327 | { 328 | "name": "stdout", 329 | "output_type": "stream", 330 | "text": [ 331 | "chain info requested!!\n" 332 | ] 333 | }, 334 | { 335 | "name": "stderr", 336 | "output_type": "stream", 337 | "text": [ 338 | "127.0.0.1 - - [06/Mar/2023 00:33:27] \"\u001b[37mPOST /transactions/new HTTP/1.1\u001b[0m\" 201 -\n" 339 | ] 340 | }, 341 | { 342 | "name": "stdout", 343 | "output_type": "stream", 344 | "text": [ 345 | "transactions_new!!! : {'sender': 'test_from', 'recipient': 'test_to', 'amount': 3, 'smart_contract': {'contract_code': \"token_name = 'pySTAKINGTOKEN' \\ntoken_total_volume = 100000\\ntoken_owner = {'token_maker' : 10000}\\nstaking_status = {}\", 'contract_function_getBalance': \"\\ndef get_balance(user_id):\\n print('{} Balance is : '.format(user_id), token_owner[user_id])\\n return token_owner[user_id]\\n\", 'contract_function_sendToken': '\\ndef send_token(sender,recipent,amount):\\n if sender in token_owner.keys(): \\n if get_balance(sender) > amount:\\n token_owner[sender] = token_owner[sender] - amount\\n if recipent in token_owner.keys():\\n token_owner[recipent] = token_owner[recipent] + amount\\n else :\\n token_owner[recipent] = amount\\n print(\"Transaction Completed\")\\n get_balance(sender) \\n get_balance(recipent) \\n\\n else:\\n return \"Insufficient Balance\"\\n else:\\n return \"Unavailable Sender id\"\\n', 'contract_function_token_staking': '\\ndef token_staking(staker,amount):\\n if staker in token_owner.keys(): ## 예치자(staker)가 실제 존재하는 사용자인지 확인\\n if get_balance(staker) > amount: ## 예치자(staker)의 잔고가 예치 금액보다 많은지 확인\\n token_owner[staker] = token_owner[staker] - amount ## 예치자(staker)의 잔고에서 예치 금액 제외\\n staking_status [len(staking_status)] = {\\'staker\\':staker,\\'amount\\':amount} \\n ## 예치 정보(staking_status)에 예치자(staker)의 예치내역 저장\\n print(\"Staing Completed\")\\n get_balance(staker) \\n \\n else:\\n return \"Insufficient Balance\"\\n else:\\n return \"Unavailable Staker id\"\\n', 'contract_function_staking_yield': \"\\ndef staking_yield(staking_status): ## 예치 이자 지급함수\\n for t in staking_status:\\n print(staking_status[t])\\n staking_status[t]['amount'] = staking_status[t]['amount'] * (1+0.1) ## 예치 이자가 10% 지급된 금액으로 예치금 변경\\n return staking_status\\n\"}}\n", 346 | "MINING STARTED\n", 347 | "WINNER SELECTED : master\n" 348 | ] 349 | }, 350 | { 351 | "name": "stderr", 352 | "output_type": "stream", 353 | "text": [ 354 | "127.0.0.1 - - [06/Mar/2023 00:33:31] \"\u001b[37mGET /mine HTTP/1.1\u001b[0m\" 200 -\n" 355 | ] 356 | }, 357 | { 358 | "name": "stdout", 359 | "output_type": "stream", 360 | "text": [ 361 | "final_winner selected : master\n", 362 | "MY NODE IS SELECTED AS MINER NODE\n" 363 | ] 364 | }, 365 | { 366 | "name": "stderr", 367 | "output_type": "stream", 368 | "text": [ 369 | "127.0.0.1 - - [06/Mar/2023 00:33:33] \"\u001b[37mGET /chain HTTP/1.1\u001b[0m\" 200 -\n" 370 | ] 371 | }, 372 | { 373 | "name": "stdout", 374 | "output_type": "stream", 375 | "text": [ 376 | "chain info requested!!\n" 377 | ] 378 | } 379 | ], 380 | "source": [ 381 | "app = Flask(__name__)\n", 382 | "\n", 383 | "@app.route('/chain', methods=['GET'])\n", 384 | "def full_chain():\n", 385 | " print(\"chain info requested!!\")\n", 386 | " response = {\n", 387 | " 'chain' : blockchain.chain, \n", 388 | " 'length' : len(blockchain.chain), \n", 389 | " }\n", 390 | " return jsonify(response), 200\n", 391 | "\n", 392 | "@app.route('/transactions/new', methods=['POST'])\n", 393 | "def new_transaction():\n", 394 | " values = request.get_json() \n", 395 | " print(\"transactions_new!!! : \", values)\n", 396 | " required = ['sender', 'recipient', 'amount'] \n", 397 | "\n", 398 | " if not all(k in values for k in required):\n", 399 | " return 'missing values', 400\n", 400 | " contract_address = hashlib.sha256(str(datetime.datetime.now()).encode() ).hexdigest()\n", 401 | " values['smart_contract'][\"contract_address\"] = contract_address \n", 402 | " \n", 403 | " if 'smart_contract' not in values:\n", 404 | " values['smart_contract'] = 'empty'\n", 405 | "\n", 406 | " index = blockchain.new_transaction(values['sender'],values['recipient'],\n", 407 | "values['amount'], values['smart_contract'])\n", 408 | " \n", 409 | " response = {'message' : 'Transaction will be added to Block {%s}' % index, \"contract_address\":contract_address}\n", 410 | " return jsonify(response), 201\n", 411 | "\n", 412 | "\n", 413 | "@app.route('/mine', methods=['GET'])\n", 414 | "def mine():\n", 415 | " print(\"MINING STARTED\") \n", 416 | " final_winner = blockchain.pos() \n", 417 | " \n", 418 | " if final_winner == blockchain.account_name: # 만약 본 노드가 winner로 선정되었으면 아래와 같이\n", 419 | "\n", 420 | " blockchain.new_transaction( # 나에게 보상을 주고\n", 421 | " sender=mine_owner, \n", 422 | " recipient=node_identifier, \n", 423 | " amount=mine_profit, # coinbase transaction \n", 424 | " smart_contract={\"contract_address\":\"mining_profit\"}, \n", 425 | " )\n", 426 | "\n", 427 | " previous_hash = blockchain.hash(blockchain.chain[-1])\n", 428 | " block = blockchain.new_block(previous_hash = previous_hash, address = mine_owner) # 신규 블록 생성\n", 429 | " print(\"MY NODE IS SELECTED AS MINER NODE\")\n", 430 | "\n", 431 | " response = {\n", 432 | " 'message' : 'new block found',\n", 433 | " 'index' : block['index'],\n", 434 | " 'transactions' : block['transactions'],\n", 435 | " 'nonce' : block['validator'],\n", 436 | " 'previous_hash' : block['previous_hash'],\n", 437 | " 'hash' : block['hash']\n", 438 | " }\n", 439 | "\n", 440 | " return jsonify(response), 200\n", 441 | " \n", 442 | " else : # isWinner = False : 본 노드가 winner가 아님\n", 443 | " print(\"MY NODE IS NOT SELECTED AS MINER NODE\")\n", 444 | "\n", 445 | " response = {\n", 446 | " 'message' : 'NOT SELECTED'\n", 447 | " }\n", 448 | "\n", 449 | " return jsonify(response), 200\n", 450 | " \n", 451 | "if __name__ == '__main__':\n", 452 | " app.run(host=my_ip, port=my_port)\n" 453 | ] 454 | }, 455 | { 456 | "cell_type": "code", 457 | "execution_count": null, 458 | "id": "0ea982d0", 459 | "metadata": {}, 460 | "outputs": [], 461 | "source": [] 462 | } 463 | ], 464 | "metadata": { 465 | "kernelspec": { 466 | "display_name": "Python 3", 467 | "language": "python", 468 | "name": "python3" 469 | }, 470 | "language_info": { 471 | "codemirror_mode": { 472 | "name": "ipython", 473 | "version": 3 474 | }, 475 | "file_extension": ".py", 476 | "mimetype": "text/x-python", 477 | "name": "python", 478 | "nbconvert_exporter": "python", 479 | "pygments_lexer": "ipython3", 480 | "version": "3.8.8" 481 | } 482 | }, 483 | "nbformat": 4, 484 | "nbformat_minor": 5 485 | } 486 | --------------------------------------------------------------------------------