Compare commits

..

48 Commits

Author SHA1 Message Date
semantic-release
1a42f227c7 0.5.5
Automatically generated by python-semantic-release
2023-03-24 14:52:53 +03:00
3cde79ef8f fix: lock erc20-demurrage-token 2023-03-24 14:52:27 +03:00
semantic-release
ab9f996174 0.5.4
All checks were successful
continuous-integration/drone/push Build is passing
Automatically generated by python-semantic-release
2022-07-05 08:26:41 +00:00
de78753675 fix: pass headers through KeyedWriterFactory
All checks were successful
continuous-integration/drone/push Build is passing
2022-07-05 11:25:02 +03:00
semantic-release
7b7fd1e2bb 0.5.3
All checks were successful
continuous-integration/drone/push Build is passing
Automatically generated by python-semantic-release
2022-07-05 08:14:20 +00:00
4eda0fb5cc fix: add auth headers to HTTPWriter
All checks were successful
continuous-integration/drone/push Build is passing
2022-07-05 11:12:54 +03:00
semantic-release
fa43080602 0.5.2
All checks were successful
continuous-integration/drone/push Build is passing
Automatically generated by python-semantic-release
2022-07-05 07:47:54 +00:00
15ae1143a5 fix: bump cic-types
All checks were successful
continuous-integration/drone/push Build is passing
2022-07-05 10:46:29 +03:00
semantic-release
efbe04df6d 0.5.1
All checks were successful
continuous-integration/drone/push Build is passing
Automatically generated by python-semantic-release
2022-07-05 07:09:24 +00:00
22b3062c49 fix: upgrade cic-types to support meta auth
All checks were successful
continuous-integration/drone/push Build is passing
2022-07-05 10:07:40 +03:00
semantic-release
2f4680e1a7 0.5.0
All checks were successful
continuous-integration/drone/push Build is passing
Automatically generated by python-semantic-release
2022-07-04 10:38:55 +00:00
bfe7086178 feat: add meta-auth (#4)
All checks were successful
continuous-integration/drone/push Build is passing
Co-authored-by: William Luke <williamluke4@gmail.com>
Reviewed-on: #4
Co-authored-by: williamluke <williamluke4@gmail.com>
Co-committed-by: williamluke <williamluke4@gmail.com>
2022-07-04 10:37:40 +00:00
semantic-release
c3e5ee3199 0.4.1
All checks were successful
continuous-integration/drone/push Build is passing
Automatically generated by python-semantic-release
2022-06-14 08:05:32 +00:00
e36ea4bcfb fix: bump deps
All checks were successful
continuous-integration/drone/push Build is passing
2022-06-14 10:50:52 +03:00
9ec3c33718 test: remove sink_account from giftable test
All checks were successful
continuous-integration/drone/push Build is passing
2022-04-29 10:53:12 +03:00
semantic-release
fbd4ed6526 0.4.0
All checks were successful
continuous-integration/drone/push Build is passing
Automatically generated by python-semantic-release
2022-04-29 07:51:06 +00:00
b7acbdc4bc feat: add giftable generation
All checks were successful
continuous-integration/drone/push Build is passing
2022-04-29 10:49:35 +03:00
semantic-release
3361f90ae1 0.3.4
All checks were successful
continuous-integration/drone/push Build is passing
Automatically generated by python-semantic-release
2022-04-27 07:26:57 +00:00
37188a60e8 fix: bump deps again
All checks were successful
continuous-integration/drone/push Build is passing
2022-04-27 10:22:58 +03:00
semantic-release
fb1ebcf8cd 0.3.3
All checks were successful
continuous-integration/drone/push Build is passing
Automatically generated by python-semantic-release
2022-04-26 18:26:26 +00:00
38cfb18527 fix: it's ok if you already exsist
All checks were successful
continuous-integration/drone/push Build is passing
2022-04-26 21:25:02 +03:00
c84517e3db fix: take the reins off
Some checks failed
continuous-integration/drone/push Build is failing
2022-04-26 21:20:35 +03:00
dcea763ce5 fix: bump deps 2022-04-26 17:21:42 +03:00
e55b82f529 fix(attachement): directory not getting created 2022-04-26 17:08:53 +03:00
semantic-release
02df3f792e 0.3.2
All checks were successful
continuous-integration/drone/push Build is passing
Automatically generated by python-semantic-release
2022-04-26 13:30:20 +00:00
d2e55fad0e fix: update deps
All checks were successful
continuous-integration/drone/push Build is passing
2022-04-26 16:27:38 +03:00
semantic-release
edf312d969 0.3.1
All checks were successful
continuous-integration/drone/push Build is passing
Automatically generated by python-semantic-release
2022-04-26 12:03:27 +00:00
5f22220825 fix: throw if directory exsists
All checks were successful
continuous-integration/drone/push Build is passing
2022-04-26 15:01:52 +03:00
semantic-release
067f354905 0.3.0
All checks were successful
continuous-integration/drone/push Build is passing
Automatically generated by python-semantic-release
2022-04-26 11:41:16 +00:00
f30076783d fix: tests
All checks were successful
continuous-integration/drone/push Build is passing
2022-04-26 14:39:49 +03:00
60e8ecc41a fix: bump ci
Some checks failed
continuous-integration/drone/push Build is failing
2022-04-26 14:12:43 +03:00
3c4a86010d Merge pull request 'feat(wizard): add csv input flag' (#3) from feat/csv_input into master
Reviewed-on: #3
2022-04-26 11:07:00 +00:00
a9f97a9a5c feat(wizard): add csv input flag 2022-04-26 14:03:38 +03:00
semantic-release
48522d02e7 0.2.3
All checks were successful
continuous-integration/drone/push Build is passing
Automatically generated by python-semantic-release
2022-03-22 07:25:34 +00:00
92794a2e3b fix: remove this
All checks were successful
continuous-integration/drone/push Build is passing
2022-03-22 10:24:03 +03:00
semantic-release
6f04de8d7e 0.2.2
All checks were successful
continuous-integration/drone/push Build is passing
Automatically generated by python-semantic-release
2022-03-22 07:16:17 +00:00
71bf1e15c4 fix: enfore upper case symbol name
All checks were successful
continuous-integration/drone/push Build is passing
2022-03-22 10:14:36 +03:00
32ba29354a docs: update bange urls
All checks were successful
continuous-integration/drone/push Build is passing
2022-03-16 11:55:13 +03:00
semantic-release
fb818a529c 0.2.1
All checks were successful
continuous-integration/drone/push Build is passing
Automatically generated by python-semantic-release
2022-03-16 08:23:27 +00:00
f7d0503c7b fix: update config paths
All checks were successful
continuous-integration/drone/push Build is passing
2022-03-16 11:21:48 +03:00
semantic-release
a862a73d7c 0.2.0
All checks were successful
continuous-integration/drone/push Build is passing
Automatically generated by python-semantic-release
2022-03-16 08:06:11 +00:00
f4e370cb5d feat: copy base configs to user configs
All checks were successful
continuous-integration/drone/push Build is passing
2022-03-16 11:04:38 +03:00
semantic-release
69bbbd7c8b 0.1.1
All checks were successful
continuous-integration/drone/push Build is passing
Automatically generated by python-semantic-release
2022-03-16 07:57:00 +00:00
b51d1e92d7 fix: update configs
All checks were successful
continuous-integration/drone/push Build is passing
2022-03-16 10:55:25 +03:00
semantic-release
b77f572b82 0.1.0
All checks were successful
continuous-integration/drone/push Build is passing
Automatically generated by python-semantic-release
2022-03-16 06:22:39 +00:00
c832763240 ci: fix semantic release branch
All checks were successful
continuous-integration/drone/push Build is passing
2022-03-16 09:20:55 +03:00
941b9e6b65 docs: add cluter deployment info
Some checks failed
continuous-integration/drone/push Build is failing
2022-03-16 09:18:29 +03:00
0fcf2eb3bc feat: add interactive deployment and switch to poetry' (#2)
Some checks failed
continuous-integration/drone/push Build is failing
Reviewed-on: #2
2022-03-16 05:37:48 +00:00
33 changed files with 2800 additions and 1760 deletions

View File

@ -59,7 +59,7 @@ confidence=
# #
# Kubeflow disables string-interpolation because we are starting to use f # Kubeflow disables string-interpolation because we are starting to use f
# style strings # style strings
disable=import-star-module-level,old-octal-literal,oct-method,print-statement,unpacking-in-except,parameter-unpacking,backtick,old-raise-syntax,old-ne-operator,long-suffix,dict-view-method,dict-iter-method,metaclass-assignment,next-method-called,raising-string,indexing-exception,raw_input-builtin,long-builtin,file-builtin,execfile-builtin,coerce-builtin,cmp-builtin,buffer-builtin,basestring-builtin,apply-builtin,filter-builtin-not-iterating,using-cmp-argument,useless-suppression,range-builtin-not-iterating,suppressed-message,missing-docstring,no-absolute-import,old-division,cmp-method,reload-builtin,zip-builtin-not-iterating,intern-builtin,unichr-builtin,reduce-builtin,standarderror-builtin,unicode-builtin,xrange-builtin,coerce-method,delslice-method,getslice-method,setslice-method,input-builtin,round-builtin,hex-method,nonzero-method,map-builtin-not-iterating,relative-import,invalid-name,bad-continuation,no-member,locally-disabled,fixme,import-error,too-many-locals,no-name-in-module,too-many-instance-attributes,no-self-use,logging-fstring-interpolation disable=old-octal-literal,oct-method,print-statement,unpacking-in-except,parameter-unpacking,backtick,old-raise-syntax,old-ne-operator,long-suffix,dict-view-method,dict-iter-method,metaclass-assignment,next-method-called,raising-string,indexing-exception,raw_input-builtin,long-builtin,file-builtin,execfile-builtin,coerce-builtin,cmp-builtin,buffer-builtin,basestring-builtin,apply-builtin,filter-builtin-not-iterating,using-cmp-argument,useless-suppression,range-builtin-not-iterating,suppressed-message,missing-docstring,no-absolute-import,old-division,cmp-method,reload-builtin,zip-builtin-not-iterating,intern-builtin,unichr-builtin,reduce-builtin,standarderror-builtin,unicode-builtin,xrange-builtin,coerce-method,delslice-method,getslice-method,setslice-method,input-builtin,round-builtin,hex-method,nonzero-method,map-builtin-not-iterating,relative-import,invalid-name,bad-continuation,no-member,locally-disabled,fixme,import-error,too-many-locals,no-name-in-module,too-many-instance-attributes,no-self-use,logging-fstring-interpolation
[REPORTS] [REPORTS]

View File

@ -1,4 +0,0 @@
- 0.0.2
* Add executable entry point in package install
- 0.0.1
* Token creation setup for eth

104
CHANGELOG.md Normal file
View File

@ -0,0 +1,104 @@
# Changelog
<!--next-version-placeholder-->
## v0.5.5 (2023-03-24)
### Fix
* Lock erc20-demurrage-token ([`3cde79e`](https://git.grassecon.net/cicnet/cic-cli/commit/3cde79ef8fb77a6b03454e675568834e0ab4ba80))
## v0.5.4 (2022-07-05)
### Fix
* Pass headers through KeyedWriterFactory ([`de78753`](https://git.grassecon.net/cicnet/cic-cli/commit/de78753675242dd253359a5a5601d9062d81f0ee))
## v0.5.3 (2022-07-05)
### Fix
* Add auth headers to HTTPWriter ([`4eda0fb`](https://git.grassecon.net/cicnet/cic-cli/commit/4eda0fb5cc2c41a735619dc3e34f21c4e27fd112))
## v0.5.2 (2022-07-05)
### Fix
* Bump cic-types ([`15ae114`](https://git.grassecon.net/cicnet/cic-cli/commit/15ae1143a5230078219072d096741546ebcc3d07))
## v0.5.1 (2022-07-05)
### Fix
* Upgrade cic-types to support meta auth ([`22b3062`](https://git.grassecon.net/cicnet/cic-cli/commit/22b3062c4909400664bd2a50ca36d5ee737531a1))
## v0.5.0 (2022-07-04)
### Feature
* Add meta-auth ([#4](https://git.grassecon.net/cicnet/cic-cli/issues/4)) ([`bfe7086`](https://git.grassecon.net/cicnet/cic-cli/commit/bfe7086178f3fc2743dd68cc20c5459ca466ae8e))
## v0.4.1 (2022-06-14)
### Fix
* Bump deps ([`e36ea4b`](https://git.grassecon.net/cicnet/cic-cli/commit/e36ea4bcfb1c417d1adf2be9455cb20b23323414))
## v0.4.0 (2022-04-29)
### Feature
* Add giftable generation ([`b7acbdc`](https://git.grassecon.net/cicnet/cic-cli/commit/b7acbdc4bc5862752585fecfaee7d2fe70d8dbbe))
## v0.3.4 (2022-04-27)
### Fix
* Bump deps again ([`37188a6`](https://git.grassecon.net/cicnet/cic-cli/commit/37188a60e85d9545acfd950c1c160801c22d2b5b))
## v0.3.3 (2022-04-26)
### Fix
* It's ok if you already exsist ([`38cfb18`](https://git.grassecon.net/cicnet/cic-cli/commit/38cfb185270fb361ff5d9da9976745e1fecc40f8))
* Take the reins off ([`c84517e`](https://git.grassecon.net/cicnet/cic-cli/commit/c84517e3db264f541e6e5a8eef30703bf28d32d0))
* Bump deps ([`dcea763`](https://git.grassecon.net/cicnet/cic-cli/commit/dcea763ce5b3d542ed0a50586720fc3a45142e77))
* **attachement:** Directory not getting created ([`e55b82f`](https://git.grassecon.net/cicnet/cic-cli/commit/e55b82f5295397b3e4123297bc6b231ca251bc83))
## v0.3.2 (2022-04-26)
### Fix
* Update deps ([`d2e55fa`](https://git.grassecon.net/cicnet/cic-cli/commit/d2e55fad0efd13fa7a1de8ed8ab43e703a4aa046))
## v0.3.1 (2022-04-26)
### Fix
* Throw if directory exsists ([`5f22220`](https://git.grassecon.net/cicnet/cic-cli/commit/5f22220825f5c485550ca9a21a54598fbe3b3ba3))
## v0.3.0 (2022-04-26)
### Feature
* **wizard:** Add csv input flag ([`a9f97a9`](https://git.grassecon.net/cicnet/cic-cli/commit/a9f97a9a5c6908e4d51710e3b121764d2511c0ab))
### Fix
* Tests ([`f300767`](https://git.grassecon.net/cicnet/cic-cli/commit/f30076783d5fc93d91d29e9343d62af4c0fdffaa))
* Bump ci ([`60e8ecc`](https://git.grassecon.net/cicnet/cic-cli/commit/60e8ecc41a472dbea25c36d869259c8161145002))
## v0.2.3 (2022-03-22)
### Fix
* Remove this ([`92794a2`](https://git.grassecon.net/cicnet/cic-cli/commit/92794a2e3b2fc5ace63f519bbe5b23c542afc853))
## v0.2.2 (2022-03-22)
### Fix
* Enfore upper case symbol name ([`71bf1e1`](https://git.grassecon.net/cicnet/cic-cli/commit/71bf1e15c4a217111ae6f6568814985a9d5b960f))
### Documentation
* Update bange urls ([`32ba293`](https://git.grassecon.net/cicnet/cic-cli/commit/32ba29354ae53bf8166bef4d117667aa314a6cfe))
## v0.2.1 (2022-03-16)
### Fix
* Update config paths ([`f7d0503`](https://git.grassecon.net/cicnet/cic-cli/commit/f7d0503c7b85d96588bf1a75fdf1cce27acf1460))
## v0.2.0 (2022-03-16)
### Feature
* Copy base configs to user configs ([`f4e370c`](https://git.grassecon.net/cicnet/cic-cli/commit/f4e370cb5db79c74abe26179f5b15bd079bdd066))
## v0.1.1 (2022-03-16)
### Fix
* Update configs ([`b51d1e9`](https://git.grassecon.net/cicnet/cic-cli/commit/b51d1e92d7ae1e3b91ca50c036ffd58e762df24b))
## v0.1.0 (2022-03-16)
### Feature
* Add interactive deployment and switch to poetry' ([#2](https://git.grassecon.net/cicnet/cic-cli/issues/2)) ([`0fcf2eb`](https://git.grassecon.net/cicnet/cic-cli/commit/0fcf2eb3bc807111db02e9e47e469ec0a965797f))
* **wizard:** Add ability to select wallet address ([`556366a`](https://git.grassecon.net/cicnet/cic-cli/commit/556366a93384bba51aa617d54bcf50f4473b790a))
* Add token symbol proof metadata references ([`a707f12`](https://git.grassecon.net/cicnet/cic-cli/commit/a707f120865186c8e4a7840d53c9dcf5f4257ab3))
### Fix
* Add getpass ([`47a9b25`](https://git.grassecon.net/cicnet/cic-cli/commit/47a9b259ae54c34df9af4aa1fb176070d305296a))
* Incorrect var name ([`41dbd5a`](https://git.grassecon.net/cicnet/cic-cli/commit/41dbd5a400287d4687d0830017466b9a43054ecf))
* **ext:** Allow loading chain_spec from config ([`1d4b051`](https://git.grassecon.net/cicnet/cic-cli/commit/1d4b0512ad65b4d2903bd7d022e562cda158a592))
* Change name to cic-cli ([`40e386d`](https://git.grassecon.net/cicnet/cic-cli/commit/40e386db1175839394f2480a1a3e1bbfc52edea9))
* Add missing json import ([`48ee805`](https://git.grassecon.net/cicnet/cic-cli/commit/48ee8050c17edb21b0dc4065bf0018b1502d4a8c))
* Broken imports ([`4f219e3`](https://git.grassecon.net/cicnet/cic-cli/commit/4f219e3d1853befa197f46a19dc8a8a76ef26811))
### Documentation
* Add cluter deployment info ([`941b9e6`](https://git.grassecon.net/cicnet/cic-cli/commit/941b9e6b650163c4f35e4b08203fb10c9309ee91))
* Rename prod config to testnet ([`67f947a`](https://git.grassecon.net/cicnet/cic-cli/commit/67f947a9af16dc01fb68459a51629320264d281f))
* Add badge ([`be5d988`](https://git.grassecon.net/cicnet/cic-cli/commit/be5d988fa4d03dfcd44f71c7c6d4a562b780da09))

View File

@ -1,28 +1,23 @@
# CIC Token Deployment Tool # CIC Token Deployment Tool
[![Status](https://ci.grassecon.net/api/badges/grassrootseconomics/cic/status.svg?ref=refs/heads/master)](https://ci.grassecon.net/grassrootseconomics/cic) [![Status](https://ci.grassecon.net/api/badges/cicnet/cic-cli/status.svg)](https://ci.grassecon.net/grassrootseconomics/cic)
[![Version](https://img.shields.io/pypi/v/cic?color=green)](https://pypi.org/project/cic/) [![Version](https://img.shields.io/pypi/v/cic-cli?color=green)](https://pypi.org/project/cic/)
CIC-CLI provides tooling to generate and publish metadata in relation to CIC-CLI provides tooling to generate and publish metadata in relation to
token deployments. token deployments.
```shell ```shell
pip install --extra-index-url https://pip.grassrootseconomics.net cic[eth] pip install cic-cli[eth]
``` ```
## Setup
### Importing a wallet from metamask
- Export the accounts private key [Instructions](https://metamask.zendesk.com/hc/en-us/articles/360015289632-How-to-Export-an-Account-Private-Key)
- Save the private key to a file
- Run `eth-keyfile -k <file> > ~/.config/cic/keystore/keyfile.json`
## Usage ## Usage
### Using the wizard ### Using the wizard
First make sure that you edit the configs below to add your paths for `[auth]keyfile_path` and `[wallet]keyfile`
The configs are located in `~/.config/cic/cli/config/`
``` ```
# Local # Local
cic wizard ./somewhere -c ./config/docker cic wizard ./somewhere -c ~/.config/cic/cli/config/docker
# Test Net # Test Net
cic wizard ./somewhere -c ./config/testnet cic wizard ./somewhere -c ~/.config/cic/cli/config/testnet
``` ```
### Modular ### Modular
Some of the concepts described below assume familiarity with base Some of the concepts described below assume familiarity with base
@ -52,7 +47,8 @@ below are limited to the context of the EVM.
## Development ## Development
### Requirements ### Requirements
- [poetry](https://python-poetry.org/docs/#installation) - Install [poetry](https://python-poetry.org/docs/#installation)
### Setup ### Setup
``` ```
@ -68,7 +64,36 @@ below are limited to the context of the EVM.
```bash ```bash
poetry run cic wizard ./somewhere -c ./config/docker poetry run cic wizard ./somewhere -c ./config/docker
``` ```
### Importing a wallet from metamask
- Export the accounts private key [Instructions](https://metamask.zendesk.com/hc/en-us/articles/360015289632-How-to-Export-an-Account-Private-Key)
- Save the private key to a file
- Run `eth-keyfile -k <file> > ~/.config/cic/keystore/keyfile.json`
### Port Forwarding
<details>
<summary>Install Kubectl</summary>
```bash
sudo apt-get update
sudo apt-get install -y apt-transport-https ca-certificates curl
sudo curl -fsSLo /usr/share/keyrings/kubernetes-archive-keyring.gpg https://packages.cloud.google.com/apt/doc/apt-key.gpg
echo "deb [signed-by=/usr/share/keyrings/kubernetes-archive-keyring.gpg] https://apt.kubernetes.io/ kubernetes-xenial main" | sudo tee /etc/apt/sources.list.d/kubernetes.list
sudo apt-get update
sudo apt-get install -y kubectl
```
</details>
- Download testnet cluster config from https://cloud.digitalocean.com/kubernetes/clusters
- Move the config to `$HOME/.kube/`
- Run `kubectl -n grassroots --kubeconfig=$HOME/.kube/<config_file_name>.yaml get pods`
- Copy the name of the meta pod (e.g `cic-meta-server-67dc7c6468-8rhdq`)
- Port foward the meta pod to the local machine using `kubectl port-forward pods/<name_of_meta_pod> 6700:8000 -n grassroots --kubeconfig=$HOME/.kube/<config_file_name>.yaml`
- Clone this repository to your local machine
- Run `poetry install -E eth` in the repo root
- Open `./cic/config/testnet/config.ini` and change
- [auth]keyfile_path
- [wallet]key_file
- Open a new terminal and run `poetry run cic wizard -c ./cic/config/testnet ./somewhere`
### Tests ### Tests
``` ```

View File

@ -1 +1 @@
__version__ = "0.0.2" __version__ = "0.5.5"

View File

@ -2,11 +2,13 @@ from __future__ import annotations
# standard import # standard import
import logging import logging
import os
from chainlib.cli.config import Config from chainlib.cli.config import Config
# local imports # local imports
from cic.contract.contract import deploy_contract, generate_contract, load_contract from cic.contract.contract import deploy_contract, generate_contract, load_contract
from cic.contract.csv import load_contract_from_csv
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -20,6 +22,10 @@ def process_args(argparser):
action="store_true", action="store_true",
help="Skip Deployment", help="Skip Deployment",
) )
argparser.add_argument(
"--csv",
help="Load Voucher from CSV",
)
argparser.add_argument( argparser.add_argument(
"--target", "--target",
default="eth", default="eth",
@ -46,22 +52,31 @@ def validate_args(_args):
pass pass
ExtraArgs = {"skip_gen": str, "skip_deploy": str, "target": str, "path": str, "p": str} def execute(
config: Config,
eargs,
def execute(config: Config, eargs: ExtraArgs): ):
directory = eargs.path directory = eargs.path
target = eargs.target target = eargs.target
skip_gen = eargs.skip_gen skip_gen = eargs.skip_gen
skip_deploy = eargs.skip_deploy skip_deploy = eargs.skip_deploy
wallet_keystore = eargs.y wallet_keystore = eargs.y
csv_file = eargs.csv
if wallet_keystore: if wallet_keystore:
config.add(wallet_keystore, "WALLET_KEY_FILE", exists_ok=True) config.add(wallet_keystore, "WALLET_KEY_FILE", exists_ok=True)
if not skip_gen: if skip_gen:
contract = generate_contract(directory, [target], config, interactive=True)
else:
contract = load_contract(directory) contract = load_contract(directory)
else:
if os.path.exists(directory):
raise Exception(f"Directory {directory} already exists")
if csv_file:
print(f"Generating from csv:{csv_file} to {directory}")
contract = load_contract_from_csv(config, directory, csv_file)
else:
print("Using Interactive Mode")
contract = generate_contract(directory, [target], config, interactive=True)
print(contract) print(contract)

15
cic/config.py Normal file
View File

@ -0,0 +1,15 @@
import os
import shutil
default_module_configs = os.path.join(os.path.dirname(os.path.realpath(__file__)), '.', 'configs')
def ensure_base_configs(config_dir: str):
"""
Ensure that the base configs are present.
"""
if not os.path.exists(config_dir):
os.makedirs(config_dir)
for f in os.listdir(default_module_configs):
if not os.path.exists(os.path.join(config_dir, f)):
shutil.copytree(os.path.join(default_module_configs, f), os.path.join(config_dir, f))

View File

@ -10,7 +10,7 @@ registry_address = 0xcf60ebc445b636a5ab787f9e8bc465a2a3ef8299
[meta] [meta]
url = http://localhost:63380 url = http://localhost:63380
http_origin = http_origin =
auth_token =
[rpc] [rpc]
provider = http://localhost:63545 provider = http://localhost:63545

View File

@ -10,6 +10,7 @@ registry_address = 0xcf60ebc445b636a5ab787f9e8bc465a2a3ef8299
[meta] [meta]
url = http://localhost:8000 url = http://localhost:8000
http_origin = http_origin =
auth_token =
[rpc] [rpc]
provider = http://localhost:8545 provider = http://localhost:8545

View File

@ -0,0 +1,28 @@
[cic_core]
meta_writer = cic.writers.KVWriter
attachment_writer = cic.writers.KVWriter
proof_writer = cic.writers.KVWriter
ext_writer = cic.writers.KVWriter
[cic]
registry_address = 0xe3e3431BF25b06166513019Ed7B21598D27d05dC
[meta]
url = https://meta.sarafu.network
http_origin =
auth_token =
[rpc]
provider = https://rpc.sarafu.network
[auth]
type = gnupg
keyfile_path =
passphrase =
[wallet]
key_file =
passphrase =
[chain]
spec = evm:kitabu:6060:sarafu

View File

@ -0,0 +1,27 @@
[cic_core]
meta_writer = cic.writers.KVWriter
attachment_writer = cic.writers.KVWriter
proof_writer = cic.writers.KVWriter
ext_writer = cic.writers.KVWriter
[cic]
registry_address = 0x47269C43e4aCcA5CFd09CB4778553B2F69963303
[meta]
url = https://meta.sarafu.network
http_origin =
auth_token =
[rpc]
provider = https://rpc.sarafu.network
[auth]
type = gnupg
keyfile_path =
passphrase =
[wallet]
key_file =
passphrase =
[chain]
spec = evm:kitabu:6060:sarafu

View File

@ -23,8 +23,8 @@ class Attachment(Data):
self.path = path self.path = path
self.writer = writer self.writer = writer
self.attachment_path = os.path.join(self.path, "attachments") self.attachment_path = os.path.join(self.path, "attachments")
if interactive:
self.start() self.start()
if interactive:
input( input(
f"Please add attachment files to '{os.path.abspath(os.path.join(self.path,'attachments'))}' and then press ENTER to continue" f"Please add attachment files to '{os.path.abspath(os.path.join(self.path,'attachments'))}' and then press ENTER to continue"
) )
@ -45,7 +45,7 @@ class Attachment(Data):
def start(self): def start(self):
"""Initialize attachment settings from template.""" """Initialize attachment settings from template."""
super(Attachment, self).start() super(Attachment, self).start()
os.makedirs(self.attachment_path) os.makedirs(self.attachment_path, exist_ok=True)
def get(self, k): def get(self, k):
"""Get a single attachment by the sha256 hash of the content. """Get a single attachment by the sha256 hash of the content.

View File

@ -14,6 +14,7 @@ from cic.contract.base import Data, data_dir
from cic.utils import object_to_str from cic.utils import object_to_str
logg = logging.getLogger(__name__) logg = logging.getLogger(__name__)
@ -22,7 +23,9 @@ class Proof(Data):
It processes inputs from the proof.json file in the session directory. It processes inputs from the proof.json file in the session directory.
Optionally, attachment objects can be added to the proof. If added, the resulting proof digest will consists of the attachment digests added to the root digest. These are then are deterministically ordered, regardless of which order attachments were given to the constructor. Optionally, attachment objects can be added to the proof. If added, the resulting proof
digest will consists of the attachment digests added to the root digest. These are then
are deterministically ordered, regardless of which order attachments were given to the constructor.
:param path: Path to settings directory :param path: Path to settings directory
:type path: str :type path: str
@ -56,7 +59,8 @@ class Proof(Data):
if interactive: if interactive:
self.description = ( self.description = (
input(f"Enter Proof Description ({self.description}): ") or self.description input(f"Enter Proof Description ({self.description}): ")
or self.description
) )
self.namespace = ( self.namespace = (
input(f"Enter Proof Namespace ({self.namespace}): ") or self.namespace input(f"Enter Proof Namespace ({self.namespace}): ") or self.namespace
@ -67,7 +71,7 @@ class Proof(Data):
"""Load proof data from settings.""" """Load proof data from settings."""
super(Proof, self).load() super(Proof, self).load()
f = open(self.proof_path, "r") f = open(self.proof_path, "r", encoding="utf-8")
o = json.load(f) o = json.load(f)
f.close() f.close()
@ -77,7 +81,7 @@ class Proof(Data):
self.issuer = o["issuer"] self.issuer = o["issuer"]
self.proofs = o["proofs"] self.proofs = o["proofs"]
if self.extra_attachments != None: if self.extra_attachments is not None:
a = self.extra_attachments.asdict() a = self.extra_attachments.asdict()
for k in a.keys(): for k in a.keys():
self.attachments[k] = a[k] self.attachments[k] = a[k]

View File

@ -40,7 +40,7 @@ class Token(Data):
): ):
super(Token, self).__init__() super(Token, self).__init__()
self.name = name self.name = name
self.symbol = symbol self.symbol = symbol.upper()
self.supply = supply self.supply = supply
self.precision = precision self.precision = precision
self.code = code self.code = code
@ -57,6 +57,7 @@ class Token(Data):
self.name = input(f"Enter Token Name ({self.name}): ") or self.name self.name = input(f"Enter Token Name ({self.name}): ") or self.name
self.symbol = input(f"Enter Token Symbol ({self.symbol}): ") or self.symbol self.symbol = input(f"Enter Token Symbol ({self.symbol}): ") or self.symbol
self.symbol = self.symbol.upper()
self.precision = input(f"Enter Token Precision ({self.precision}): ") or self.precision self.precision = input(f"Enter Token Precision ({self.precision}): ") or self.precision
self.supply = input(f"Enter Token Supply ({self.supply}): ") or self.supply self.supply = input(f"Enter Token Supply ({self.supply}): ") or self.supply
@ -68,7 +69,7 @@ class Token(Data):
o = json.load(f) o = json.load(f)
self.name = o["name"] self.name = o["name"]
self.symbol = o["symbol"] self.symbol = o["symbol"].upper()
self.precision = o["precision"] self.precision = o["precision"]
self.code = o["code"] self.code = o["code"]
self.supply = o["supply"] self.supply = o["supply"]
@ -100,7 +101,7 @@ class Token(Data):
with open(token_template_file_path, encoding="utf-8") as f: with open(token_template_file_path, encoding="utf-8") as f:
o = json.load(f) o = json.load(f)
o["name"] = self.name o["name"] = self.name
o["symbol"] = self.symbol o["symbol"] = self.symbol.upper()
o["precision"] = self.precision o["precision"] = self.precision
o["code"] = self.code o["code"] = self.code
o["supply"] = self.supply o["supply"] = self.supply
@ -115,7 +116,7 @@ class Token(Data):
def __str__(self): def __str__(self):
s = f"name = {self.name}\n" s = f"name = {self.name}\n"
s += f"symbol = {self.symbol}\n" s += f"symbol = {self.symbol.upper()}\n"
s += f"precision = {self.precision}\n" s += f"precision = {self.precision}\n"
s += f"supply = {self.supply}\n" s += f"supply = {self.supply}\n"
for idx, extra in enumerate(self.extra_args): for idx, extra in enumerate(self.extra_args):

13
cic/contract/constants.py Normal file
View File

@ -0,0 +1,13 @@
GITABLE_CONTRACT_URL = "https://gitlab.com/cicnet/eth-erc20/-/raw/master/python/giftable_erc20_token/data/GiftableToken"
DMR_CONTRACT_URL = "https://gitlab.com/cicnet/erc20-demurrage-token/-/raw/v0.1.1/python/erc20_demurrage_token/data/DemurrageTokenSingleNocap"
CONTRACT_URLS = [
{
"url": GITABLE_CONTRACT_URL,
"name": "Giftable Token",
},
{
"url": DMR_CONTRACT_URL,
"name": "Demurrage Token Single No Cap",
},
]

View File

@ -1,25 +1,22 @@
# Standard # Standard
import importlib import importlib
import json
import logging import logging
import os import os
from typing import TYPE_CHECKING, List from typing import List
import requests # External imports
from chainlib.chain import ChainSpec from chainlib.chain import ChainSpec
from chainlib.cli.config import Config from chainlib.cli.config import Config
# Local Modules
from cic.contract.components.attachment import Attachment from cic.contract.components.attachment import Attachment
from cic.contract.components.meta import Meta from cic.contract.components.meta import Meta
from cic.contract.components.proof import Proof from cic.contract.components.proof import Proof
from cic.contract.components.token import Token from cic.contract.components.token import Token
from cic.contract.helpers import init_writers_from_config from cic.contract.helpers import init_writers_from_config
from cic.contract.network import Network from cic.contract.network import Network
# Local Modules
from cic.contract.processor import ContractProcessor from cic.contract.processor import ContractProcessor
from cic.writers import HTTPWriter, KeyedWriterFactory, MetadataWriter, OutputWriter from cic.writers import HTTPWriter, KeyedWriterFactory, MetadataWriter
# external imports
from cic_types.ext.metadata import MetadataRequestsHandler from cic_types.ext.metadata import MetadataRequestsHandler
from cic_types.ext.metadata.signer import Signer as MetadataSigner from cic_types.ext.metadata.signer import Signer as MetadataSigner
@ -74,37 +71,27 @@ def load_contract(directory) -> Contract:
def generate_contract( def generate_contract(
directory: str, targets: List[str], config, interactive=True directory: str, targets: List[str], config, interactive=True
) -> Contract: ) -> Contract:
if os.path.exists(directory):
contine = input(
f"Directory {directory} already exists, Would you like to delete it? (y/n): "
)
if contine.lower() != "y":
log.debug("Trying to load existing contract")
return load_contract(directory)
else:
print(f"Deleted {directory}")
os.system(f"rm -rf {directory}")
os.makedirs(directory) os.makedirs(directory)
log.debug("Generating token") log.info("Generating token")
token = Token(directory, interactive=interactive) token = Token(directory, interactive=interactive)
token.start() token.start()
log.debug("Generating proof") log.info("Generating proof")
proof = Proof(directory, interactive=interactive) proof = Proof(directory, interactive=interactive)
proof.start() proof.start()
log.debug("Generating meta") log.info("Generating meta")
meta = Meta(directory, interactive=interactive) meta = Meta(directory, interactive=interactive)
meta.start() meta.start()
log.debug("Generating attachment") log.info("Generating attachment")
attachment = Attachment(directory, interactive=interactive) attachment = Attachment(directory, interactive=interactive)
log.debug("Generating network") log.info("Generating network")
network = Network(directory, targets=targets) network = Network(directory, targets=targets)
network.start() network.start()
log.debug( log.info(
f"""Populating infomation from network: f"""Populating infomation from network:
CIC_REGISTRY_ADDRESS: {config.get("CIC_REGISTRY_ADDRESS")} CIC_REGISTRY_ADDRESS: {config.get("CIC_REGISTRY_ADDRESS")}
CHAIN_SPEC: {config.get("CHAIN_SPEC")} CHAIN_SPEC: {config.get("CHAIN_SPEC")}
@ -118,7 +105,7 @@ def generate_contract(
signer_hint = config.get("WALLET_KEY_FILE") signer_hint = config.get("WALLET_KEY_FILE")
keys = cmd_mod.list_keys(config, signer_hint) keys = cmd_mod.list_keys(config, signer_hint)
if len(keys) > 1: if len(keys) > 1:
print(f"More than one key found, please select one:") print("More than one key found, please select one:")
for idx, key in enumerate(keys): for idx, key in enumerate(keys):
print(f"{idx} - {key} ") print(f"{idx} - {key} ")
selecting_key = True selecting_key = True
@ -152,7 +139,6 @@ def deploy_contract(
target: str, target: str,
contract_directory: str, contract_directory: str,
): ):
modname = f"cic.ext.{target}" modname = f"cic.ext.{target}"
cmd_mod = importlib.import_module(modname) cmd_mod = importlib.import_module(modname)
@ -161,9 +147,12 @@ def deploy_contract(
output_writer_path_meta = output_directory output_writer_path_meta = output_directory
metadata_endpoint = config.get("META_URL") metadata_endpoint = config.get("META_URL")
metadata_auth_token = config.get("META_AUTH_TOKEN")
headers = {"Authorization": f"Basic {metadata_auth_token}"}
if metadata_endpoint is not None: if metadata_endpoint is not None:
MetadataRequestsHandler.base_url = metadata_endpoint MetadataRequestsHandler.base_url = metadata_endpoint
MetadataRequestsHandler.auth_token = metadata_auth_token
MetadataSigner.gpg_path = "/tmp" MetadataSigner.gpg_path = "/tmp"
MetadataSigner.key_file_path = config.get("AUTH_KEYFILE_PATH") MetadataSigner.key_file_path = config.get("AUTH_KEYFILE_PATH")
MetadataSigner.gpg_passphrase = config.get("AUTH_PASSPHRASE") MetadataSigner.gpg_passphrase = config.get("AUTH_PASSPHRASE")
@ -178,12 +167,12 @@ def deploy_contract(
) )
ca = Attachment( ca = Attachment(
path=contract_directory, path=contract_directory,
writer=writers["attachment"](path=output_writer_path_meta), writer=writers["attachment"](path=output_writer_path_meta, headers=headers),
) )
cp = Proof( cp = Proof(
path=contract_directory, path=contract_directory,
attachments=ca, attachments=ca,
writer=writers["proof"](path=output_writer_path_meta), writer=writers["proof"](path=output_writer_path_meta, headers=headers),
) )
cn = Network(path=contract_directory) cn = Network(path=contract_directory)

212
cic/contract/csv.py Normal file
View File

@ -0,0 +1,212 @@
import csv
import importlib
import logging
import os
from enum import IntEnum
from pathlib import Path
from typing import List
from chainlib.chain import ChainSpec
from cic.contract.components.attachment import Attachment
from cic.contract.components.meta import Meta
from cic.contract.components.proof import Proof
from cic.contract.components.token import Token
from cic.contract.constants import DMR_CONTRACT_URL, GITABLE_CONTRACT_URL
from cic.contract.contract import Contract
from cic.contract.helpers import download_file
from cic.contract.network import Network
log = logging.getLogger(__name__)
CONTRACT_CSV_HEADER = [
"issuer",
"namespace",
"voucher_name",
"symbol",
"location",
"country_code",
"supply",
"precision",
"token_type",
"demurrage",
"period_minutes",
"phone_number",
"email_address",
"sink_account",
"description",
]
class CSV_Column(IntEnum):
issuer = 0
namespace = 1
voucher_name = 2
symbol = 3
location = 4
country_code = 5
supply = 6
precision = 7
token_type = 8
demurrage = 9
period_minutes = 10
phone_number = 11
email_address = 12
sink_account = 13
description = 14
def load_contracts_from_csv(config, directory, csv_path: str) -> List[Contract]:
targets = ["eth"]
os.makedirs(directory)
contract_rows = []
with open(csv_path, "rt", encoding="utf-8") as file:
csvreader = csv.reader(file, delimiter=",")
for idx, row in enumerate(csvreader):
if idx == 0:
if row != CONTRACT_CSV_HEADER:
raise Exception(
f'Seems you are using the wrong csv format. Expected the header to be: \n\t {", ".join(CONTRACT_CSV_HEADER)}'
)
continue
contract_rows.append(row)
contracts = []
for idx, contract_row in enumerate(contract_rows):
issuer = contract_row[CSV_Column.issuer]
namespace = contract_row[CSV_Column.namespace]
voucher_name = contract_row[CSV_Column.voucher_name]
symbol = contract_row[CSV_Column.symbol]
location = contract_row[CSV_Column.location]
country_code = contract_row[CSV_Column.country_code]
supply = contract_row[CSV_Column.supply]
precision = contract_row[CSV_Column.precision]
token_type = contract_row[CSV_Column.token_type]
demurrage = contract_row[CSV_Column.demurrage]
period_minutes = contract_row[CSV_Column.period_minutes]
phone_number = contract_row[CSV_Column.phone_number]
email_address = contract_row[CSV_Column.email_address]
sink_account = contract_row[CSV_Column.sink_account]
description = contract_row[CSV_Column.description]
if token_type == "demurrage":
bin_path = os.path.abspath(download_file(DMR_CONTRACT_URL + ".bin"))
log.info(f"Generating {token_type} contract for {issuer}")
token = Token(
directory,
name=voucher_name,
symbol=symbol,
precision=precision,
supply=supply,
extra_args=[demurrage, period_minutes, sink_account],
extra_args_types=["uint256", "uint256", "address"],
code=bin_path,
)
elif token_type == "giftable":
bin_path = os.path.abspath(download_file(GITABLE_CONTRACT_URL + ".bin"))
token = Token(
directory,
name=voucher_name,
symbol=symbol,
precision=precision,
supply=supply,
extra_args=[],
extra_args_types=[],
code=bin_path,
)
else:
raise Exception(
f"Only demurrage and gitable contracts currently supported at this time. {token_type} is not supported"
)
if token is None:
raise Exception(f"There was an issue building the contract")
token.start()
log.info("Generating proof")
proof = Proof(
directory,
attachments=None,
issuer=issuer,
description=description,
namespace=namespace,
)
proof.start()
log.info("Generating meta")
meta = Meta(
directory,
name=issuer,
contact={
"phone": phone_number,
"email": email_address,
},
country_code=country_code,
location=location,
)
meta.start()
log.info("Generating attachment")
attachment = Attachment(directory)
log.info("Generating network")
network = Network(directory, targets=targets)
network.start()
log.info(
f"""Populating infomation from network:
CIC_REGISTRY_ADDRESS: {config.get("CIC_REGISTRY_ADDRESS")}
CHAIN_SPEC: {config.get("CHAIN_SPEC")}
RPC_PROVIDER: {config.get("RPC_PROVIDER")}
"""
)
for target in targets:
# TODO Clean this up
modname = f"cic.ext.{target}"
cmd_mod = importlib.import_module(modname)
signer_hint = config.get("WALLET_KEY_FILE")
if signer_hint is None:
raise Exception("No Wallet Keyfile was provided")
keys = cmd_mod.list_keys(config, signer_hint)
if keys is None or len(keys) == 0:
raise Exception(f"No wallet keys found in {signer_hint}")
if len(keys) > 1:
log.warning(
f"More than one key found in the keystore. Using the first one\n - {keys[0]}"
)
key_account_address = keys[0]
m = importlib.import_module(f"cic.ext.{target}.start")
m.extension_start(
network,
registry_address=config.get("CIC_REGISTRY_ADDRESS"),
chain_spec=ChainSpec.from_chain_str(config.get("CHAIN_SPEC")),
rpc_provider=config.get("RPC_PROVIDER"),
key_account_address=key_account_address,
)
network.load()
contracts.append(
Contract(
token=token,
proof=proof,
meta=meta,
attachment=attachment,
network=network,
)
)
return contracts
def load_contract_from_csv(config, directory, csv_path: str) -> Contract:
path = Path(csv_path)
if path.is_file():
contracts = load_contracts_from_csv(config, directory, csv_path=csv_path)
if len(contracts) == 0:
raise Exception("No contracts found in CSV")
if len(contracts) > 1:
log.warning(
"Warning multiple contracts found in CSV. Only the first contract will be used"
)
else:
raise Exception("CSV file does not exist")
return contracts[0]

View File

@ -1,32 +1,25 @@
# standard imports # standard imports
import os
import logging
import sys
import json
import requests
import importlib
import tempfile
import hashlib import hashlib
import importlib
import json
import logging
import os
import sys
import tempfile
from typing import Callable, TypedDict, Union
import requests
from cic.contract.constants import CONTRACT_URLS
# local imports # local imports
from cic.writers import OutputWriter from cic.writers import WritersType
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
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/DemurrageTokenSingleNocap",
"name": "Demurrage Token Single No Cap",
}
]
# Download File from Url # Download File from Url
def download_file(url: str, filename=None) -> (str, bytes): def download_file(url: str, filename=None) -> str:
directory = tempfile.gettempdir() directory = tempfile.gettempdir()
filename = filename if filename else url.split("/")[-1] filename = filename if filename else url.split("/")[-1]
log.debug(f"Downloading {filename}") log.debug(f"Downloading {filename}")
@ -38,27 +31,29 @@ def download_file(url: str, filename=None) -> (str, bytes):
log.debug(f"{filename} downloaded to {path}") log.debug(f"{filename} downloaded to {path}")
return path return path
def get_contract_args(data: list): def get_contract_args(data: list):
for item in data: for item in data:
if item["type"] == "constructor": if item["type"] == "constructor":
return item["inputs"] return item["inputs"]
raise Exception("No constructor found in contract") raise Exception("No constructor found in contract")
def select_contract(): def select_contract():
print("Contracts:") print("Contracts:")
print("\t C - Custom (path/url to contract)") print("\t C - Custom (path/url to contract)")
for idx, contract in enumerate(CONTRACTS): for idx, contract in enumerate(CONTRACT_URLS):
print(f"\t {idx} - {contract['name']}") print(f"\t {idx} - {contract['name']}")
val = input("Select contract (C,0,1..): ") val = input("Select contract (C,0,1..): ")
if val.isdigit() and int(val) < len(CONTRACTS): if val.isdigit() and int(val) < len(CONTRACT_URLS):
contract = CONTRACTS[int(val)] contract = CONTRACT_URLS[int(val)]
bin_path = os.path.abspath(download_file(contract["url"] + ".bin")) bin_path = os.path.abspath(download_file(contract["url"] + ".bin"))
json_path = download_file(contract["url"] + ".json") json_path = download_file(contract["url"] + ".json")
elif val == "C": elif val == "C":
possible_bin_location = input("Enter a path or url to a contract.bin: ") possible_bin_location = input("Enter a path or url to a contract.bin: ")
if possible_bin_location.startswith('http'): if possible_bin_location.startswith("http"):
# possible_bin_location is url # possible_bin_location is url
bin_path = download_file(possible_bin_location) bin_path = download_file(possible_bin_location)
else: else:
@ -99,25 +94,28 @@ def select_contract():
} }
Writers = { class Writers(TypedDict):
"meta": OutputWriter, meta: Union[WritersType, Callable[..., WritersType]]
"attachment": OutputWriter, attachment: Callable[..., WritersType]
"proof": OutputWriter, proof: Callable[..., WritersType]
"ext": OutputWriter, ext: Union[WritersType, Callable[..., WritersType]]
}
def init_writers_from_config(config) -> Writers: def init_writers_from_config(config) -> Writers:
writers: Writers = { writers = {}
"meta": None, writer_keys = ["meta", "attachment", "proof", "ext"]
"attachment": None, for key in writer_keys:
"proof": None,
"ext": None,
}
for key in writers:
writer_config_name = f"CIC_CORE_{key.upper()}_WRITER" writer_config_name = f"CIC_CORE_{key.upper()}_WRITER"
(module_name, attribute_name) = config.get(writer_config_name).rsplit(".", maxsplit=1) (module_name, attribute_name) = config.get(writer_config_name).rsplit(
".", maxsplit=1
)
mod = importlib.import_module(module_name) mod = importlib.import_module(module_name)
writer = getattr(mod, attribute_name) writer = getattr(mod, attribute_name)
writers[key] = writer writers[key] = writer
return writers return Writers(
meta=writers["meta"],
attachment=writers["attachment"],
proof=writers["proof"],
ext=writers["ext"],
)

View File

@ -84,6 +84,7 @@ class ContractProcessor:
if a is None: if a is None:
logg.debug(f'skipping missing task receiver "{task}"') logg.debug(f'skipping missing task receiver "{task}"')
continue continue
logg.debug(f'Processing "{ext}:{task}"')
v = a.process( v = a.process(
token_address=token_address, token_address=token_address,
token_symbol=token_symbol, token_symbol=token_symbol,

View File

@ -5,12 +5,12 @@ proof_writer = cic.writers.KVWriter
ext_writer = cic.writers.KVWriter ext_writer = cic.writers.KVWriter
[cic] [cic]
registry_address = 0xcf60ebc445b636a5ab787f9e8bc465a2a3ef8299 registry_address = 0xe3e3431BF25b06166513019Ed7B21598D27d05dC
[meta] [meta]
url = https://meta.grassecon.net url = https://meta.sarafu.network
http_origin = http_origin =
auth_token =
[auth] [auth]
type = gnupg type = gnupg
keyfile_path = keyfile_path =

View File

@ -2,6 +2,5 @@
"name": "", "name": "",
"location": "", "location": "",
"country_code": "", "country_code": "",
"contact": { "contact": {}
}
} }

View File

@ -1,71 +1,90 @@
# standard imports # standard imports
import os
import logging
import sys
import importlib import importlib
import logging
import os
import sys
# external imports # external imports
import chainlib.cli import chainlib.cli
import cic.cmd.export as cmd_export
import cic.cmd.ext as cmd_ext
# local imports # local imports
import cic.cmd.init as cmd_init import cic.cmd.init as cmd_init
import cic.cmd.show as cmd_show import cic.cmd.show as cmd_show
import cic.cmd.ext as cmd_ext
import cic.cmd.export as cmd_export
import cic.cmd.wizard as cmd_wizard import cic.cmd.wizard as cmd_wizard
from cic.config import ensure_base_configs
logging.basicConfig(level=logging.WARNING) logging.basicConfig(level=logging.WARNING)
logg = logging.getLogger() logg = logging.getLogger()
script_dir = os.path.dirname(os.path.realpath(__file__)) script_dir = os.path.dirname(os.path.realpath(__file__))
data_dir = os.path.join(script_dir, '..', 'data') data_dir = os.path.join(script_dir, "..", "data")
base_config_dir = os.path.join(data_dir, 'config') base_config_dir = os.path.join(data_dir, "config")
schema_dir = os.path.join(script_dir, '..', 'schema') schema_dir = os.path.join(script_dir, "..", "schema")
user_config_dir = os.path.join(
os.path.expanduser("~"), ".config", "cic", "cli", "config"
)
arg_flags = chainlib.cli.argflag_std_read | chainlib.cli.Flag.SEQ arg_flags = chainlib.cli.argflag_std_read | chainlib.cli.Flag.SEQ
argparser = chainlib.cli.ArgumentParser( argparser = chainlib.cli.ArgumentParser(
env=os.environ, env=os.environ,
arg_flags=arg_flags, arg_flags=arg_flags,
description='CIC cli tool for generating and publishing tokens' description="CIC cli tool for generating and publishing contracts",
) )
sub = argparser.add_subparsers() sub = argparser.add_subparsers()
sub.dest = 'command' sub.dest = "command"
sub_init = sub.add_parser('init', help='initialize new cic data directory') sub_init = sub.add_parser("init", help="initialize new cic data directory")
cmd_init.process_args(sub_init) cmd_init.process_args(sub_init)
sub_show = sub.add_parser('show', help='display summary of current state of cic data directory') sub_show = sub.add_parser(
"show", help="display summary of current state of cic data directory"
)
cmd_show.process_args(sub_show) cmd_show.process_args(sub_show)
sub_export = sub.add_parser('export', help='export cic data directory state to a specified target') sub_export = sub.add_parser(
"export", help="export cic data directory state to a specified target"
)
cmd_export.process_args(sub_export) cmd_export.process_args(sub_export)
sub_ext = sub.add_parser('ext', help='extension helpers') sub_ext = sub.add_parser("ext", help="extension helpers")
cmd_ext.process_args(sub_ext) cmd_ext.process_args(sub_ext)
sub_wizard = sub.add_parser('wizard', help='An interactive wizard for creating and publishing contracts') sub_wizard = sub.add_parser(
"wizard", help="An interactive wizard for creating and publishing contracts"
)
cmd_wizard.process_args(sub_wizard) cmd_wizard.process_args(sub_wizard)
args = argparser.parse_args(sys.argv[1:]) args = argparser.parse_args(sys.argv[1:])
if args.command is None: if args.command is None:
logg.critical('Subcommand missing') logg.critical("Subcommand missing")
sys.stderr.write("\033[;91m" + 'subcommand missing' + "\033[;39m\n") sys.stderr.write("\033[;91m" + "subcommand missing" + "\033[;39m\n")
argparser.print_help(sys.stderr) argparser.print_help(sys.stderr)
sys.exit(1) sys.exit(1)
modname = f'cic.cmd.{args.command}' modname = f"cic.cmd.{args.command}"
logg.debug(f'using module {modname}') logg.debug(f"using module {modname}")
cmd_mod = importlib.import_module(modname) cmd_mod = importlib.import_module(modname)
extra_args = { extra_args = {
'p': 'RPC_PROVIDER', "p": "RPC_PROVIDER",
} }
ensure_base_configs(user_config_dir)
config = chainlib.cli.Config.from_args(args, arg_flags=arg_flags, base_config_dir=base_config_dir, extra_args=extra_args)
def main(): def main():
default_config_dir = args.config or os.path.join(user_config_dir, "mainnet")
config = chainlib.cli.Config.from_args(
args,
arg_flags=arg_flags,
base_config_dir=base_config_dir,
extra_args=extra_args,
default_config_dir=default_config_dir,
)
try: try:
cmd_mod.execute(config, args) cmd_mod.execute(config, args)
except Exception as e: except Exception as e:
@ -73,5 +92,6 @@ def main():
sys.stderr.write("\033[;91m" + str(e) + "\033[;39m\n") sys.stderr.write("\033[;91m" + str(e) + "\033[;39m\n")
sys.exit(1) sys.exit(1)
if __name__ == '__main__':
if __name__ == "__main__":
main() main()

View File

@ -4,15 +4,15 @@ def object_to_str(obj, keys):
for key in keys: for key in keys:
value = eval("obj." + key) value = eval("obj." + key)
key = key.replace("()", "") key = key.replace("()", "")
if type(value) == str: if isinstance(value, str):
s += f"{key} = {value}\n" s += f"{key} = {value}\n"
elif type(value) == list: elif isinstance(value, list):
for idx, vv in enumerate(value): for idx, vv in enumerate(value):
if not vv: if not vv:
s += f"{key}[{idx}] = \n" s += f"{key}[{idx}] = \n"
continue continue
s += f"{key}[{idx}] = {vv}\n" s += f"{key}[{idx}] = {vv}\n"
elif type(value) == dict: elif isinstance(value, dict):
for vv_key in value.keys(): for vv_key in value.keys():
vv_value = value[vv_key] vv_value = value[vv_key]
if not vv_value: if not vv_value:

View File

@ -1,12 +1,13 @@
# standard imports # standard imports
import base64 import base64
import json
import logging import logging
import os import os
import sys import sys
import urllib.request import urllib.request
import json from typing import Dict, Type, Union
from cic_types.ext.metadata import MetadataRequestsHandler, MetadataPointer from cic_types.ext.metadata import MetadataPointer, MetadataRequestsHandler
logg = logging.getLogger(__name__) logg = logging.getLogger(__name__)
@ -31,6 +32,7 @@ class KVWriter(OutputWriter):
except FileNotFoundError: except FileNotFoundError:
os.makedirs(path) os.makedirs(path)
self.path = path self.path = path
super().__init__(*args, **kwargs)
def write(self, k, v): def write(self, k, v):
fp = os.path.join(self.path, str(k)) fp = os.path.join(self.path, str(k))
@ -41,16 +43,17 @@ class KVWriter(OutputWriter):
class HTTPWriter(OutputWriter): class HTTPWriter(OutputWriter):
def __init__(self, path=None, *args, **kwargs): def __init__(self, path=None, headers: Dict[str, str] = None, *args, **kwargs):
super(HTTPWriter, self).__init__(*args, **kwargs) super(HTTPWriter, self).__init__(*args, **kwargs)
self.path = path self.path = path
self.headers = headers
def write(self, k, v): def write(self, k, v):
path = self.path path = self.path
if k is not None: if k is not None:
path = os.path.join(path, k) path = os.path.join(path, k)
logg.debug(f"http writer post {path} \n key: {k}, value: {v}") logg.debug(f"HTTPWriter POST {path} data: {v}, headers: {self.headers}")
rq = urllib.request.Request(path, method="POST", data=v) rq = urllib.request.Request(path, method="POST", data=v, headers=self.headers)
r = urllib.request.urlopen(rq) r = urllib.request.urlopen(rq)
logg.info(f"http writer submitted at {r.read()}") logg.info(f"http writer submitted at {r.read()}")
@ -59,6 +62,7 @@ class KeyedWriter(OutputWriter):
def __init__(self, writer_keyed, writer_immutable): def __init__(self, writer_keyed, writer_immutable):
self.writer_keyed = writer_keyed self.writer_keyed = writer_keyed
self.writer_immutable = writer_immutable self.writer_immutable = writer_immutable
super().__init__()
def write(self, k, v): def write(self, k, v):
logg.debug(f"writing keywriter key: {k} value: {v}") logg.debug(f"writing keywriter key: {k} value: {v}")
@ -72,7 +76,7 @@ class KeyedWriter(OutputWriter):
class KeyedWriterFactory: class KeyedWriterFactory:
def __init__( def __init__(
self, key_writer_constructor, immutable_writer_constructor, *args, **kwargs self, key_writer_constructor, immutable_writer_constructor, *_args, **kwargs
): ):
self.key_writer_constructor = key_writer_constructor self.key_writer_constructor = key_writer_constructor
self.immutable_writer_constructor = immutable_writer_constructor self.immutable_writer_constructor = immutable_writer_constructor
@ -81,13 +85,15 @@ class KeyedWriterFactory:
logg.debug(f"adding key {k} t keyed writer factory") logg.debug(f"adding key {k} t keyed writer factory")
self.x[k] = v self.x[k] = v
def new(self, path=None, *args, **kwargs): def new(self, path=None, headers: Dict[str, str] = None, *_args, **_kwargs):
writer_keyed = None writer_keyed = None
writer_immutable = None writer_immutable = None
if self.key_writer_constructor is not None: if self.key_writer_constructor is not None:
writer_keyed = self.key_writer_constructor(path, **self.x) writer_keyed = self.key_writer_constructor(path, **self.x)
if self.immutable_writer_constructor is not None: if self.immutable_writer_constructor is not None:
writer_immutable = self.immutable_writer_constructor(path, **self.x) writer_immutable = self.immutable_writer_constructor(
path, headers, **self.x
)
return KeyedWriter(writer_keyed, writer_immutable) return KeyedWriter(writer_keyed, writer_immutable)
@ -112,3 +118,8 @@ class MetadataWriter(OutputWriter):
r = rq.create(v) r = rq.create(v)
logg.info(f"metadata submitted at {k}") logg.info(f"metadata submitted at {k}")
return r return r
WritersType = Union[
Type[OutputWriter], Type[KeyedWriter], Type[MetadataWriter], Type[OutputWriter]
]

View File

@ -1,27 +0,0 @@
[cic_core]
meta_writer = cic.writers.KVWriter
attachment_writer = cic.writers.KVWriter
proof_writer = cic.writers.KVWriter
ext_writer = cic.writers.KVWriter
[cic]
registry_address = 0x999487d8B1EC2b2ac9F4a9D1A6D35a24D75D7EF4
[meta]
url = https://meta.grassecon.net
http_origin =
[rpc]
provider = https://rpc.kitabu.grassecon.net
[auth]
type = gnupg
keyfile_path = /home/will/.config/cic/staff-client/user.asc
passphrase = queenmarlena
[wallet]
key_file = /home/will/grassroots/cic-internal-integration/apps/contract-migration/keystore
passphrase =
[chain]
spec = evm:kitabu:5050:sarafu

View File

@ -1,27 +0,0 @@
[cic_core]
meta_writer = cic.writers.KVWriter
attachment_writer = cic.writers.KVWriter
proof_writer = cic.writers.KVWriter
ext_writer = cic.writers.KVWriter
[cic]
registry_address = 0xcf60ebc445b636a5ab787f9e8bc465a2a3ef8299
[meta]
url = http://localhost:6700
http_origin =
[rpc]
provider = https://rpc.grassecon.net
[auth]
type = gnupg
keyfile_path = /home/will/grassroots/cic-internal-integration/apps/cic-ussd/tests/data/pgp/privatekeys_meta.asc
passphrase = merman
[wallet]
key_file = /home/will/grassroots/cic-internal-integration/apps/contract-migration/keystore
passphrase =
[chain]
spec = evm:byzantium:5050:bloxberg

3565
poetry.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
[tool.poetry] [tool.poetry]
name = "cic-cli" name = "cic-cli"
version = "0.0.2" version = "0.5.5"
description = "Generic cli tooling for the CIC token network" description = "Generic cli tooling for the CIC token network"
authors = [ authors = [
"Louis Holbrook <dev@holbrook.no>", "Louis Holbrook <dev@holbrook.no>",
@ -32,27 +32,30 @@ cic = 'cic.runnable.cic_cmd:main'
[[tool.poetry.source]] [[tool.poetry.source]]
name = "grassroots_" name = "grassroots_"
url = "https://pip.grassrootseconomics.net/" url = "https://pip.grassrootseconomics.net/"
default = true default = false
secondary = true
[[tool.poetry.source]] [[tool.poetry.source]]
name = "pypi_" name = "pypi_"
url = "https://pypi.org/simple/" url = "https://pypi.org/simple/"
secondary = true default = true
secondary = false
[tool.poetry.dependencies] [tool.poetry.dependencies]
python = "^3.8" python = "^3.8"
funga-eth = "~0.5.5" funga-eth = "^0.6.0"
cic-types = "~0.2.1a8" cic-types = "^0.2.7"
confini = "~0.5.3" confini = "^0.6.0"
chainlib = "~0.0.17" chainlib = "~0.1.0"
cbor2 = "5.4.1" cbor2 = "~5.4.1"
chainlib-eth = { version = "~0.0.25", optional = true } chainlib-eth = { version = "~0.1.1", optional = true }
eth-token-index = { version = "0.2.4", optional = true } eth-token-index = { version = "^0.3.0", optional = true }
eth-address-index = { version = "~0.2.4", optional = true } eth-address-index = { version = "~0.5.0", optional = true }
okota = { version = "~0.2.5", optional = true } okota = { version = "^0.4.0", optional = true }
cic_eth_registry = { version = "~0.6.6", optional = true } cic-eth-registry = { version = "^0.6.9", optional = true }
cic_contracts = { version = "~0.0.5", optional = true } cic-contracts = { version = "~0.1.0", optional = true }
[tool.poetry.dev-dependencies] [tool.poetry.dev-dependencies]
@ -61,10 +64,10 @@ pytest-cov = "2.10.1"
python-semantic-release = "^7.25.2" python-semantic-release = "^7.25.2"
pylint = "^2.12.2" pylint = "^2.12.2"
black = { version = "^22.1.0", allow-prereleases = true } black = { version = "^22.1.0", allow-prereleases = true }
eth-erc20 = ">0.1.2a3,<0.2.0"
eth_tester = "0.5.0b3" eth_tester = "0.5.0b3"
py-evm = "0.3.0a20" py-evm = "0.3.0a20"
rlp = "2.0.1" rlp = "2.0.1"
mypy = "^0.961"
[tool.poetry.extras] [tool.poetry.extras]
eth = [ eth = [
@ -81,13 +84,13 @@ requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api" build-backend = "poetry.core.masonry.api"
[tool.pytest.ini_options] [tool.pytest.ini_options]
addopts = "--cov=cic --cov-fail-under=40 --cov-report term-missing" addopts = "--cov=cic --cov-report term-missing -v"
testpaths = ["tests"] testpaths = ["tests"]
[tool.semantic_release] [tool.semantic_release]
version_variable = ["cic/__init__.py:__version__", "pyproject.toml:version"] version_variable = ["cic/__init__.py:__version__", "pyproject.toml:version"]
version_source = "commit" version_source = "commit"
branch = "main" branch = "master"
upload_to_repository = true upload_to_repository = true
upload_to_release = true upload_to_release = true
build_command = "pip install poetry && poetry build" build_command = "pip install poetry && poetry build"

View File

@ -4,15 +4,14 @@ import random
import tempfile import tempfile
import unittest import unittest
from cic.contract.components.attachment import Attachment # external imports
from cic.contract.components.proof import Proof from hexathon import add_0x
from cic.contract.processor import ContractProcessor
# local imports # local imports
from cic.writers import KVWriter from cic.writers import KVWriter
from cic.contract.components.attachment import Attachment
# external imports from cic.contract.components.proof import Proof
from hexathon import add_0x from cic.contract.processor import ContractProcessor
test_base_dir = os.path.dirname(os.path.realpath(__file__)) test_base_dir = os.path.dirname(os.path.realpath(__file__))
test_data_dir = os.path.join(test_base_dir, "testdata") test_data_dir = os.path.join(test_base_dir, "testdata")

138
tests/test_csv_generate.py Normal file
View File

@ -0,0 +1,138 @@
import os
import tempfile
import pytest
from cic.contract.csv import load_contract_from_csv
from tests.base_cic import test_data_dir
@pytest.mark.skip(reason="Public RPC is currently dead")
def test_csv_generate_demurrage():
outputs_dir = os.path.join(tempfile.mkdtemp(), "outputs")
test_csv_path = os.path.join(test_data_dir, "voucher", "bondi.csv")
contract = load_contract_from_csv(
{
"WALLET_KEY_FILE": os.path.join(test_data_dir, "keystore", "ok"),
"WALLET_PASSPHRASE": "test",
"CHAIN_SPEC": "evm:kitabu:6060:sarafu",
"RPC_PROVIDER": "http://142.93.38.53:8545",
"CIC_REGISTRY_ADDRESS": "0xe3e3431BF25b06166513019Ed7B21598D27d05dC",
},
outputs_dir,
csv_path=test_csv_path,
)
# assert len(contracts) == 1
# contract = contracts[0]
# Token
assert contract.token.name == "Bondeni"
assert contract.token.extra_args == [
"46124891913883000000000000000000",
"1440",
"0xB8830b647C01433F9492F315ddBFDc35CB3Be6A6",
]
assert contract.token.extra_args_types == ["uint256", "uint256", "address"]
# assert contract.token.code == os.path.join(test_data_dir, "contracts", "Bondi.bin")
assert contract.token.precision == '6'
assert contract.token.supply == "5025"
assert contract.token.symbol == "BONDE"
# Meta
assert contract.meta.country_code == "KE"
assert contract.meta.location == "Mutitu Kilifi"
assert contract.meta.contact == {
"email": "info@grassecon.org",
"phone": "254797782065",
}
assert contract.meta.name == "Bondeni SHG"
# Network
assert contract.network.resources["eth"]["chain_spec"] == {
"arch": "evm",
"common_name": "sarafu",
"custom": [],
"extra": {},
"fork": "kitabu",
"network_id": 6060,
}
assert contract.network.resources["eth"]["contents"] == {
"address_declarator": {
"key_account": "cc4f82f5dacde395e1e0cfc4d62827c8b8b5688c",
"reference": "f055e83f713DbFF947e923749Af9802eaffFB5f9",
},
"token": {
"key_account": "cc4f82f5dacde395e1e0cfc4d62827c8b8b5688c",
"reference": None,
},
"token_index": {
"key_account": "cc4f82f5dacde395e1e0cfc4d62827c8b8b5688c",
"reference": "5A1EB529438D8b3cA943A45a48744f4c73d1f098",
},
}
assert contract.proof.description == "1 BONDE = 1 itumbe"
assert contract.proof.namespace == "ge"
assert contract.proof.proofs == []
assert contract.proof.version() == 0
@pytest.mark.skip(reason="Public RPC is currently dead")
def test_csv_generate_giftable():
outputs_dir = os.path.join(tempfile.mkdtemp(), "outputs")
test_csv_path = os.path.join(test_data_dir, "voucher", "bondi_giftable.csv")
contract = load_contract_from_csv(
{
"WALLET_KEY_FILE": os.path.join(test_data_dir, "keystore", "ok"),
"WALLET_PASSPHRASE": "test",
"CHAIN_SPEC": "evm:kitabu:6060:sarafu",
"RPC_PROVIDER": "http://142.93.38.53:8545",
"CIC_REGISTRY_ADDRESS": "0xe3e3431BF25b06166513019Ed7B21598D27d05dC",
},
outputs_dir,
csv_path=test_csv_path,
)
# assert len(contracts) == 1
# contract = contracts[0]
# Token
assert contract.token.name == "Bondeni"
assert contract.token.extra_args == []
assert contract.token.extra_args_types == []
# assert contract.token.code == os.path.join(test_data_dir, "contracts", "Bondi.bin")
assert contract.token.precision == '6'
assert contract.token.supply == "5025"
assert contract.token.symbol == "BONDE"
# Meta
assert contract.meta.country_code == "KE"
assert contract.meta.location == "Mutitu Kilifi"
assert contract.meta.contact == {
"email": "info@grassecon.org",
"phone": "254797782065",
}
assert contract.meta.name == "Bondeni SHG"
# Network
assert contract.network.resources["eth"]["chain_spec"] == {
"arch": "evm",
"common_name": "sarafu",
"custom": [],
"extra": {},
"fork": "kitabu",
"network_id": 6060,
}
assert contract.network.resources["eth"]["contents"] == {
"address_declarator": {
"key_account": "cc4f82f5dacde395e1e0cfc4d62827c8b8b5688c",
"reference": "f055e83f713DbFF947e923749Af9802eaffFB5f9",
},
"token": {
"key_account": "cc4f82f5dacde395e1e0cfc4d62827c8b8b5688c",
"reference": None,
},
"token_index": {
"key_account": "cc4f82f5dacde395e1e0cfc4d62827c8b8b5688c",
"reference": "5A1EB529438D8b3cA943A45a48744f4c73d1f098",
},
}
assert contract.proof.description == "1 BONDE = 1 itumbe"
assert contract.proof.namespace == "ge"
assert contract.proof.proofs == []
assert contract.proof.version() == 0

View File

@ -14,7 +14,7 @@ from cic.keystore import KeystoreDirectory
# test imports # test imports
from tests.base_cic import test_base_dir from tests.base_cic import test_base_dir
logging = logging.getLogger() log = logging.getLogger(__name__)
script_dir = test_base_dir script_dir = test_base_dir

2
tests/testdata/voucher/bondi.csv vendored Normal file
View File

@ -0,0 +1,2 @@
issuer,namespace,voucher_name,symbol,location,country_code,supply,precision,token_type,demurrage,period_minutes,phone_number,email_address,sink_account,description
Bondeni SHG,ge,Bondeni,BONDE,Mutitu Kilifi,KE,5025,6,demurrage,46124891913883000000000000000000,1440,254797782065,info@grassecon.org,0xB8830b647C01433F9492F315ddBFDc35CB3Be6A6,1 BONDE = 1 itumbe
1 issuer namespace voucher_name symbol location country_code supply precision token_type demurrage period_minutes phone_number email_address sink_account description
2 Bondeni SHG ge Bondeni BONDE Mutitu Kilifi KE 5025 6 demurrage 46124891913883000000000000000000 1440 254797782065 info@grassecon.org 0xB8830b647C01433F9492F315ddBFDc35CB3Be6A6 1 BONDE = 1 itumbe

View File

@ -0,0 +1,2 @@
issuer,namespace,voucher_name,symbol,location,country_code,supply,precision,token_type,demurrage,period_minutes,phone_number,email_address,sink_account,description
Bondeni SHG,ge,Bondeni,BONDE,Mutitu Kilifi,KE,5025,6,giftable,,,254797782065,info@grassecon.org,,1 BONDE = 1 itumbe
1 issuer namespace voucher_name symbol location country_code supply precision token_type demurrage period_minutes phone_number email_address sink_account description
2 Bondeni SHG ge Bondeni BONDE Mutitu Kilifi KE 5025 6 giftable 254797782065 info@grassecon.org 1 BONDE = 1 itumbe