From d3f65798f1c70a31695ebb574eadcce1d3e1c3dc Mon Sep 17 00:00:00 2001 From: William Luke Date: Thu, 10 Feb 2022 11:22:21 +0300 Subject: [PATCH] feat: add interactive token deployment --- .gitignore | 6 + README.md | 88 +++++++++- cic/actions/__init__.py | 0 cic/actions/deploy.py | 96 ++++++++++ cic/attachment.py | 2 +- cic/cmd/easy.py | 351 +++++++++++++++++++++++++++++++++++++ cic/cmd/export.py | 7 +- cic/cmd/ext.py | 2 +- cic/data/config/config.ini | 7 + cic/ext/eth/start.py | 28 +-- cic/meta.py | 44 +++-- cic/network.py | 14 +- cic/proof.py | 126 ++++++------- cic/runnable/cic_cmd.py | 3 + cic/token.py | 86 +++++---- requirements.txt | 1 + 16 files changed, 720 insertions(+), 141 deletions(-) create mode 100644 cic/actions/__init__.py create mode 100644 cic/actions/deploy.py create mode 100644 cic/cmd/easy.py diff --git a/.gitignore b/.gitignore index e581009..62505b6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,9 @@ __pycache__ *.pyc *.egg-info +.venv +build +.vscode +.idea +contracts +*.egg \ No newline at end of file diff --git a/README.md b/README.md index 1736aac..9b36108 100644 --- a/README.md +++ b/README.md @@ -6,14 +6,14 @@ token deployments. To install the project (replacing \ with the current version: 0.0.1): -``` shell +```shell python setup.py sdist pip install --extra-index-url https://pip.grassrootseconomics.net:8433 dist/cic-.tar.gz ``` ## Structure of the components -![image](data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIKICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPgo8IS0tIEdlbmVyYXRlZCBieSBncmFwaHZpeiB2ZXJzaW9uIDIuNDkuMCAoMCkKIC0tPgo8IS0tIFBhZ2VzOiAxIC0tPgo8c3ZnIHdpZHRoPSI0NTZwdCIgaGVpZ2h0PSIyNjBwdCIKIHZpZXdCb3g9IjAuMDAgMC4wMCA0NTUuNTAgMjYwLjAwIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIj4KPGcgaWQ9ImdyYXBoMCIgY2xhc3M9ImdyYXBoIiB0cmFuc2Zvcm09InNjYWxlKDEgMSkgcm90YXRlKDApIHRyYW5zbGF0ZSg0IDI1NikiPgo8cG9seWdvbiBmaWxsPSJ3aGl0ZSIgc3Ryb2tlPSJ0cmFuc3BhcmVudCIgcG9pbnRzPSItNCw0IC00LC0yNTYgNDUxLjUsLTI1NiA0NTEuNSw0IC00LDQiLz4KPCEtLSBpbW11dGFibGVfZGF0YSAtLT4KPGcgaWQ9Im5vZGUxIiBjbGFzcz0ibm9kZSI+Cjx0aXRsZT5pbW11dGFibGVfZGF0YTwvdGl0bGU+Cjxwb2x5Z29uIGZpbGw9Im5vbmUiIHN0cm9rZT0iYmxhY2siIHBvaW50cz0iNDQ3LjUsLTM2IDI4MS41LC0zNiAyODEuNSwwIDQ0Ny41LDAgNDQ3LjUsLTM2Ii8+Cjx0ZXh0IHRleHQtYW5jaG9yPSJtaWRkbGUiIHg9IjM2NC41IiB5PSItMTQuMyIgZm9udC1mYW1pbHk9IlRpbWVzLHNlcmlmIiBmb250LXNpemU9IjE0LjAwIj5JbW11dGFibGUgbWV0YWRhdGE8L3RleHQ+CjwvZz4KPCEtLSBtdXRhYmxlX2RhdGEgLS0+CjxnIGlkPSJub2RlMiIgY2xhc3M9Im5vZGUiPgo8dGl0bGU+bXV0YWJsZV9kYXRhPC90aXRsZT4KPHBvbHlnb24gZmlsbD0ibm9uZSIgc3Ryb2tlPSJibGFjayIgcG9pbnRzPSIyNjMuNSwtMzYgMTE1LjUsLTM2IDExNS41LDAgMjYzLjUsMCAyNjMuNSwtMzYiLz4KPHRleHQgdGV4dC1hbmNob3I9Im1pZGRsZSIgeD0iMTg5LjUiIHk9Ii0xNC4zIiBmb250LWZhbWlseT0iVGltZXMsc2VyaWYiIGZvbnQtc2l6ZT0iMTQuMDAiPk11dGFibGUgbWV0YWRhdGE8L3RleHQ+CjwvZz4KPCEtLSB0b2tlbl9zbWFydF9jb250cmFjdCAtLT4KPGcgaWQ9Im5vZGUzIiBjbGFzcz0ibm9kZSI+Cjx0aXRsZT50b2tlbl9zbWFydF9jb250cmFjdDwvdGl0bGU+Cjxwb2x5Z29uIGZpbGw9Im5vbmUiIHN0cm9rZT0iYmxhY2siIHBvaW50cz0iOTcsLTM2IDAsLTM2IDAsMCA5NywwIDk3LC0zNiIvPgo8dGV4dCB0ZXh0LWFuY2hvcj0ibWlkZGxlIiB4PSI0OC41IiB5PSItMTQuMyIgZm9udC1mYW1pbHk9IlRpbWVzLHNlcmlmIiBmb250LXNpemU9IjE0LjAwIj5Ub2tlbiBjb2RlPC90ZXh0Pgo8L2c+CjwhLS0gbmV0d29yayAtLT4KPGcgaWQ9Im5vZGU0IiBjbGFzcz0ibm9kZSI+Cjx0aXRsZT5uZXR3b3JrPC90aXRsZT4KPGVsbGlwc2UgZmlsbD0ibm9uZSIgc3Ryb2tlPSJibGFjayIgY3g9IjEyOC41IiBjeT0iLTIzNCIgcng9IjQ4Ljk5IiByeT0iMTgiLz4KPHRleHQgdGV4dC1hbmNob3I9Im1pZGRsZSIgeD0iMTI4LjUiIHk9Ii0yMzAuMyIgZm9udC1mYW1pbHk9IlRpbWVzLHNlcmlmIiBmb250LXNpemU9IjE0LjAwIj5uZXR3b3JrPC90ZXh0Pgo8L2c+CjwhLS0gZXh0ZW5zaW9uIC0tPgo8ZyBpZD0ibm9kZTUiIGNsYXNzPSJub2RlIj4KPHRpdGxlPmV4dGVuc2lvbjwvdGl0bGU+CjxlbGxpcHNlIGZpbGw9Im5vbmUiIHN0cm9rZT0iYmxhY2siIGN4PSIxODkuNSIgY3k9Ii0xNjIiIHJ4PSI1NS43OSIgcnk9IjE4Ii8+Cjx0ZXh0IHRleHQtYW5jaG9yPSJtaWRkbGUiIHg9IjE4OS41IiB5PSItMTU4LjMiIGZvbnQtZmFtaWx5PSJUaW1lcyxzZXJpZiIgZm9udC1zaXplPSIxNC4wMCI+ZXh0ZW5zaW9uPC90ZXh0Pgo8L2c+CjwhLS0gbmV0d29yayYjNDU7Jmd0O2V4dGVuc2lvbiAtLT4KPGcgaWQ9ImVkZ2UxIiBjbGFzcz0iZWRnZSI+Cjx0aXRsZT5uZXR3b3JrJiM0NTsmZ3Q7ZXh0ZW5zaW9uPC90aXRsZT4KPHBhdGggZmlsbD0ibm9uZSIgc3Ryb2tlPSJibGFjayIgZD0iTTE0Mi42NSwtMjE2Ljc2QzE1MC4yNCwtMjA4LjA2IDE1OS43NCwtMTk3LjE1IDE2OC4yMSwtMTg3LjQzIi8+Cjxwb2x5Z29uIGZpbGw9ImJsYWNrIiBzdHJva2U9ImJsYWNrIiBwb2ludHM9IjE3MS4wNywtMTg5LjQ3IDE3NS4wMSwtMTc5LjYzIDE2NS44LC0xODQuODcgMTcxLjA3LC0xODkuNDciLz4KPC9nPgo8IS0tIHRva2VuIC0tPgo8ZyBpZD0ibm9kZTYiIGNsYXNzPSJub2RlIj4KPHRpdGxlPnRva2VuPC90aXRsZT4KPGVsbGlwc2UgZmlsbD0ibm9uZSIgc3Ryb2tlPSJibGFjayIgY3g9Ijc0LjUiIGN5PSItOTAiIHJ4PSIzNi4yOSIgcnk9IjE4Ii8+Cjx0ZXh0IHRleHQtYW5jaG9yPSJtaWRkbGUiIHg9Ijc0LjUiIHk9Ii04Ni4zIiBmb250LWZhbWlseT0iVGltZXMsc2VyaWYiIGZvbnQtc2l6ZT0iMTQuMDAiPnRva2VuPC90ZXh0Pgo8L2c+CjwhLS0gZXh0ZW5zaW9uJiM0NTsmZ3Q7dG9rZW4gLS0+CjxnIGlkPSJlZGdlMiIgY2xhc3M9ImVkZ2UiPgo8dGl0bGU+ZXh0ZW5zaW9uJiM0NTsmZ3Q7dG9rZW48L3RpdGxlPgo8cGF0aCBmaWxsPSJub25lIiBzdHJva2U9ImJsYWNrIiBkPSJNMTY0LjU1LC0xNDUuODFDMTQ3LjE4LC0xMzUuMjQgMTIzLjg5LC0xMjEuMDcgMTA1LjM2LC0xMDkuNzgiLz4KPHBvbHlnb24gZmlsbD0iYmxhY2siIHN0cm9rZT0iYmxhY2siIHBvaW50cz0iMTA3LjA1LC0xMDYuNzEgOTYuNjgsLTEwNC41IDEwMy40MSwtMTEyLjY5IDEwNy4wNSwtMTA2LjcxIi8+CjwvZz4KPCEtLSBwcm9vZiAtLT4KPGcgaWQ9Im5vZGU4IiBjbGFzcz0ibm9kZSI+Cjx0aXRsZT5wcm9vZjwvdGl0bGU+CjxlbGxpcHNlIGZpbGw9Im5vbmUiIHN0cm9rZT0iYmxhY2siIGN4PSIzNDUuNSIgY3k9Ii05MCIgcng9IjM1LjE5IiByeT0iMTgiLz4KPHRleHQgdGV4dC1hbmNob3I9Im1pZGRsZSIgeD0iMzQ1LjUiIHk9Ii04Ni4zIiBmb250LWZhbWlseT0iVGltZXMsc2VyaWYiIGZvbnQtc2l6ZT0iMTQuMDAiPnByb29mPC90ZXh0Pgo8L2c+CjwhLS0gZXh0ZW5zaW9uJiM0NTsmZ3Q7cHJvb2YgLS0+CjxnIGlkPSJlZGdlNSIgY2xhc3M9ImVkZ2UiPgo8dGl0bGU+ZXh0ZW5zaW9uJiM0NTsmZ3Q7cHJvb2Y8L3RpdGxlPgo8cGF0aCBmaWxsPSJub25lIiBzdHJva2U9ImJsYWNrIiBkPSJNMjIwLjY5LC0xNDdDMjQ2LjY3LC0xMzUuMzUgMjgzLjYzLC0xMTguNzYgMzEwLjU5LC0xMDYuNjciLz4KPHBvbHlnb24gZmlsbD0iYmxhY2siIHN0cm9rZT0iYmxhY2siIHBvaW50cz0iMzEyLjM0LC0xMDkuNzIgMzIwLjAzLC0xMDIuNDMgMzA5LjQ3LC0xMDMuMzMgMzEyLjM0LC0xMDkuNzIiLz4KPC9nPgo8IS0tIG1ldGEgLS0+CjxnIGlkPSJub2RlOSIgY2xhc3M9Im5vZGUiPgo8dGl0bGU+bWV0YTwvdGl0bGU+CjxlbGxpcHNlIGZpbGw9Im5vbmUiIHN0cm9rZT0iYmxhY2siIGN4PSIxODkuNSIgY3k9Ii05MCIgcng9IjM0LjM5IiByeT0iMTgiLz4KPHRleHQgdGV4dC1hbmNob3I9Im1pZGRsZSIgeD0iMTg5LjUiIHk9Ii04Ni4zIiBmb250LWZhbWlseT0iVGltZXMsc2VyaWYiIGZvbnQtc2l6ZT0iMTQuMDAiPm1ldGE8L3RleHQ+CjwvZz4KPCEtLSBleHRlbnNpb24mIzQ1OyZndDttZXRhIC0tPgo8ZyBpZD0iZWRnZTYiIGNsYXNzPSJlZGdlIj4KPHRpdGxlPmV4dGVuc2lvbiYjNDU7Jmd0O21ldGE8L3RpdGxlPgo8cGF0aCBmaWxsPSJub25lIiBzdHJva2U9ImJsYWNrIiBkPSJNMTg5LjUsLTE0My43QzE4OS41LC0xMzUuOTggMTg5LjUsLTEyNi43MSAxODkuNSwtMTE4LjExIi8+Cjxwb2x5Z29uIGZpbGw9ImJsYWNrIiBzdHJva2U9ImJsYWNrIiBwb2ludHM9IjE5MywtMTE4LjEgMTg5LjUsLTEwOC4xIDE4NiwtMTE4LjEgMTkzLC0xMTguMSIvPgo8L2c+CjwhLS0gdG9rZW4mIzQ1OyZndDt0b2tlbl9zbWFydF9jb250cmFjdCAtLT4KPGcgaWQ9ImVkZ2UzIiBjbGFzcz0iZWRnZSI+Cjx0aXRsZT50b2tlbiYjNDU7Jmd0O3Rva2VuX3NtYXJ0X2NvbnRyYWN0PC90aXRsZT4KPHBhdGggZmlsbD0ibm9uZSIgc3Ryb2tlPSJibGFjayIgZD0iTTY4LjIxLC03Mi4wNUM2NS4yOCwtNjQuMTggNjEuNzMsLTU0LjYyIDU4LjQ1LC00NS43OSIvPgo8cG9seWdvbiBmaWxsPSJibGFjayIgc3Ryb2tlPSJibGFjayIgcG9pbnRzPSI2MS42OCwtNDQuNDMgNTQuOTIsLTM2LjI4IDU1LjEyLC00Ni44NyA2MS42OCwtNDQuNDMiLz4KPC9nPgo8IS0tIHByb2Nlc3NvciAtLT4KPGcgaWQ9Im5vZGU3IiBjbGFzcz0ibm9kZSI+Cjx0aXRsZT5wcm9jZXNzb3I8L3RpdGxlPgo8ZWxsaXBzZSBmaWxsPSJub25lIiBzdHJva2U9ImJsYWNrIiBjeD0iMjUxLjUiIGN5PSItMjM0IiByeD0iNTUuNzkiIHJ5PSIxOCIvPgo8dGV4dCB0ZXh0LWFuY2hvcj0ibWlkZGxlIiB4PSIyNTEuNSIgeT0iLTIzMC4zIiBmb250LWZhbWlseT0iVGltZXMsc2VyaWYiIGZvbnQtc2l6ZT0iMTQuMDAiPnByb2Nlc3NvcjwvdGV4dD4KPC9nPgo8IS0tIHByb2Nlc3NvciYjNDU7Jmd0O2V4dGVuc2lvbiAtLT4KPGcgaWQ9ImVkZ2U0IiBjbGFzcz0iZWRnZSI+Cjx0aXRsZT5wcm9jZXNzb3ImIzQ1OyZndDtleHRlbnNpb248L3RpdGxlPgo8cGF0aCBmaWxsPSJub25lIiBzdHJva2U9ImJsYWNrIiBkPSJNMjM2LjgxLC0yMTYuNDFDMjI5LjA5LC0yMDcuNjkgMjE5LjQ4LC0xOTYuODUgMjEwLjk0LC0xODcuMjEiLz4KPHBvbHlnb24gZmlsbD0iYmxhY2siIHN0cm9rZT0iYmxhY2siIHBvaW50cz0iMjEzLjM0LC0xODQuNjQgMjA0LjA5LC0xNzkuNDcgMjA4LjEsLTE4OS4yOCAyMTMuMzQsLTE4NC42NCIvPgo8L2c+CjwhLS0gcHJvb2YmIzQ1OyZndDtpbW11dGFibGVfZGF0YSAtLT4KPGcgaWQ9ImVkZ2U5IiBjbGFzcz0iZWRnZSI+Cjx0aXRsZT5wcm9vZiYjNDU7Jmd0O2ltbXV0YWJsZV9kYXRhPC90aXRsZT4KPHBhdGggZmlsbD0ibm9uZSIgc3Ryb2tlPSJibGFjayIgZD0iTTM1MC4xLC03Mi4wNUMzNTIuMjEsLTY0LjI2IDM1NC43OCwtNTQuODIgMzU3LjE1LC00Ni4wOCIvPgo8cG9seWdvbiBmaWxsPSJibGFjayIgc3Ryb2tlPSJibGFjayIgcG9pbnRzPSIzNjAuNTcsLTQ2Ljg0IDM1OS44MSwtMzYuMjggMzUzLjgxLC00NS4wMSAzNjAuNTcsLTQ2Ljg0Ii8+CjwvZz4KPCEtLSBtZXRhJiM0NTsmZ3Q7bXV0YWJsZV9kYXRhIC0tPgo8ZyBpZD0iZWRnZTciIGNsYXNzPSJlZGdlIj4KPHRpdGxlPm1ldGEmIzQ1OyZndDttdXRhYmxlX2RhdGE8L3RpdGxlPgo8cGF0aCBmaWxsPSJub25lIiBzdHJva2U9ImJsYWNrIiBkPSJNMTg5LjUsLTcxLjdDMTg5LjUsLTYzLjk4IDE4OS41LC01NC43MSAxODkuNSwtNDYuMTEiLz4KPHBvbHlnb24gZmlsbD0iYmxhY2siIHN0cm9rZT0iYmxhY2siIHBvaW50cz0iMTkzLC00Ni4xIDE4OS41LC0zNi4xIDE4NiwtNDYuMSAxOTMsLTQ2LjEiLz4KPC9nPgo8IS0tIGF0dGFjaG1lbnQgLS0+CjxnIGlkPSJub2RlMTAiIGNsYXNzPSJub2RlIj4KPHRpdGxlPmF0dGFjaG1lbnQ8L3RpdGxlPgo8ZWxsaXBzZSBmaWxsPSJub25lIiBzdHJva2U9ImJsYWNrIiBjeD0iMzQ1LjUiIGN5PSItMTYyIiByeD0iNjMuODkiIHJ5PSIxOCIvPgo8dGV4dCB0ZXh0LWFuY2hvcj0ibWlkZGxlIiB4PSIzNDUuNSIgeT0iLTE1OC4zIiBmb250LWZhbWlseT0iVGltZXMsc2VyaWYiIGZvbnQtc2l6ZT0iMTQuMDAiPmF0dGFjaG1lbnQ8L3RleHQ+CjwvZz4KPCEtLSBhdHRhY2htZW50JiM0NTsmZ3Q7cHJvb2YgLS0+CjxnIGlkPSJlZGdlOCIgY2xhc3M9ImVkZ2UiPgo8dGl0bGU+YXR0YWNobWVudCYjNDU7Jmd0O3Byb29mPC90aXRsZT4KPHBhdGggZmlsbD0ibm9uZSIgc3Ryb2tlPSJibGFjayIgZD0iTTM0NS41LC0xNDMuN0MzNDUuNSwtMTM1Ljk4IDM0NS41LC0xMjYuNzEgMzQ1LjUsLTExOC4xMSIvPgo8cG9seWdvbiBmaWxsPSJibGFjayIgc3Ryb2tlPSJibGFjayIgcG9pbnRzPSIzNDksLTExOC4xIDM0NS41LC0xMDguMSAzNDIsLTExOC4xIDM0OSwtMTE4LjEiLz4KPC9nPgo8L2c+Cjwvc3ZnPgo=) +![image](./doc/sphinx/components.svg) CIC-CLI is designed to interface any network type backend. The current state of the package contains interface to EVM only. Thus, the examples @@ -27,12 +27,92 @@ documentation for more information. To initialize a new token deployment for the EVM: -``` shell +```shell cic init --target eth --name --symbol --precision ``` To automatically fill in settings detected in the network for the EVM: -``` shell +```shell cic ext --registry -d -i -p eth ``` + +## Development + +### Setup + +``` + python3 -m venv ./.venv + source ./.venv/bin/activate + pip install --extra-index-url https://pip.grassrootseconomics.net -r requirements.txt -r test_requirements.txt eth_requirements.txt +``` + +### Running the CLI + +```bash +python -m cic.runnable.cic_cmd -h # Help +``` + +### Deploying Token + +1. Generate Token Template + +``` +python -m cic.runnable.cic_cmd init --target eth --name "Foo Token" --symbol FOO --precision 6 foo +``` + +2. Populating network.json +Ea6225212005E86a4490018Ded4bf37F3E772161 +``` +python -m cic.runnable.cic_cmd ext -p http://localhost:63545 -i evm:byzantium:8996:bloxberg --registry 0xcf60ebc445b636a5ab787f9e8bc465a2a3ef8299 -d foo eth +``` + +add eb3907ecad74a0013c259d5874ae7f22dcbcc95c from stack/apps/contact_migrations/keystore:address to foo.network.resources.eth.contents.\*.key_account + +Fill out proof.json + +``` +{ + "description": "Smoking is bad for your health", + "issuer": "William Luke", + "namespace": "ge", + "proofs": [], + "version": 0 +} +``` + +Fill out token.json +[eth-erc20](https://gitlab.com/cicnet/eth-erc20) +[erc20-demurrage-token](https://gitlab.com/cicnet/erc20-demurrage-token) + +``` +{ + "code": "/home/will/grassroots/eth-erc20/python/giftable_erc20_token/data/GiftableToken.bin", + "extra": [ + { + "arg": "", + "arg_type": "" + } + ], + "name": "Foo Token", + "precision": "6", + "supply": 1000000000000000000000000, + "symbol": "FOO" +} +``` + +Deploy +0xcf60ebc445b636a5ab787f9e8bc465a2a3ef8299 +``` +python -m cic.runnable.cic_cmd export --metadata-endpoint http://localhost:63380 -vv -y /home/will/grassroots/cic-internal-integration/apps/contract-migration/keystore -o out -d foo eth +``` + +eth-contract-registry-list -e 0xcf60ebc445b636a5ab787f9e8bc465a2a3ef8299 -u +Use TokenRegistery for e +eth-token-index-list -p http://localhost:63545 -i evm:byzantium:8996:bloxberg -e eb3907ecad74a0013c259d5874ae7f22dcbcc95c -u + +### Tests + +``` +bash ./run_tests.sh +``` diff --git a/cic/actions/__init__.py b/cic/actions/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/cic/actions/deploy.py b/cic/actions/deploy.py new file mode 100644 index 0000000..425eb2d --- /dev/null +++ b/cic/actions/deploy.py @@ -0,0 +1,96 @@ +# standard imports +import logging +import importlib +import os + +# external imports +from cic_types.ext.metadata import MetadataRequestsHandler +from cic_types.ext.metadata.signer import Signer as MetadataSigner + +# local imports +from cic import ( + Proof, + Processor, + ) +from cic.output import ( + HTTPWriter, + KeyedWriterFactory, + ) +from cic.meta import ( + Meta, + MetadataWriter, + ) +from cic.attachment import Attachment +from cic.network import Network +from cic.token import Token +from typing import Optional +logg = logging.getLogger(__name__) + + + + + +def init_writers_from_config(config): + w = { + 'meta': None, + 'attachment': None, + 'proof': None, + 'ext': None, + } + for v in w.keys(): + k = 'CIC_CORE_{}_WRITER'.format(v.upper()) + (d, c) = config.get(k).rsplit('.', maxsplit=1) + m = importlib.import_module(d) + o = getattr(m, c) + w[v] = o + + return w + + +def deploy(config, target: str,contract_directory: str, metadata_endpoint: Optional[str], keystore_directory: str, key_file_path: str, gpg_passphrase: str): + modname = 'cic.ext.{}'.format(target) + cmd_mod = importlib.import_module(modname) + + writers = init_writers_from_config(config) + output_directory = os.path.join(contract_directory, 'out') + output_writer_path_meta = output_directory + if metadata_endpoint != None: + MetadataRequestsHandler.base_url = metadata_endpoint + MetadataSigner.gpg_path = os.path.join('/tmp') + MetadataSigner.key_file_path = key_file_path + MetadataSigner.gpg_passphrase = gpg_passphrase + writers['proof'] = KeyedWriterFactory(MetadataWriter, HTTPWriter).new + writers['attachment'] = KeyedWriterFactory(None, HTTPWriter).new + writers['meta'] = MetadataWriter + output_writer_path_meta = metadata_endpoint + + ct = Token(path=contract_directory) + cm = Meta(path=contract_directory, writer=writers['meta'](path=output_writer_path_meta)) + ca = Attachment(path=contract_directory, writer=writers['attachment'](path=output_writer_path_meta)) + cp = Proof(path=contract_directory, attachments=ca, writer=writers['proof'](path=output_writer_path_meta)) + cn = Network(path=contract_directory) + + ca.load() + ct.load() + cp.load() + cm.load() + cn.load() + + chain_spec = None + try: + chain_spec = config.get('CHAIN_SPEC') + except KeyError: + chain_spec = cn.chain_spec + config.add(chain_spec, 'CHAIN_SPEC', exists_ok=True) + logg.debug(f'CHAIN_SPEC config set to {str(chain_spec)}') + + (rpc, signer) = cmd_mod.parse_adapter(config, keystore_directory) + + ref = cn.resource(target) + chain_spec = cn.chain_spec(target) + logg.debug('found reference {} chain spec {} for target {}'.format(ref['contents'], chain_spec, target)) + c = getattr(cmd_mod, 'new')(chain_spec, ref['contents'], cp, signer_hint=signer, rpc=rpc, outputs_writer=writers['ext'](path=output_directory)) + c.apply_token(ct) + + p = Processor(proof=cp, attachment=ca, metadata=cm, extensions=[c]) + p.process() diff --git a/cic/attachment.py b/cic/attachment.py index 2f97609..1415655 100644 --- a/cic/attachment.py +++ b/cic/attachment.py @@ -1,6 +1,6 @@ # standard imports -import os import logging +import os # local imports from .base import * diff --git a/cic/cmd/easy.py b/cic/cmd/easy.py new file mode 100644 index 0000000..3411d39 --- /dev/null +++ b/cic/cmd/easy.py @@ -0,0 +1,351 @@ +# standard import +import importlib +import json +import logging +import os +import subprocess + +import requests + +# external imports +from chainlib.chain import ChainSpec + +# local imports +from cic import Proof +from cic.actions.deploy import deploy +from cic.attachment import Attachment +from cic.meta import Meta +from cic.network import Network +from cic.token import Token + +log = logging.getLogger(__name__) + + +def process_args(argparser): + argparser.add_argument( + "--skip-gen", action="store_true", default=False, help="Skip Generation" + ) + argparser.add_argument( + "--skip-deploy", + action="store_true", + help="Skip Deployment", + ) + argparser.add_argument( + "--target", + default="eth", + help="Contract Tech Target (eth)", + ) + argparser.add_argument( + "path", + type=str, + help="Path to generate/use contract deployment info", + ) + argparser.add_argument( + "-p", + type=str, + help="RPC Provider (http://localhost:8545)", + ) + + +def validate_args(args): + pass + + +CONTRACTS = [ + { + "url": "https://gitlab.com/cicnet/eth-erc20/-/raw/master/python/giftable_erc20_token/data/GiftableToken", + "name": "Giftable Token", + }, + { + "url": "https://gitlab.com/cicnet/erc20-demurrage-token/-/raw/master/python/erc20_demurrage_token/data/DemurrageTokenMultiCap", + "name": "Demurrage Token Multi Cap (Might not work)", + }, + { + "url": "https://gitlab.com/cicnet/erc20-demurrage-token/-/raw/master/python/erc20_demurrage_token/data/DemurrageTokenMultiNocap", + "name": "Demurrage Token Multi No Cap (Might not work)", + }, + { + "url": "https://gitlab.com/cicnet/erc20-demurrage-token/-/raw/master/python/erc20_demurrage_token/data/DemurrageTokenSingleCap", + "name": "Demurrage Token Single Cap (Might not work)", + }, + { + "url": "https://gitlab.com/cicnet/erc20-demurrage-token/-/raw/master/python/erc20_demurrage_token/data/DemurrageTokenSingleNocap", + "name": "Demurrage Token Single No Cap", + }, +] + +# Download File from Url +def download_file(url: str, directory: str, filename=None) -> (str, bytes): + os.makedirs(directory, exist_ok=True) + filename = filename if filename else url.split("/")[-1] + path = os.path.join(directory, filename) + if not os.path.exists(path): + log.debug(f"Downloading {filename}") + r = requests.get(url, allow_redirects=True) + open(path, "wb").write(r.content) + return path + return path + + +def get_contract_args(data: list): + for item in data: + if item["type"] == "constructor": + return item["inputs"] + raise Exception("No constructor found in contract") + + +def print_contract_args(json_path: str): + json_data = json.load(open(json_path)) + print(f"Contract Args:") + for contract_arg in get_contract_args(json_data): + print( + f"\t{contract_arg.get('name', '')} - {contract_arg.get('type', '')}" + ) + + +def select_contract(): + print(f"Contracts:") + print(f"\t C - Custom (path/url to contract)") + for idx, contract in enumerate(CONTRACTS): + print(f"\t {idx} - {contract['name']}") + + val = input("Select contract (C,0,1..): ") + if val.isdigit() and int(val) < len(CONTRACTS): + contract = CONTRACTS[int(val)] + directory = f"./contracts/{contract['name']}" + bin_path = os.path.abspath(download_file(contract["url"] + ".bin", directory)) + json_path = download_file(contract["url"] + ".json", directory) + elif val == "C": + possible_bin_location = input("Enter path/url to contract: ") + # possible_bin_location is path + if possible_bin_location[0] == "." or possible_bin_location[0] == "/": + if os.path.exists(possible_bin_location): + bin_path = os.path.abspath(possible_bin_location) + else: + raise Exception(f"File {possible_bin_location} does not exist") + + possible_json_path = val.replace(".bin", ".json") + if os.path.exists(possible_json_path): + json_path = possible_json_path + # possible_bin_location is url + else: + bin_path = download_file(contract["url"] + ".bin", directory) + else: + print("Invalid selection") + exit(1) + extra_args = [] + extra_args_types = [] + + if os.path.exists(json_path): + json_data = json.load(open(json_path)) + for contract_arg in get_contract_args(json_data): + arg_name = contract_arg.get("name") + arg_type = contract_arg.get("type") + if arg_name not in ["_decimals", "_name", "_symbol"]: + val = input(f"Enter value for {arg_name} ({arg_type}): ") + extra_args.append(val) + extra_args_types.append(arg_type) + + return { + "bin_path": bin_path, + "json_path": json_path, + "extra_args": extra_args, + "extra_args_types": extra_args_types, + } + + +def init_token( + directory: str, + code="", + extra_args=[], + extra_args_types=[], +): + contract = select_contract() + code = contract["bin_path"] + extra_args = contract["extra_args"] + extra_args_types = contract["extra_args_types"] + + name = input("Enter Token Name (Foo Token): ") or "Foo Token" + symbol = input("Enter Token Symbol (FOO): ") or "FOO" + precision = input("Enter Token Precision (6): ") or 6 + supply = input("Enter Token Supply (0): ") or 0 + + contract_token = Token( + directory, + name=name, + symbol=symbol, + precision=precision, + extra_args=extra_args, + extra_args_types=extra_args_types, + supply=supply, + code=code, + ) + contract_token.start() + return contract_token + + +def init_proof(directory): + description = input("Enter Proof Description (None): ") or None + namespace = input("Enter Proof Namespace (ge): ") or "ge" + issuer = input("Enter Proof Issuer (None): ") or None + contract_proof = Proof(directory, description, namespace, issuer) + contract_proof.start() + return contract_proof + + +def init_meta(directory): + name = input("Enter Name (None): ") or "" + country_code = input("Enter Country Code (KE): ") or "KE" + location = input("Enter Location (None): ") or "" + adding_contact_info = True + contact = {} + while adding_contact_info: + value = input("Enter contact info (e.g 'phone: +254723522718'): ") or None + if value: + data = value.split(":") + if len(data) != 2: + print("Invalid contact info, you must enter in the format 'key: value'") + continue + contact[data[0].strip()] = data[1].strip() + else: + adding_contact_info = False + contract_meta = Meta( + directory, + name=name, + country_code=country_code, + location=location, + contact=contact, + ) + contract_meta.start() + return contract_meta + + +def init_attachment(directory): + contract_attchment = Attachment(directory) + contract_attchment.start() + input( + f"Please add attachment files to '{os.path.abspath(os.path.join(directory,'attachments'))}' and then press ENTER to continue" + ) + contract_attchment.load() + return contract_attchment + + +def init_network( + directory, registry_address,key_account_address, chain_spec, rpc_provider, targets=["eth"] +): + contract_network = Network(directory, targets=targets) + contract_network.start() + + for target in targets: + m = importlib.import_module(f"cic.ext.{target}.start") + m.extension_start( + contract_network, + registry_address=registry_address, + chain_spec=chain_spec, + rpc_provider=rpc_provider, + key_account_address=key_account_address + ) + contract_network.load() + return contract_network + + +def execute(config, eargs): + directory = eargs.path + if os.path.exists(directory): + contine = input( + "Directory already exists, Would you like to delete it? (y/n): " + ) + if contine.lower() != "y": + print("Exiting") + exit(1) + else: + print(f"Deleted {directory}") + os.system(f"rm -rf {directory}") + target = eargs.target + if not eargs.skip_gen: + os.makedirs(directory) + # Defaults + default_contract_registry = config.get( + "CIC_CONTRACT_REGISTRY_ADDRESS", + "0xcf60ebc445b636a5ab787f9e8bc465a2a3ef8299", + ) + default_key_account = config.get( + "CIC_KEY_ACCOUNT_ADDRESS", "eb3907ecad74a0013c259d5874ae7f22dcbcc95c" + ) + default_metadata_endpoint = "http://localhost:63380" or config.get( + "META_ENDPOINT", "http://localhost:63380" + ) + default_wallet_keyfile = config.get( + "WALLET_KEY_FILE", + "/home/will/grassroots/cic-internal-integration/apps/cic-ussd/tests/data/pgp/privatekeys_meta.asc", + ) + default_wallet_passphrase = config.get("WALLET_PASSPHRASE", "merman") + + default_chain_spec = config.get("CHAIN_SPEC", "evm:byzantium:8996:bloxberg") + default_rpc_provider = config.get("RPC_PROVIDER", "http://localhost:63545") + + # Options + contract_registry = ( + input(f"Enter Contract Registry ({default_contract_registry}): ") + or default_contract_registry + ) + rpc_provider = ( + input(f"Enter RPC Provider ({default_rpc_provider}): ") + or default_rpc_provider + ) + chain_spec = ChainSpec.from_chain_str( + (input(f"Enter ChainSpec ({default_chain_spec}): ") or default_chain_spec) + ) + key_account = ( + input(f"Enter KeyAccount ({default_key_account}): ") or default_key_account + ) + metadata_endpoint = ( + input(f"Enter Metadata Endpoint ({default_metadata_endpoint}): ") + or default_metadata_endpoint + ) + + token = init_token(directory) + proof = init_proof(directory) + meta = init_meta(directory) + attachment = init_attachment(directory) + network = init_network( + directory, + registry_address=contract_registry, + key_account_address=key_account, + chain_spec=chain_spec, + rpc_provider=rpc_provider, + targets=[target], + ) + print(f"[cic.header]\nversion = {proof.version()}\n") + print(f"[cic.token]\n{token}") + print(f"[cic.proof]\n{proof}") + print(f"[cic.meta]\n{meta}") + print(f"[cic.attachment]\n{attachment}") + print(f"[cic.network]\n{network}") + if not eargs.skip_deploy: + ready_to_deploy = input("Ready to deploy? (y/n): ") + if ready_to_deploy == "y": + deploy( + config, + contract_directory=directory, + gpg_passphrase=default_wallet_passphrase, + key_file_path=default_wallet_keyfile, + metadata_endpoint=metadata_endpoint, + keystore_directory="/home/will/grassroots/cic-internal-integration/apps/contract-migration/keystore", + target=target, + ) + print("Deployed") + else: + print("Not deploying") + + +# +# +# rpc="http://localhost:63545" + +# python -m cic.runnable.cic_cmd init --target eth --name "$token_name" --symbol $token_symbol --precision 6 $token_symbol_lowercase + +# python -m cic.runnable.cic_cmd ext -p $rpc -i $chain_spec --registry $contract_registry -d $token_symbol_lowercase eth -vv +# python -m cic.runnable.cic_cmd export -p $rpc --metadata-endpoint http://localhost:63380 -vv -y /home/will/grassroots/cic-internal-integration/apps/contract-migration/keystore -o $token_symbol_lowercase/out -d $token_symbol_lowercase eth +if __name__ == "__main__": + execute() diff --git a/cic/cmd/export.py b/cic/cmd/export.py index a873b41..97bd638 100644 --- a/cic/cmd/export.py +++ b/cic/cmd/export.py @@ -23,7 +23,7 @@ from cic.meta import ( from cic.attachment import Attachment from cic.network import Network from cic.token import Token - +from typing import Optional logg = logging.getLogger(__name__) @@ -56,8 +56,9 @@ def init_writers_from_config(config): return w +EArgs = {'target': str, 'directory': str, 'output_directory': str, 'metadata_endpoint': Optional[str], 'y': str} -def execute(config, eargs): +def execute(config, eargs: EArgs): modname = 'cic.ext.{}'.format(eargs.target) cmd_mod = importlib.import_module(modname) @@ -67,7 +68,7 @@ def execute(config, eargs): if eargs.metadata_endpoint != None: MetadataRequestsHandler.base_url = eargs.metadata_endpoint MetadataSigner.gpg_path = os.path.join('/tmp') - MetadataSigner.key_file_path = '/home/lash/src/client/cic/grassrootseconomics/cic-internal-integration/apps/cic-ussd/tests/data/pgp/privatekeys_meta.asc' + MetadataSigner.key_file_path = '/home/will/grassroots/cic-internal-integration/apps/cic-ussd/tests/data/pgp/privatekeys_meta.asc' MetadataSigner.gpg_passphrase = 'merman' writers['proof'] = KeyedWriterFactory(MetadataWriter, HTTPWriter).new writers['attachment'] = KeyedWriterFactory(None, HTTPWriter).new diff --git a/cic/cmd/ext.py b/cic/cmd/ext.py index 789938d..3f02d73 100644 --- a/cic/cmd/ext.py +++ b/cic/cmd/ext.py @@ -25,6 +25,6 @@ def execute(config, eargs): chain_spec = ChainSpec.from_chain_str(eargs.i) - m = importlib.import_module('cic.ext.{}.start'.format(eargs.target)) + m = importlib.import_module(f'cic.ext.{eargs.target}.start') m.extension_start(cn, registry_address=eargs.registry, chain_spec=chain_spec, rpc_provider=config.get('RPC_PROVIDER')) diff --git a/cic/data/config/config.ini b/cic/data/config/config.ini index 9a77060..38530b9 100644 --- a/cic/data/config/config.ini +++ b/cic/data/config/config.ini @@ -3,3 +3,10 @@ meta_writer = cic.output.KVWriter attachment_writer = cic.output.KVWriter proof_writer = cic.output.KVWriter ext_writer = cic.output.KVWriter + +[cic] +contract_registry_address = +key_account_address = + +[meta] +endpoint=https://meta.grassecon.net diff --git a/cic/ext/eth/start.py b/cic/ext/eth/start.py index 1c327ea..9ff00f3 100644 --- a/cic/ext/eth/start.py +++ b/cic/ext/eth/start.py @@ -1,6 +1,6 @@ # external imports -from cic_eth_registry import CICRegistry from chainlib.eth.connection import RPCConnection +from cic_eth_registry import CICRegistry def extension_start(network, *args, **kwargs): @@ -9,18 +9,26 @@ def extension_start(network, *args, **kwargs): :param network: Network object to read and write settings from :type network: cic.network.Network """ - CICRegistry.address = kwargs['registry_address'] + CICRegistry.address = kwargs["registry_address"] - RPCConnection.register_location(kwargs['rpc_provider'], kwargs['chain_spec']) - conn = RPCConnection.connect(kwargs['chain_spec']) + key_account_address = kwargs["key_account_address"] or "" - registry = CICRegistry(kwargs['chain_spec'], conn) + RPCConnection.register_location(kwargs["rpc_provider"], kwargs["chain_spec"]) + conn = RPCConnection.connect(kwargs["chain_spec"]) - address_declarator = registry.by_name('AddressDeclarator') - network.resource_set('eth', 'address_declarator', address_declarator) + registry = CICRegistry(kwargs["chain_spec"], conn) - token_index = registry.by_name('TokenRegistry') - network.resource_set('eth', 'token_index', token_index) + address_declarator = registry.by_name("AddressDeclarator") + network.resource_set( + "eth", "address_declarator", address_declarator, key_account=key_account_address + ) - network.set('eth', kwargs['chain_spec']) + token_index = registry.by_name("TokenRegistry") + network.resource_set( + "eth", "token_index", token_index, key_account=key_account_address + ) + + network.resource_set("eth", "token", None, key_account=key_account_address) + + network.set("eth", kwargs["chain_spec"]) network.save() diff --git a/cic/meta.py b/cic/meta.py index a4bcce5..955ed38 100644 --- a/cic/meta.py +++ b/cic/meta.py @@ -1,21 +1,20 @@ # standard imports -import os +import base64 import json import logging -import base64 +import os # external imports from cic_types import MetadataPointer -from cic_types.processor import generate_metadata_pointer from cic_types.ext.metadata import MetadataRequestsHandler +from cic_types.processor import generate_metadata_pointer from hexathon import strip_0x -# local imports -from .base import ( - Data, - data_dir, - ) from cic.output import OutputWriter + +# local imports +from .base import Data, data_dir + logg = logging.getLogger(__name__) @@ -29,10 +28,12 @@ class Meta(Data): :param writer: Writer interface receiving the output of the processor :type writer: cic.output.OutputWriter """ - def __init__(self, path='.', writer=None): + def __init__(self, path='.', writer=None, name="", location="", country_code="", contact={}): super(Meta, self).__init__() self.name = None - self.contact = {} + self.contact = contact + self.country_code = country_code + self.location = location self.path = path self.writer = writer self.meta_path = os.path.join(self.path, 'meta.json') @@ -49,7 +50,8 @@ class Meta(Data): self.name = o['name'] self.contact = o['contact'] - + self.country_code = o['country_code'] + self.location = o['location'] self.inited = True @@ -63,7 +65,12 @@ class Meta(Data): f = open(meta_template_file_path) o = json.load(f) f.close() - + + o['name'] = self.name + o['contact'] = self.contact + o['country_code'] = self.country_code + o['location'] = self.location + f = open(self.meta_path, 'w') json.dump(o, f, sort_keys=True, indent="\t") f.close() @@ -107,12 +114,15 @@ class Meta(Data): def __str__(self): - s = "contact.name = {}\n".format(self.name) - - for k in self.contact.keys(): - if self.contact[k] == '': + s = f"contact.name = {self.name}\n" + s = f"contact.country_code = {self.country_code}\n" + s = f"contact.location = {self.location}\n" + for contact_key in self.contact.keys(): + contact_value = self.contact[contact_key] + if not contact_value: + s += f"contact.{contact_key} = \n" continue - s += "contact.{} = {}\n".format(k.lower(), self.contact[k]) + s += f"contact.{contact_key} = {contact_value}\n" return s diff --git a/cic/network.py b/cic/network.py index baf0f93..84f63cc 100644 --- a/cic/network.py +++ b/cic/network.py @@ -54,7 +54,7 @@ class Network(Data): """ super(Network, self).load() - network_template_file_path = os.path.join(data_dir, 'network_template_v{}.json'.format(self.version())) + network_template_file_path = os.path.join(data_dir, f'network_template_v{self.version()}.json') f = open(network_template_file_path) o_part = json.load(f) @@ -138,11 +138,11 @@ class Network(Data): def __str__(self): s = '' - for k in self.resources.keys(): - for kk in self.resources[k]['contents'].keys(): - v = self.resources[k]['contents'][kk] - if v == None: - v = '' - s += '{}.{} = {}\n'.format(k, kk, v) + for resource in self.resources.keys(): + for content_key in self.resources[resource]['contents'].keys(): + content_value = self.resources[resource]['contents'][content_key] + if content_value == None: + content_value = '' + s += f'{resource}.{content_key} = {content_value}\n' return s diff --git a/cic/proof.py b/cic/proof.py index bfa16d0..2d43780 100644 --- a/cic/proof.py +++ b/cic/proof.py @@ -33,34 +33,40 @@ class Proof(Data): :type writer: cic.output.OutputWriter """ - def __init__(self, path='.', attachments=None, writer=None): + def __init__( + self, + path=".", + description=None, + namespace="ge", + issuer=None, + attachments=None, + writer=None, + ): super(Proof, self).__init__() self.proofs = [] - self.namespace = 'ge' - self.description = None - self.issuer = None + self.namespace = namespace + self.description = description + self.issuer = issuer self.path = path self.writer = writer self.extra_attachments = attachments self.attachments = {} - self.proof_path = os.path.join(self.path, 'proof.json') + self.proof_path = os.path.join(self.path, "proof.json") self.temp_proof_path = tempfile.mkstemp()[1] - def load(self): - """Load proof data from settings. - """ + """Load proof data from settings.""" super(Proof, self).load() - f = open(self.proof_path, 'r') + f = open(self.proof_path, "r") o = json.load(f) f.close() - self.set_version(o['version']) - self.description = o['description'] - self.namespace = o['namespace'] - self.issuer = o['issuer'] - self.proofs = o['proofs'] + self.set_version(o["version"]) + self.description = o["description"] + self.namespace = o["namespace"] + self.issuer = o["issuer"] + self.proofs = o["proofs"] if self.extra_attachments != None: a = self.extra_attachments.asdict() @@ -72,34 +78,33 @@ class Proof(Data): self.inited = True - def start(self): - """Initialize proof settings from template. - """ + """Initialize proof settings from template.""" super(Proof, self).start() - proof_template_file_path = os.path.join(data_dir, 'proof_template_v{}.json'.format(self.version())) - + proof_template_file_path = os.path.join( + data_dir, "proof_template_v{}.json".format(self.version()) + ) + f = open(proof_template_file_path) o = json.load(f) - f.close() - - f = open(self.proof_path, 'w') + f.close() + o["issuer"] = self.issuer + o["description"] = self.description + o["namespace"] = self.namespace + f = open(self.proof_path, "w") json.dump(o, f, sort_keys=True, indent="\t") f.close() - def asdict(self): - """Output proof state to dict. - """ + """Output proof state to dict.""" return { - 'version': self.version(), - 'namespace': self.namespace, - 'description': self.description, - 'issuer': self.issuer, - 'proofs': self.proofs, - } - + "version": self.version(), + "namespace": self.namespace, + "description": self.description, + "issuer": self.issuer, + "proofs": self.proofs, + } # TODO: the efficiency of this method could probably be improved. def __get_ordered_hashes(self): @@ -108,34 +113,30 @@ class Proof(Data): return ks - -# def get(self): -# hsh = self.hash(b).hex() -# self.attachments[hsh] = self.temp_proof_path -# logg.debug('cbor of {} is {} hashes to {}'.format(v, b.hex(), hsh)) - + # def get(self): + # hsh = self.hash(b).hex() + # self.attachments[hsh] = self.temp_proof_path + # logg.debug('cbor of {} is {} hashes to {}'.format(v, b.hex(), hsh)) def root(self): - """Calculate the root digest from the serialized proof object. - """ - v = self.asdict() - #b = cbor2.dumps(v) + """Calculate the root digest from the serialized proof object.""" + v = self.asdict() + # b = cbor2.dumps(v) b = json.dumps(v) - f = open(self.temp_proof_path, 'w') + f = open(self.temp_proof_path, "w") f.write(b) f.close() - b = b.encode('utf-8') + b = b.encode("utf-8") k = self.hash(b) return (k.hex(), b) - def process(self, token_address=None, token_symbol=None, writer=None): """Serialize and publish proof. - See cic.processor.Processor.process + See cic.processor.Processor.process """ if writer == None: writer = self.writer @@ -144,38 +145,39 @@ class Proof(Data): writer.write(k, v) root_key = k - token_symbol_bytes = token_symbol.encode('utf-8') - k = generate_metadata_pointer(token_symbol_bytes, MetadataPointer.TOKEN_PROOF_SYMBOL) + token_symbol_bytes = token_symbol.encode("utf-8") + k = generate_metadata_pointer( + token_symbol_bytes, MetadataPointer.TOKEN_PROOF_SYMBOL + ) writer.write(k, v) token_address_bytes = bytes.fromhex(strip_0x(token_address)) k = generate_metadata_pointer(token_address_bytes, MetadataPointer.TOKEN_PROOF) writer.write(k, v) -# (hsh, hshs) = self.get() - #hshs = list(map(strip_0x, hshs)) -# hshs_bin = list(map(bytes.fromhex, hshs)) -# hshs_cat = b''.join(hshs_bin) + # (hsh, hshs) = self.get() + # hshs = list(map(strip_0x, hshs)) + # hshs_bin = list(map(bytes.fromhex, hshs)) + # hshs_cat = b''.join(hshs_bin) -# f = open(self.temp_proof_path, 'rb') -# v = f.read() -# f.close() -# writer.write(hsh, v) + # f = open(self.temp_proof_path, 'rb') + # v = f.read() + # f.close() + # writer.write(hsh, v) -# r = self.hash(hshs_cat) -# r_hex = r.hex() + # r = self.hash(hshs_cat) + # r_hex = r.hex() - #logg.debug('generated proof {} for hashes {}'.format(r_hex, hshs)) + # logg.debug('generated proof {} for hashes {}'.format(r_hex, hshs)) - #writer.write(r_hex, hshs_cat) + # writer.write(r_hex, hshs_cat) o = self.asdict() - f = open(self.proof_path, 'w') + f = open(self.proof_path, "w") json.dump(o, f, sort_keys=True, indent="\t") f.close() return root_key - def __str__(self): return "description = {}\n".format(self.description) diff --git a/cic/runnable/cic_cmd.py b/cic/runnable/cic_cmd.py index 3804dbe..18f563e 100644 --- a/cic/runnable/cic_cmd.py +++ b/cic/runnable/cic_cmd.py @@ -11,6 +11,7 @@ import cic.cmd.init as cmd_init import cic.cmd.show as cmd_show import cic.cmd.ext as cmd_ext import cic.cmd.export as cmd_export +import cic.cmd.easy as cmd_easy logging.basicConfig(level=logging.WARNING) logg = logging.getLogger() @@ -34,6 +35,8 @@ cmd_export.process_args(sub_export) sub_ext = sub.add_parser('ext', help='extension helpers') cmd_ext.process_args(sub_ext) +sub_easy = sub.add_parser('easy', help='Easy Mode Contract Deployment') +cmd_easy.process_args(sub_easy) args = argparser.parse_args(sys.argv[1:]) diff --git a/cic/token.py b/cic/token.py index b9387b7..2056ddd 100644 --- a/cic/token.py +++ b/cic/token.py @@ -1,12 +1,9 @@ # standard imports -import os import json +import os # local imports -from .base import ( - Data, - data_dir, - ) +from .base import Data, data_dir class Token(Data): @@ -27,40 +24,49 @@ class Token(Data): :param code: Bytecode for token chain application :type code: str (hex) """ - def __init__(self, path='.', name=None, symbol=None, precision=1, supply=0, code=None): + + def __init__( + self, + path=".", + name=None, + symbol=None, + precision=1, + supply=0, + code=None, + extra_args=[], + extra_args_types=[], + ): super(Token, self).__init__() self.name = name self.symbol = symbol self.supply = supply self.precision = precision self.code = code - self.extra_args: list = [] - self.extra_args_types: list = [] + self.extra_args = extra_args + self.extra_args_types = extra_args_types self.path = path - self.token_path = os.path.join(self.path, 'token.json') - + self.token_path = os.path.join(self.path, "token.json") def load(self): - """Load token data from settings. - """ + """Load token data from settings.""" super(Token, self).load() - f = open(self.token_path, 'r') + f = open(self.token_path, "r") o = json.load(f) f.close() - self.name = o['name'] - self.symbol = o['symbol'] - self.precision = o['precision'] - self.code = o['code'] - self.supply = o['supply'] + self.name = o["name"] + self.symbol = o["symbol"] + self.precision = o["precision"] + self.code = o["code"] + self.supply = o["supply"] extras = [] extra_types = [] - token_extras: list = o['extra'] + token_extras: list = o["extra"] if token_extras: for token_extra in token_extras: - arg = token_extra.get('arg') - arg_type = token_extra.get('arg_type') + arg = token_extra.get("arg") + arg_type = token_extra.get("arg_type") if arg: extras.append(arg) if arg_type: @@ -69,32 +75,40 @@ class Token(Data): self.extra_args_types = extra_types self.inited = True - def start(self): - """Initialize token settings from arguments passed to the constructor and/or template. - """ + """Initialize token settings from arguments passed to the constructor and/or template.""" super(Token, self).load() - token_template_file_path = os.path.join(data_dir, 'token_template_v{}.json'.format(self.version())) - + token_template_file_path = os.path.join( + data_dir, "token_template_v{}.json".format(self.version()) + ) + f = open(token_template_file_path) o = json.load(f) - f.close() + f.close() + o["name"] = self.name + o["symbol"] = self.symbol + o["precision"] = self.precision + o["code"] = self.code + o["supply"] = self.supply + extra = [] + for i in range(len(self.extra_args)): + extra.append( + {"arg": self.extra_args[i], "arg_type": self.extra_args_types[i]} + ) + if len(extra): + o["extra"] = extra + print(extra) - o['name'] = self.name - o['symbol'] = self.symbol - o['precision'] = self.precision - o['code'] = self.code - o['supply'] = self.supply - - f = open(self.token_path, 'w') + f = open(self.token_path, "w") json.dump(o, f, sort_keys=True, indent="\t") f.close() - def __str__(self): s = """name = {} symbol = {} precision = {} -""".format(self.name, self.symbol, self.precision) +""".format( + self.name, self.symbol, self.precision + ) return s diff --git a/requirements.txt b/requirements.txt index 458e323..1cf00c6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,3 +3,4 @@ cic-types~=0.2.1a5 confini~=0.5.1 chainlib~=0.0.13 cbor2==5.4.1 +click==8.0.3 \ No newline at end of file