Compare commits
2 Commits
master
...
1498edbb07
Author | SHA1 | Date | |
---|---|---|---|
1498edbb07 | |||
10cbb1344d |
@ -1,2 +0,0 @@
|
|||||||
[report]
|
|
||||||
omit =
|
|
65
.drone.yml
65
.drone.yml
@ -1,65 +0,0 @@
|
|||||||
---
|
|
||||||
################
|
|
||||||
# Test #
|
|
||||||
################
|
|
||||||
|
|
||||||
kind: pipeline
|
|
||||||
name: default
|
|
||||||
type: docker
|
|
||||||
|
|
||||||
steps:
|
|
||||||
# Run tests against Python with pytest
|
|
||||||
- name: test
|
|
||||||
image: python:3.8
|
|
||||||
commands:
|
|
||||||
# Install dependencies
|
|
||||||
- pip install poetry
|
|
||||||
- poetry install -E eth
|
|
||||||
- poetry run pylint cic --fail-under=8.00
|
|
||||||
- poetry run pytest
|
|
||||||
environment:
|
|
||||||
LOGLEVEL: info
|
|
||||||
|
|
||||||
volumes:
|
|
||||||
- name: poetry_cache
|
|
||||||
path: /root/.cache/pypoetry
|
|
||||||
- name: pip_cache
|
|
||||||
path: /root/.cache/pip
|
|
||||||
when:
|
|
||||||
event:
|
|
||||||
- push
|
|
||||||
- name: publish
|
|
||||||
image: python:3.8
|
|
||||||
commands:
|
|
||||||
# Install dependencies
|
|
||||||
- pip install poetry
|
|
||||||
- poetry install
|
|
||||||
- poetry run semantic-release publish
|
|
||||||
depends_on:
|
|
||||||
- test
|
|
||||||
when:
|
|
||||||
branch:
|
|
||||||
- master
|
|
||||||
event:
|
|
||||||
- push
|
|
||||||
environment:
|
|
||||||
LOGLEVEL: info
|
|
||||||
GIT_SSL_NO_VERIFY: 1
|
|
||||||
REPOSITORY_USERNAME: __token__
|
|
||||||
REPOSITORY_PASSWORD:
|
|
||||||
from_secret: pypi_token
|
|
||||||
GITEA_TOKEN:
|
|
||||||
from_secret: gitea_token
|
|
||||||
|
|
||||||
volumes:
|
|
||||||
- name: poetry_cache
|
|
||||||
path: /root/.cache/pypoetry
|
|
||||||
- name: pip_cache
|
|
||||||
path: /root/.cache/pip
|
|
||||||
volumes:
|
|
||||||
- name: poetry_cache
|
|
||||||
host:
|
|
||||||
path: /tmp/cache/drone/pypoetry
|
|
||||||
- name: pip_cache
|
|
||||||
host:
|
|
||||||
path: /tmp/cache/drone/pip
|
|
9
.gitignore
vendored
9
.gitignore
vendored
@ -1,12 +1,3 @@
|
|||||||
__pycache__
|
__pycache__
|
||||||
*.pyc
|
*.pyc
|
||||||
*.egg-info
|
*.egg-info
|
||||||
.venv
|
|
||||||
build/
|
|
||||||
.vscode
|
|
||||||
.idea
|
|
||||||
contracts
|
|
||||||
*.egg
|
|
||||||
.coverage
|
|
||||||
deployments/
|
|
||||||
dist/
|
|
402
.pylintrc
402
.pylintrc
@ -1,402 +0,0 @@
|
|||||||
[MASTER]
|
|
||||||
|
|
||||||
# Specify a configuration file.
|
|
||||||
#rcfile=
|
|
||||||
|
|
||||||
# Python code to execute, usually for sys.path manipulation such as
|
|
||||||
# pygtk.require().
|
|
||||||
#init-hook=
|
|
||||||
|
|
||||||
# Add files or directories to the blacklist. They should be base names, not
|
|
||||||
# paths.
|
|
||||||
ignore=third_party
|
|
||||||
|
|
||||||
# Add files or directories matching the regex patterns to the blacklist. The
|
|
||||||
# regex matches against base names, not paths.
|
|
||||||
ignore-patterns=object_detection_grpc_client.py,prediction_pb2.py,prediction_pb2_grpc.py
|
|
||||||
|
|
||||||
# Pickle collected data for later comparisons.
|
|
||||||
persistent=no
|
|
||||||
|
|
||||||
# List of plugins (as comma separated values of python modules names) to load,
|
|
||||||
# usually to register additional checkers.
|
|
||||||
load-plugins=
|
|
||||||
|
|
||||||
# Use multiple processes to speed up Pylint.
|
|
||||||
jobs=4
|
|
||||||
|
|
||||||
# Allow loading of arbitrary C extensions. Extensions are imported into the
|
|
||||||
# active Python interpreter and may run arbitrary code.
|
|
||||||
unsafe-load-any-extension=no
|
|
||||||
|
|
||||||
# A comma-separated list of package or module names from where C extensions may
|
|
||||||
# be loaded. Extensions are loading into the active Python interpreter and may
|
|
||||||
# run arbitrary code
|
|
||||||
extension-pkg-whitelist=
|
|
||||||
|
|
||||||
|
|
||||||
[MESSAGES CONTROL]
|
|
||||||
|
|
||||||
# Only show warnings with the listed confidence levels. Leave empty to show
|
|
||||||
# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED
|
|
||||||
confidence=
|
|
||||||
|
|
||||||
# Enable the message, report, category or checker with the given id(s). You can
|
|
||||||
# either give multiple identifier separated by comma (,) or put this option
|
|
||||||
# multiple time (only on the command line, not in the configuration file where
|
|
||||||
# it should appear only once). See also the "--disable" option for examples.
|
|
||||||
#enable=
|
|
||||||
|
|
||||||
# Disable the message, report, category or checker with the given id(s). You
|
|
||||||
# can either give multiple identifiers separated by comma (,) or put this
|
|
||||||
# option multiple times (only on the command line, not in the configuration
|
|
||||||
# file where it should appear only once).You can also use "--disable=all" to
|
|
||||||
# disable everything first and then reenable specific checks. For example, if
|
|
||||||
# you want to run only the similarities checker, you can use "--disable=all
|
|
||||||
# --enable=similarities". If you want to run only the classes checker, but have
|
|
||||||
# no Warning level messages displayed, use"--disable=all --enable=classes
|
|
||||||
# --disable=W"
|
|
||||||
#
|
|
||||||
# Kubeflow disables string-interpolation because we are starting to use f
|
|
||||||
# style strings
|
|
||||||
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]
|
|
||||||
|
|
||||||
# Set the output format. Available formats are text, parseable, colorized, msvs
|
|
||||||
# (visual studio) and html. You can also give a reporter class, eg
|
|
||||||
# mypackage.mymodule.MyReporterClass.
|
|
||||||
output-format=text
|
|
||||||
|
|
||||||
# Put messages in a separate file for each module / package specified on the
|
|
||||||
# command line instead of printing them on stdout. Reports (if any) will be
|
|
||||||
# written in a file name "pylint_global.[txt|html]". This option is deprecated
|
|
||||||
# and it will be removed in Pylint 2.0.
|
|
||||||
files-output=no
|
|
||||||
|
|
||||||
# Tells whether to display a full report or only the messages
|
|
||||||
reports=no
|
|
||||||
|
|
||||||
# Python expression which should return a note less than 10 (10 is the highest
|
|
||||||
# note). You have access to the variables errors warning, statement which
|
|
||||||
# respectively contain the number of errors / warnings messages and the total
|
|
||||||
# number of statements analyzed. This is used by the global evaluation report
|
|
||||||
# (RP0004).
|
|
||||||
evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)
|
|
||||||
|
|
||||||
# Template used to display messages. This is a python new-style format string
|
|
||||||
# used to format the message information. See doc for all details
|
|
||||||
#msg-template=
|
|
||||||
|
|
||||||
|
|
||||||
[BASIC]
|
|
||||||
|
|
||||||
# Good variable names which should always be accepted, separated by a comma
|
|
||||||
good-names=i,j,k,ex,Run,_
|
|
||||||
|
|
||||||
# Bad variable names which should always be refused, separated by a comma
|
|
||||||
bad-names=foo,bar,baz,toto,tutu,tata
|
|
||||||
|
|
||||||
# Colon-delimited sets of names that determine each other's naming style when
|
|
||||||
# the name regexes allow several styles.
|
|
||||||
name-group=
|
|
||||||
|
|
||||||
# Include a hint for the correct naming format with invalid-name
|
|
||||||
include-naming-hint=no
|
|
||||||
|
|
||||||
# List of decorators that produce properties, such as abc.abstractproperty. Add
|
|
||||||
# to this list to register other decorators that produce valid properties.
|
|
||||||
property-classes=abc.abstractproperty
|
|
||||||
|
|
||||||
# Regular expression matching correct function names
|
|
||||||
function-rgx=[a-z_][a-z0-9_]{2,30}$
|
|
||||||
|
|
||||||
# Naming hint for function names
|
|
||||||
function-name-hint=[a-z_][a-z0-9_]{2,30}$
|
|
||||||
|
|
||||||
# Regular expression matching correct variable names
|
|
||||||
variable-rgx=[a-z_][a-z0-9_]{2,30}$
|
|
||||||
|
|
||||||
# Naming hint for variable names
|
|
||||||
variable-name-hint=[a-z_][a-z0-9_]{2,30}$
|
|
||||||
|
|
||||||
# Regular expression matching correct constant names
|
|
||||||
const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$
|
|
||||||
|
|
||||||
# Naming hint for constant names
|
|
||||||
const-name-hint=(([A-Z_][A-Z0-9_]*)|(__.*__))$
|
|
||||||
|
|
||||||
# Regular expression matching correct attribute names
|
|
||||||
attr-rgx=[a-z_][a-z0-9_]{2,30}$
|
|
||||||
|
|
||||||
# Naming hint for attribute names
|
|
||||||
attr-name-hint=[a-z_][a-z0-9_]{2,30}$
|
|
||||||
|
|
||||||
# Regular expression matching correct argument names
|
|
||||||
argument-rgx=[a-z_][a-z0-9_]{2,30}$
|
|
||||||
|
|
||||||
# Naming hint for argument names
|
|
||||||
argument-name-hint=[a-z_][a-z0-9_]{2,30}$
|
|
||||||
|
|
||||||
# Regular expression matching correct class attribute names
|
|
||||||
class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$
|
|
||||||
|
|
||||||
# Naming hint for class attribute names
|
|
||||||
class-attribute-name-hint=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$
|
|
||||||
|
|
||||||
# Regular expression matching correct inline iteration names
|
|
||||||
inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$
|
|
||||||
|
|
||||||
# Naming hint for inline iteration names
|
|
||||||
inlinevar-name-hint=[A-Za-z_][A-Za-z0-9_]*$
|
|
||||||
|
|
||||||
# Regular expression matching correct class names
|
|
||||||
class-rgx=[A-Z_][a-zA-Z0-9]+$
|
|
||||||
|
|
||||||
# Naming hint for class names
|
|
||||||
class-name-hint=[A-Z_][a-zA-Z0-9]+$
|
|
||||||
|
|
||||||
# Regular expression matching correct module names
|
|
||||||
module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$
|
|
||||||
|
|
||||||
# Naming hint for module names
|
|
||||||
module-name-hint=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$
|
|
||||||
|
|
||||||
# Regular expression matching correct method names
|
|
||||||
method-rgx=[a-z_][a-z0-9_]{2,30}$
|
|
||||||
|
|
||||||
# Naming hint for method names
|
|
||||||
method-name-hint=[a-z_][a-z0-9_]{2,30}$
|
|
||||||
|
|
||||||
# Regular expression which should only match function or class names that do
|
|
||||||
# not require a docstring.
|
|
||||||
no-docstring-rgx=^_
|
|
||||||
|
|
||||||
# Minimum line length for functions/classes that require docstrings, shorter
|
|
||||||
# ones are exempt.
|
|
||||||
docstring-min-length=-1
|
|
||||||
|
|
||||||
|
|
||||||
[ELIF]
|
|
||||||
|
|
||||||
# Maximum number of nested blocks for function / method body
|
|
||||||
max-nested-blocks=5
|
|
||||||
|
|
||||||
|
|
||||||
[TYPECHECK]
|
|
||||||
|
|
||||||
# Tells whether missing members accessed in mixin class should be ignored. A
|
|
||||||
# mixin class is detected if its name ends with "mixin" (case insensitive).
|
|
||||||
ignore-mixin-members=yes
|
|
||||||
|
|
||||||
# List of module names for which member attributes should not be checked
|
|
||||||
# (useful for modules/projects where namespaces are manipulated during runtime
|
|
||||||
# and thus existing member attributes cannot be deduced by static analysis. It
|
|
||||||
# supports qualified module names, as well as Unix pattern matching.
|
|
||||||
ignored-modules=
|
|
||||||
|
|
||||||
# List of class names for which member attributes should not be checked (useful
|
|
||||||
# for classes with dynamically set attributes). This supports the use of
|
|
||||||
# qualified names.
|
|
||||||
ignored-classes=optparse.Values,thread._local,_thread._local
|
|
||||||
|
|
||||||
# List of members which are set dynamically and missed by pylint inference
|
|
||||||
# system, and so shouldn't trigger E1101 when accessed. Python regular
|
|
||||||
# expressions are accepted.
|
|
||||||
generated-members=
|
|
||||||
|
|
||||||
# List of decorators that produce context managers, such as
|
|
||||||
# contextlib.contextmanager. Add to this list to register other decorators that
|
|
||||||
# produce valid context managers.
|
|
||||||
contextmanager-decorators=contextlib.contextmanager
|
|
||||||
|
|
||||||
|
|
||||||
[FORMAT]
|
|
||||||
|
|
||||||
# Maximum number of characters on a single line.
|
|
||||||
max-line-length=140
|
|
||||||
|
|
||||||
# Regexp for a line that is allowed to be longer than the limit.
|
|
||||||
ignore-long-lines=^\s*(# )?<?https?://\S+>?$
|
|
||||||
|
|
||||||
# Allow the body of an if to be on the same line as the test if there is no
|
|
||||||
# else.
|
|
||||||
single-line-if-stmt=no
|
|
||||||
|
|
||||||
# List of optional constructs for which whitespace checking is disabled. `dict-
|
|
||||||
# separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}.
|
|
||||||
# `trailing-comma` allows a space between comma and closing bracket: (a, ).
|
|
||||||
# `empty-line` allows space-only lines.
|
|
||||||
no-space-check=trailing-comma,dict-separator
|
|
||||||
|
|
||||||
# Maximum number of lines in a module
|
|
||||||
max-module-lines=1000
|
|
||||||
|
|
||||||
# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
|
|
||||||
# tab).
|
|
||||||
# Use 2 spaces consistent with TensorFlow style.
|
|
||||||
indent-string=' '
|
|
||||||
|
|
||||||
# Number of spaces of indent required inside a hanging or continued line.
|
|
||||||
indent-after-paren=4
|
|
||||||
|
|
||||||
# Expected format of line ending, e.g. empty (any line ending), LF or CRLF.
|
|
||||||
expected-line-ending-format=
|
|
||||||
|
|
||||||
|
|
||||||
[MISCELLANEOUS]
|
|
||||||
|
|
||||||
# List of note tags to take in consideration, separated by a comma.
|
|
||||||
notes=FIXME,XXX,TODO
|
|
||||||
|
|
||||||
|
|
||||||
[VARIABLES]
|
|
||||||
|
|
||||||
# Tells whether we should check for unused import in __init__ files.
|
|
||||||
init-import=no
|
|
||||||
|
|
||||||
# A regular expression matching the name of dummy variables (i.e. expectedly
|
|
||||||
# not used).
|
|
||||||
dummy-variables-rgx=(_+[a-zA-Z0-9]*?$)|dummy
|
|
||||||
|
|
||||||
# List of additional names supposed to be defined in builtins. Remember that
|
|
||||||
# you should avoid to define new builtins when possible.
|
|
||||||
additional-builtins=
|
|
||||||
|
|
||||||
# List of strings which can identify a callback function by name. A callback
|
|
||||||
# name must start or end with one of those strings.
|
|
||||||
callbacks=cb_,_cb
|
|
||||||
|
|
||||||
# List of qualified module names which can have objects that can redefine
|
|
||||||
# builtins.
|
|
||||||
redefining-builtins-modules=six.moves,future.builtins
|
|
||||||
|
|
||||||
|
|
||||||
[LOGGING]
|
|
||||||
|
|
||||||
# Logging modules to check that the string format arguments are in logging
|
|
||||||
# function parameter format
|
|
||||||
logging-modules=logging
|
|
||||||
|
|
||||||
|
|
||||||
[SIMILARITIES]
|
|
||||||
|
|
||||||
# Minimum lines number of a similarity.
|
|
||||||
min-similarity-lines=4
|
|
||||||
|
|
||||||
# Ignore comments when computing similarities.
|
|
||||||
ignore-comments=yes
|
|
||||||
|
|
||||||
# Ignore docstrings when computing similarities.
|
|
||||||
ignore-docstrings=yes
|
|
||||||
|
|
||||||
# Ignore imports when computing similarities.
|
|
||||||
ignore-imports=no
|
|
||||||
|
|
||||||
|
|
||||||
[SPELLING]
|
|
||||||
|
|
||||||
# Spelling dictionary name. Available dictionaries: none. To make it working
|
|
||||||
# install python-enchant package.
|
|
||||||
spelling-dict=
|
|
||||||
|
|
||||||
# List of comma separated words that should not be checked.
|
|
||||||
spelling-ignore-words=
|
|
||||||
|
|
||||||
# A path to a file that contains private dictionary; one word per line.
|
|
||||||
spelling-private-dict-file=
|
|
||||||
|
|
||||||
# Tells whether to store unknown words to indicated private dictionary in
|
|
||||||
# --spelling-private-dict-file option instead of raising a message.
|
|
||||||
spelling-store-unknown-words=no
|
|
||||||
|
|
||||||
|
|
||||||
[IMPORTS]
|
|
||||||
|
|
||||||
# Deprecated modules which should not be used, separated by a comma
|
|
||||||
deprecated-modules=regsub,TERMIOS,Bastion,rexec
|
|
||||||
|
|
||||||
# Create a graph of every (i.e. internal and external) dependencies in the
|
|
||||||
# given file (report RP0402 must not be disabled)
|
|
||||||
import-graph=
|
|
||||||
|
|
||||||
# Create a graph of external dependencies in the given file (report RP0402 must
|
|
||||||
# not be disabled)
|
|
||||||
ext-import-graph=
|
|
||||||
|
|
||||||
# Create a graph of internal dependencies in the given file (report RP0402 must
|
|
||||||
# not be disabled)
|
|
||||||
int-import-graph=
|
|
||||||
|
|
||||||
# Force import order to recognize a module as part of the standard
|
|
||||||
# compatibility libraries.
|
|
||||||
known-standard-library=
|
|
||||||
|
|
||||||
# Force import order to recognize a module as part of a third party library.
|
|
||||||
known-third-party=enchant
|
|
||||||
|
|
||||||
# Analyse import fallback blocks. This can be used to support both Python 2 and
|
|
||||||
# 3 compatible code, which means that the block might have code that exists
|
|
||||||
# only in one or another interpreter, leading to false positives when analysed.
|
|
||||||
analyse-fallback-blocks=no
|
|
||||||
|
|
||||||
|
|
||||||
[DESIGN]
|
|
||||||
|
|
||||||
# Maximum number of arguments for function / method
|
|
||||||
max-args=7
|
|
||||||
|
|
||||||
# Argument names that match this expression will be ignored. Default to name
|
|
||||||
# with leading underscore
|
|
||||||
ignored-argument-names=_.*
|
|
||||||
|
|
||||||
# Maximum number of locals for function / method body
|
|
||||||
max-locals=15
|
|
||||||
|
|
||||||
# Maximum number of return / yield for function / method body
|
|
||||||
max-returns=6
|
|
||||||
|
|
||||||
# Maximum number of branch for function / method body
|
|
||||||
max-branches=12
|
|
||||||
|
|
||||||
# Maximum number of statements in function / method body
|
|
||||||
max-statements=50
|
|
||||||
|
|
||||||
# Maximum number of parents for a class (see R0901).
|
|
||||||
max-parents=7
|
|
||||||
|
|
||||||
# Maximum number of attributes for a class (see R0902).
|
|
||||||
max-attributes=7
|
|
||||||
|
|
||||||
# Minimum number of public methods for a class (see R0903).
|
|
||||||
min-public-methods=0
|
|
||||||
|
|
||||||
# Maximum number of public methods for a class (see R0904).
|
|
||||||
max-public-methods=20
|
|
||||||
|
|
||||||
# Maximum number of boolean expressions in a if statement
|
|
||||||
max-bool-expr=5
|
|
||||||
|
|
||||||
|
|
||||||
[CLASSES]
|
|
||||||
|
|
||||||
# List of method names used to declare (i.e. assign) instance attributes.
|
|
||||||
defining-attr-methods=__init__,__new__,setUp
|
|
||||||
|
|
||||||
# List of valid names for the first argument in a class method.
|
|
||||||
valid-classmethod-first-arg=cls
|
|
||||||
|
|
||||||
# List of valid names for the first argument in a metaclass class method.
|
|
||||||
valid-metaclass-classmethod-first-arg=mcs
|
|
||||||
|
|
||||||
# List of member names, which should be excluded from the protected access
|
|
||||||
# warning.
|
|
||||||
exclude-protected=_asdict,_fields,_replace,_source,_make
|
|
||||||
|
|
||||||
|
|
||||||
[EXCEPTIONS]
|
|
||||||
|
|
||||||
# Exceptions that will emit a warning when being caught. Defaults to
|
|
||||||
# "Exception"
|
|
||||||
overgeneral-exceptions=Exception
|
|
4
CHANGELOG
Normal file
4
CHANGELOG
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
- 0.0.2
|
||||||
|
* Add executable entry point in package install
|
||||||
|
- 0.0.1
|
||||||
|
* Token creation setup for eth
|
104
CHANGELOG.md
104
CHANGELOG.md
@ -1,104 +0,0 @@
|
|||||||
# 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))
|
|
@ -1 +1,2 @@
|
|||||||
__version__ = "0.5.5"
|
from .proof import Proof
|
||||||
|
from .processor import Processor
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
# standard imports
|
# standard imports
|
||||||
import logging
|
|
||||||
import os
|
import os
|
||||||
|
import logging
|
||||||
|
|
||||||
# local imports
|
# local imports
|
||||||
from cic.contract.base import Data, data_dir
|
from .base import *
|
||||||
|
|
||||||
logg = logging.getLogger(__name__)
|
logg = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -14,38 +14,37 @@ class Attachment(Data):
|
|||||||
:param path: Path to settings directory
|
:param path: Path to settings directory
|
||||||
:type path: str
|
:type path: str
|
||||||
:param writer: Writer interface receiving the output of the processor
|
:param writer: Writer interface receiving the output of the processor
|
||||||
:type writer: cic.writers.OutputWriter
|
:type writer: cic.output.OutputWriter
|
||||||
"""
|
"""
|
||||||
|
def __init__(self, path='.', writer=None):
|
||||||
def __init__(self, path=".", writer=None, interactive=False):
|
|
||||||
super(Attachment, self).__init__()
|
super(Attachment, self).__init__()
|
||||||
self.contents = {}
|
self.contents = {}
|
||||||
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')
|
||||||
self.start()
|
|
||||||
if interactive:
|
|
||||||
input(
|
|
||||||
f"Please add attachment files to '{os.path.abspath(os.path.join(self.path,'attachments'))}' and then press ENTER to continue"
|
|
||||||
)
|
|
||||||
self.load()
|
|
||||||
|
|
||||||
def load(self):
|
def load(self):
|
||||||
"""Loads attachment data from settings."""
|
"""Loads attachment data from settings.
|
||||||
|
"""
|
||||||
for s in os.listdir(self.attachment_path):
|
for s in os.listdir(self.attachment_path):
|
||||||
fp = os.path.realpath(os.path.join(self.attachment_path, s))
|
fp = os.path.realpath(os.path.join(self.attachment_path, s))
|
||||||
with open(fp, "rb") as f:
|
f = open(fp, 'rb')
|
||||||
r = f.read()
|
r = f.read()
|
||||||
|
f.close()
|
||||||
|
|
||||||
z = self.hash(r).hex()
|
z = self.hash(r).hex()
|
||||||
self.contents[z] = fp
|
self.contents[z] = fp
|
||||||
|
|
||||||
logg.debug(f"loaded attachment file {fp} digest {z}")
|
logg.debug('loaded attachment file {} digest {}'.format(fp, z))
|
||||||
|
|
||||||
|
|
||||||
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, exist_ok=True)
|
os.makedirs(self.attachment_path)
|
||||||
|
|
||||||
|
|
||||||
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.
|
||||||
@ -55,10 +54,13 @@ class Attachment(Data):
|
|||||||
"""
|
"""
|
||||||
return self.contents[k]
|
return self.contents[k]
|
||||||
|
|
||||||
|
|
||||||
def asdict(self):
|
def asdict(self):
|
||||||
"""Output attachment state to dict"""
|
"""Output attachment state to dict
|
||||||
|
"""
|
||||||
return self.contents
|
return self.contents
|
||||||
|
|
||||||
|
|
||||||
def process(self, token_address=None, token_symbol=None, writer=None):
|
def process(self, token_address=None, token_symbol=None, writer=None):
|
||||||
"""Serialize and publish attachments.
|
"""Serialize and publish attachments.
|
||||||
|
|
||||||
@ -67,16 +69,18 @@ class Attachment(Data):
|
|||||||
if writer == None:
|
if writer == None:
|
||||||
writer = self.writer
|
writer = self.writer
|
||||||
|
|
||||||
for key, value in self.contents.items():
|
for k in self.contents.keys():
|
||||||
fp = os.path.join(self.attachment_path, value)
|
fp = os.path.join(self.attachment_path, self.contents[k])
|
||||||
with open(fp, "rb") as f:
|
f = open(fp, 'rb')
|
||||||
data = f.read()
|
v = f.read()
|
||||||
logg.debug(f"writing attachment {key}")
|
f.close()
|
||||||
writer.write(key, data)
|
logg.debug('writing attachment {}'.format(k))
|
||||||
|
writer.write(k, v)
|
||||||
|
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
s = ""
|
s = ''
|
||||||
for key, value in self.contents.items():
|
for k in self.contents.keys():
|
||||||
s += f"{key} = {value}\n" # self.digests[i].hex(), self.contents[i])
|
s += '{} = {}\n'.format(k, self.contents[k]) #self.digests[i].hex(), self.contents[i])
|
||||||
|
|
||||||
return s
|
return s
|
@ -3,7 +3,7 @@ import os
|
|||||||
import hashlib
|
import hashlib
|
||||||
|
|
||||||
|
|
||||||
mod_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)), '..')
|
mod_dir = os.path.dirname(os.path.realpath(__file__))
|
||||||
root_dir = os.path.join(mod_dir, '..')
|
root_dir = os.path.join(mod_dir, '..')
|
||||||
data_dir = os.path.join(mod_dir, 'data')
|
data_dir = os.path.join(mod_dir, 'data')
|
||||||
schema_dir = os.path.join(mod_dir, 'schema')
|
schema_dir = os.path.join(mod_dir, 'schema')
|
@ -1,51 +1,39 @@
|
|||||||
# standard imports
|
# standard imports
|
||||||
import importlib
|
|
||||||
import logging
|
import logging
|
||||||
|
import importlib
|
||||||
import os
|
import os
|
||||||
from typing import Optional
|
|
||||||
|
|
||||||
# external imports
|
# 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
|
||||||
|
|
||||||
# local imports
|
# local imports
|
||||||
from cic.contract.processor import ContractProcessor
|
from cic import (
|
||||||
from cic.contract.components.proof import Proof
|
Proof,
|
||||||
from cic.contract.components.attachment import Attachment
|
Processor,
|
||||||
from cic.contract.components.meta import Meta
|
)
|
||||||
from cic.contract.network import Network
|
from cic.output import (
|
||||||
from cic.contract.components.token import Token
|
HTTPWriter,
|
||||||
from cic.writers import HTTPWriter, KeyedWriterFactory, MetadataWriter
|
KeyedWriterFactory,
|
||||||
|
)
|
||||||
|
from cic.meta import (
|
||||||
|
Meta,
|
||||||
|
MetadataWriter,
|
||||||
|
)
|
||||||
|
from cic.attachment import Attachment
|
||||||
|
from cic.network import Network
|
||||||
|
from cic.token import Token
|
||||||
|
|
||||||
logg = logging.getLogger(__name__)
|
logg = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def process_args(argparser):
|
def process_args(argparser):
|
||||||
argparser.add_argument(
|
argparser.add_argument('-d', '--directory', type=str, dest='directory', default='.', help='directory')
|
||||||
"-d", "--directory", type=str, dest="directory", default=".", help="directory"
|
argparser.add_argument('-o', '--output-directory', type=str, dest='output_directory', help='output directory')
|
||||||
)
|
argparser.add_argument('--metadata-endpoint', dest='metadata_endpoint', type=str, help='metadata endpoint to interact with')
|
||||||
argparser.add_argument(
|
argparser.add_argument('-y', '--signer', type=str, dest='y', help='target-specific signer to use for export')
|
||||||
"-o",
|
argparser.add_argument('-p', type=str, help='RPC endpoint')
|
||||||
"--output-directory",
|
argparser.add_argument('target', type=str, help='target network type')
|
||||||
type=str,
|
|
||||||
dest="output_directory",
|
|
||||||
help="output directory",
|
|
||||||
)
|
|
||||||
argparser.add_argument(
|
|
||||||
"--metadata-endpoint",
|
|
||||||
dest="metadata_endpoint",
|
|
||||||
type=str,
|
|
||||||
help="metadata endpoint to interact with",
|
|
||||||
)
|
|
||||||
argparser.add_argument(
|
|
||||||
"-y",
|
|
||||||
"--signer",
|
|
||||||
type=str,
|
|
||||||
dest="y",
|
|
||||||
help="target-specific signer to use for export",
|
|
||||||
)
|
|
||||||
argparser.add_argument("-p", type=str, help="RPC endpoint")
|
|
||||||
argparser.add_argument("target", type=str, help="target network type")
|
|
||||||
|
|
||||||
|
|
||||||
def validate_args(args):
|
def validate_args(args):
|
||||||
@ -54,14 +42,14 @@ def validate_args(args):
|
|||||||
|
|
||||||
def init_writers_from_config(config):
|
def init_writers_from_config(config):
|
||||||
w = {
|
w = {
|
||||||
"meta": None,
|
'meta': None,
|
||||||
"attachment": None,
|
'attachment': None,
|
||||||
"proof": None,
|
'proof': None,
|
||||||
"ext": None,
|
'ext': None,
|
||||||
}
|
}
|
||||||
for v in w.keys():
|
for v in w.keys():
|
||||||
k = "CIC_CORE_{}_WRITER".format(v.upper())
|
k = 'CIC_CORE_{}_WRITER'.format(v.upper())
|
||||||
(d, c) = config.get(k).rsplit(".", maxsplit=1)
|
(d, c) = config.get(k).rsplit('.', maxsplit=1)
|
||||||
m = importlib.import_module(d)
|
m = importlib.import_module(d)
|
||||||
o = getattr(m, c)
|
o = getattr(m, c)
|
||||||
w[v] = o
|
w[v] = o
|
||||||
@ -69,17 +57,8 @@ def init_writers_from_config(config):
|
|||||||
return w
|
return w
|
||||||
|
|
||||||
|
|
||||||
ExtraArgs = {
|
def execute(config, eargs):
|
||||||
"target": str,
|
modname = 'cic.ext.{}'.format(eargs.target)
|
||||||
"directory": str,
|
|
||||||
"output_directory": str,
|
|
||||||
"metadata_endpoint": Optional[str],
|
|
||||||
"y": str,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def execute(config, eargs: ExtraArgs):
|
|
||||||
modname = f"cic.ext.{eargs.target}"
|
|
||||||
cmd_mod = importlib.import_module(modname)
|
cmd_mod = importlib.import_module(modname)
|
||||||
|
|
||||||
writers = init_writers_from_config(config)
|
writers = init_writers_from_config(config)
|
||||||
@ -87,26 +66,18 @@ def execute(config, eargs: ExtraArgs):
|
|||||||
output_writer_path_meta = eargs.output_directory
|
output_writer_path_meta = eargs.output_directory
|
||||||
if eargs.metadata_endpoint != None:
|
if eargs.metadata_endpoint != None:
|
||||||
MetadataRequestsHandler.base_url = eargs.metadata_endpoint
|
MetadataRequestsHandler.base_url = eargs.metadata_endpoint
|
||||||
MetadataSigner.gpg_path = os.path.join("/tmp")
|
MetadataSigner.gpg_path = os.path.join('/tmp')
|
||||||
MetadataSigner.key_file_path = config.get("AUTH_KEYFILE_PATH")
|
MetadataSigner.key_file_path = '/home/lash/src/client/cic/grassrootseconomics/cic-internal-integration/apps/cic-ussd/tests/data/pgp/privatekeys_meta.asc'
|
||||||
MetadataSigner.gpg_passphrase = config.get("AUTH_PASSPHRASE")
|
MetadataSigner.gpg_passphrase = 'merman'
|
||||||
writers["proof"] = KeyedWriterFactory(MetadataWriter, HTTPWriter).new
|
writers['proof'] = KeyedWriterFactory(MetadataWriter, HTTPWriter).new
|
||||||
writers["attachment"] = KeyedWriterFactory(None, HTTPWriter).new
|
writers['attachment'] = KeyedWriterFactory(None, HTTPWriter).new
|
||||||
writers["meta"] = MetadataWriter
|
writers['meta'] = MetadataWriter
|
||||||
output_writer_path_meta = eargs.metadata_endpoint
|
output_writer_path_meta = eargs.metadata_endpoint
|
||||||
|
|
||||||
ct = Token(path=eargs.directory)
|
ct = Token(path=eargs.directory)
|
||||||
cm = Meta(
|
cm = Meta(path=eargs.directory, writer=writers['meta'](path=output_writer_path_meta))
|
||||||
path=eargs.directory, writer=writers["meta"](path=output_writer_path_meta)
|
ca = Attachment(path=eargs.directory, writer=writers['attachment'](path=output_writer_path_meta))
|
||||||
)
|
cp = Proof(path=eargs.directory, attachments=ca, writer=writers['proof'](path=output_writer_path_meta))
|
||||||
ca = Attachment(
|
|
||||||
path=eargs.directory, writer=writers["attachment"](path=output_writer_path_meta)
|
|
||||||
)
|
|
||||||
cp = Proof(
|
|
||||||
path=eargs.directory,
|
|
||||||
attachments=ca,
|
|
||||||
writer=writers["proof"](path=output_writer_path_meta),
|
|
||||||
)
|
|
||||||
cn = Network(path=eargs.directory)
|
cn = Network(path=eargs.directory)
|
||||||
|
|
||||||
ca.load()
|
ca.load()
|
||||||
@ -117,29 +88,20 @@ def execute(config, eargs: ExtraArgs):
|
|||||||
|
|
||||||
chain_spec = None
|
chain_spec = None
|
||||||
try:
|
try:
|
||||||
chain_spec = config.get("CHAIN_SPEC")
|
chain_spec = config.get('CHAIN_SPEC')
|
||||||
except KeyError:
|
except KeyError:
|
||||||
chain_spec = cn.chain_spec
|
chain_spec = cn.chain_spec
|
||||||
config.add(chain_spec, "CHAIN_SPEC", exists_ok=True)
|
config.add(chain_spec, 'CHAIN_SPEC', exists_ok=True)
|
||||||
logg.debug(f"CHAIN_SPEC config set to {str(chain_spec)}")
|
logg.debug('CHAIN_SPEC config set to {}'.format(str(chain_spec)))
|
||||||
|
|
||||||
# signer = cmd_mod.parse_signer(eargs.y)
|
#signer = cmd_mod.parse_signer(eargs.y)
|
||||||
(rpc, signer) = cmd_mod.parse_adapter(config, eargs.y)
|
(rpc, signer) = cmd_mod.parse_adapter(config, eargs.y)
|
||||||
|
|
||||||
ref = cn.resource(eargs.target)
|
ref = cn.resource(eargs.target)
|
||||||
chain_spec = cn.chain_spec(eargs.target)
|
chain_spec = cn.chain_spec(eargs.target)
|
||||||
logg.debug(
|
logg.debug('found reference {} chain spec {} for target {}'.format(ref['contents'], chain_spec, eargs.target))
|
||||||
f"found reference {ref['contents']} chain spec {chain_spec} for target {eargs.target}"
|
c = getattr(cmd_mod, 'new')(chain_spec, ref['contents'], cp, signer_hint=signer, rpc=rpc, outputs_writer=writers['ext'](path=eargs.output_directory))
|
||||||
)
|
|
||||||
c = getattr(cmd_mod, "new")(
|
|
||||||
chain_spec,
|
|
||||||
ref["contents"],
|
|
||||||
cp,
|
|
||||||
signer_hint=signer,
|
|
||||||
rpc=rpc,
|
|
||||||
outputs_writer=writers["ext"](path=eargs.output_directory),
|
|
||||||
)
|
|
||||||
c.apply_token(ct)
|
c.apply_token(ct)
|
||||||
|
|
||||||
p = ContractProcessor(proof=cp, attachment=ca, metadata=cm, extensions=[c])
|
p = Processor(proof=cp, attachment=ca, metadata=cm, extensions=[c])
|
||||||
p.process()
|
p.process()
|
||||||
|
@ -3,21 +3,16 @@ import importlib
|
|||||||
|
|
||||||
# external imports
|
# external imports
|
||||||
from chainlib.chain import ChainSpec
|
from chainlib.chain import ChainSpec
|
||||||
|
|
||||||
# local imports
|
# local imports
|
||||||
from cic.contract.network import Network
|
from cic.network import Network
|
||||||
|
|
||||||
|
|
||||||
def process_args(argparser):
|
def process_args(argparser):
|
||||||
argparser.add_argument(
|
argparser.add_argument('--registry', required=True, type=str, help='contract registry address')
|
||||||
"--registry", type=str, help="contract registry address"
|
argparser.add_argument('-d', '--directory', type=str, dest='directory', default='.', help='directory')
|
||||||
)
|
argparser.add_argument('-p', type=str, help='RPC endpoint')
|
||||||
argparser.add_argument(
|
argparser.add_argument('-i', type=str, help='chain spec string')
|
||||||
"-d", "--directory", type=str, dest="directory", default=".", help="directory"
|
argparser.add_argument('target', help='target to initialize')
|
||||||
)
|
|
||||||
argparser.add_argument("-p", type=str, help="RPC endpoint")
|
|
||||||
argparser.add_argument("-i", type=str, help="chain spec string")
|
|
||||||
argparser.add_argument("target", help="target to initialize")
|
|
||||||
|
|
||||||
|
|
||||||
def validate_args(args):
|
def validate_args(args):
|
||||||
@ -28,11 +23,8 @@ def execute(config, eargs):
|
|||||||
cn = Network(eargs.directory, targets=eargs.target)
|
cn = Network(eargs.directory, targets=eargs.target)
|
||||||
cn.load()
|
cn.load()
|
||||||
|
|
||||||
chain_spec = ChainSpec.from_chain_str(eargs.i or config.get("CHAIN_SPEC"))
|
|
||||||
m = importlib.import_module(f"cic.ext.{eargs.target}.start")
|
chain_spec = ChainSpec.from_chain_str(eargs.i)
|
||||||
m.extension_start(
|
m = importlib.import_module('cic.ext.{}.start'.format(eargs.target))
|
||||||
cn,
|
m.extension_start(cn, registry_address=eargs.registry, chain_spec=chain_spec, rpc_provider=config.get('RPC_PROVIDER'))
|
||||||
registry_address=eargs.registry or config.get("CIC_REGISTRY_ADDRESS"),
|
|
||||||
chain_spec=chain_spec,
|
|
||||||
rpc_provider=config.get("RPC_PROVIDER"),
|
|
||||||
) # TODO add key account address
|
|
||||||
|
@ -3,27 +3,21 @@ import logging
|
|||||||
import os
|
import os
|
||||||
|
|
||||||
# local imports
|
# local imports
|
||||||
from cic.contract.components.proof import Proof
|
from cic import Proof
|
||||||
from cic.contract.components.meta import Meta
|
from cic.meta import Meta
|
||||||
from cic.contract.components.attachment import Attachment
|
from cic.attachment import Attachment
|
||||||
from cic.contract.network import Network
|
from cic.network import Network
|
||||||
from cic.contract.components.token import Token
|
from cic.token import Token
|
||||||
|
|
||||||
logg = logging.getLogger(__name__)
|
logg = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def process_args(argparser):
|
def process_args(argparser):
|
||||||
argparser.add_argument(
|
argparser.add_argument('--target', action='append', type=str, default=[], help='initialize network specification file with target')
|
||||||
"--target",
|
argparser.add_argument('--name', type=str, help='token name')
|
||||||
action="append",
|
argparser.add_argument('--symbol', type=str, help='token symbol')
|
||||||
type=str,
|
argparser.add_argument('--precision', type=str, help='token unit precision')
|
||||||
default=[],
|
argparser.add_argument('directory', help='directory to initialize')
|
||||||
help="initialize network specification file with target",
|
|
||||||
)
|
|
||||||
argparser.add_argument("--name", type=str, help="token name")
|
|
||||||
argparser.add_argument("--symbol", type=str, help="token symbol")
|
|
||||||
argparser.add_argument("--precision", type=str, help="token unit precision")
|
|
||||||
argparser.add_argument("directory", help="directory to initialize")
|
|
||||||
|
|
||||||
|
|
||||||
def validate_args(args):
|
def validate_args(args):
|
||||||
@ -31,13 +25,11 @@ def validate_args(args):
|
|||||||
|
|
||||||
|
|
||||||
def execute(config, eargs):
|
def execute(config, eargs):
|
||||||
logg.info("initializing in {}".format(eargs.directory))
|
logg.info('initializing in {}'.format(eargs.directory))
|
||||||
|
|
||||||
os.makedirs(eargs.directory)
|
os.makedirs(eargs.directory)
|
||||||
|
|
||||||
ct = Token(
|
ct = Token(eargs.directory, name=eargs.name, symbol=eargs.symbol, precision=eargs.precision)
|
||||||
eargs.directory, name=eargs.name, symbol=eargs.symbol, precision=eargs.precision
|
|
||||||
)
|
|
||||||
cp = Proof(eargs.directory)
|
cp = Proof(eargs.directory)
|
||||||
cm = Meta(eargs.directory)
|
cm = Meta(eargs.directory)
|
||||||
ca = Attachment(eargs.directory)
|
ca = Attachment(eargs.directory)
|
||||||
|
@ -1,21 +1,14 @@
|
|||||||
# local imports
|
# local imports
|
||||||
from cic.contract.components.proof import Proof
|
from cic import Proof
|
||||||
from cic.contract.components.meta import Meta
|
from cic.meta import Meta
|
||||||
from cic.contract.components.attachment import Attachment
|
from cic.attachment import Attachment
|
||||||
from cic.contract.network import Network
|
from cic.network import Network
|
||||||
from cic.contract.components.token import Token
|
from cic.token import Token
|
||||||
|
|
||||||
|
|
||||||
def process_args(argparser):
|
def process_args(argparser):
|
||||||
argparser.add_argument("-f", "--file", type=str, help="add file")
|
argparser.add_argument('-f', '--file', type=str, help='add file')
|
||||||
argparser.add_argument(
|
argparser.add_argument('-d', '--directory', type=str, dest='directory', default='.', help='cic data directory')
|
||||||
"-d",
|
|
||||||
"--directory",
|
|
||||||
type=str,
|
|
||||||
dest="directory",
|
|
||||||
default=".",
|
|
||||||
help="cic data directory",
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def validate_args(args):
|
def validate_args(args):
|
||||||
@ -35,12 +28,8 @@ def execute(config, eargs):
|
|||||||
ca.load()
|
ca.load()
|
||||||
cn.load()
|
cn.load()
|
||||||
|
|
||||||
print(
|
print("""[cic.header]
|
||||||
"""[cic.header]
|
version = {}\n""".format(cp.version()))
|
||||||
version = {}\n""".format(
|
|
||||||
cp.version()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
print("[cic.token]\n{}".format(ct))
|
print("[cic.token]\n{}".format(ct))
|
||||||
print("[cic.proof]\n{}".format(cp))
|
print("[cic.proof]\n{}".format(cp))
|
||||||
print("[cic.meta]\n{}".format(cm))
|
print("[cic.meta]\n{}".format(cm))
|
||||||
|
@ -1,102 +0,0 @@
|
|||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
# standard import
|
|
||||||
import logging
|
|
||||||
import os
|
|
||||||
|
|
||||||
from chainlib.cli.config import Config
|
|
||||||
|
|
||||||
# local imports
|
|
||||||
from cic.contract.contract import deploy_contract, generate_contract, load_contract
|
|
||||||
from cic.contract.csv import load_contract_from_csv
|
|
||||||
|
|
||||||
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(
|
|
||||||
"--csv",
|
|
||||||
help="Load Voucher from CSV",
|
|
||||||
)
|
|
||||||
argparser.add_argument(
|
|
||||||
"--target",
|
|
||||||
default="eth",
|
|
||||||
help="Contract 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)",
|
|
||||||
)
|
|
||||||
argparser.add_argument(
|
|
||||||
"-y",
|
|
||||||
type=str,
|
|
||||||
help="Wallet Keystore",
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def validate_args(_args):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def execute(
|
|
||||||
config: Config,
|
|
||||||
eargs,
|
|
||||||
):
|
|
||||||
directory = eargs.path
|
|
||||||
target = eargs.target
|
|
||||||
skip_gen = eargs.skip_gen
|
|
||||||
skip_deploy = eargs.skip_deploy
|
|
||||||
wallet_keystore = eargs.y
|
|
||||||
csv_file = eargs.csv
|
|
||||||
|
|
||||||
if wallet_keystore:
|
|
||||||
config.add(wallet_keystore, "WALLET_KEY_FILE", exists_ok=True)
|
|
||||||
|
|
||||||
if skip_gen:
|
|
||||||
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(f"Meta: {config.get('META_URL')}")
|
|
||||||
print(f"ChainSpec: {config.get('CHAIN_SPEC', contract.network.chain_spec(target))}")
|
|
||||||
print(f"RPC: {config.get('RPC_PROVIDER')}\n")
|
|
||||||
|
|
||||||
if not skip_deploy:
|
|
||||||
ready_to_deploy = input("Are you ready to Deploy? (y/n): ")
|
|
||||||
if ready_to_deploy == "y":
|
|
||||||
deploy_contract(
|
|
||||||
config=config,
|
|
||||||
contract_directory=directory,
|
|
||||||
target=target,
|
|
||||||
)
|
|
||||||
print("Deployed")
|
|
||||||
else:
|
|
||||||
print("Skipping deployment")
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
# execute()
|
|
||||||
print("Not Implemented")
|
|
@ -1,15 +0,0 @@
|
|||||||
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))
|
|
||||||
|
|
@ -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:63380
|
|
||||||
http_origin =
|
|
||||||
auth_token =
|
|
||||||
[rpc]
|
|
||||||
provider = http://localhost:63545
|
|
||||||
|
|
||||||
[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/.config/cic/keystore
|
|
||||||
passphrase =
|
|
||||||
|
|
||||||
[chain]
|
|
||||||
spec = evm:byzantium:8996:bloxberg
|
|
@ -1,28 +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:8000
|
|
||||||
http_origin =
|
|
||||||
auth_token =
|
|
||||||
|
|
||||||
[rpc]
|
|
||||||
provider = http://localhost:8545
|
|
||||||
|
|
||||||
[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:8996:bloxberg
|
|
@ -1,28 +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 = 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
|
|
@ -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 = 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
|
|
@ -1,137 +0,0 @@
|
|||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
# standard imports
|
|
||||||
import os
|
|
||||||
import json
|
|
||||||
import logging
|
|
||||||
|
|
||||||
# external imports
|
|
||||||
from cic_types import MetadataPointer
|
|
||||||
from cic_types.processor import generate_metadata_pointer
|
|
||||||
from hexathon import strip_0x
|
|
||||||
|
|
||||||
# local imports
|
|
||||||
from cic.contract.base import Data, data_dir
|
|
||||||
from cic.utils import object_to_str
|
|
||||||
|
|
||||||
logg = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
class Meta(Data):
|
|
||||||
"""Serialize and publish metadata for token.
|
|
||||||
|
|
||||||
The token metadata is any mutable data that is not part of the initial token proof, but published simultaneously as the token nonetheless.
|
|
||||||
|
|
||||||
:param path: Path to settings directory
|
|
||||||
:type path: str
|
|
||||||
:param writer: Writer interface receiving the output of the processor
|
|
||||||
:type writer: cic.writers.OutputWriter
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(
|
|
||||||
self, path=".", writer=None, name="", location="", country_code="KE", contact={}, interactive=False
|
|
||||||
):
|
|
||||||
super(Meta, self).__init__()
|
|
||||||
self.name = name
|
|
||||||
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")
|
|
||||||
|
|
||||||
if interactive:
|
|
||||||
self.name = input(f"Enter Metadata Name ({self.name}): ") or self.name
|
|
||||||
self.country_code = input(f"Enter Metadata Country Code ({self.country_code}): ") or self.country_code
|
|
||||||
self.location = input(f"Enter Metadata Location ({self.location}): ") or self.location
|
|
||||||
|
|
||||||
adding_contact_info = True
|
|
||||||
contact = {}
|
|
||||||
while adding_contact_info:
|
|
||||||
value = input("Enter Metadata 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
|
|
||||||
self.contact = contact
|
|
||||||
|
|
||||||
def load(self):
|
|
||||||
"""Load metadata from settings."""
|
|
||||||
super(Meta, self).load()
|
|
||||||
|
|
||||||
f = open(self.meta_path, "r", encoding="utf-8")
|
|
||||||
o = json.load(f)
|
|
||||||
f.close()
|
|
||||||
|
|
||||||
self.name = o["name"]
|
|
||||||
self.contact = o["contact"]
|
|
||||||
self.country_code = o["country_code"]
|
|
||||||
self.location = o["location"]
|
|
||||||
self.inited = True
|
|
||||||
|
|
||||||
def start(self):
|
|
||||||
"""Initialize metadata settings from template."""
|
|
||||||
super(Meta, self).start()
|
|
||||||
|
|
||||||
meta_template_file_path = os.path.join(
|
|
||||||
data_dir, f"meta_template_v{self.version()}.json"
|
|
||||||
)
|
|
||||||
|
|
||||||
f = open(meta_template_file_path, encoding="utf-8")
|
|
||||||
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", encoding="utf-8")
|
|
||||||
json.dump(o, f, sort_keys=True, indent="\t")
|
|
||||||
f.close()
|
|
||||||
|
|
||||||
def reference(self, token_address):
|
|
||||||
"""Calculate the mutable reference for the token metadata."""
|
|
||||||
token_address_bytes = bytes.fromhex(strip_0x(token_address))
|
|
||||||
return generate_metadata_pointer(
|
|
||||||
token_address_bytes, MetadataPointer.TOKEN_META
|
|
||||||
)
|
|
||||||
|
|
||||||
def asdict(self):
|
|
||||||
"""Output proof state to dict."""
|
|
||||||
return {
|
|
||||||
"name": self.name,
|
|
||||||
"country_code": self.country_code,
|
|
||||||
"location": self.location,
|
|
||||||
"contact": self.contact,
|
|
||||||
}
|
|
||||||
|
|
||||||
def process(self, token_address=None, token_symbol=None, writer=None):
|
|
||||||
"""Serialize and publish metadata.
|
|
||||||
|
|
||||||
See cic.processor.Processor.process
|
|
||||||
"""
|
|
||||||
if writer is None:
|
|
||||||
writer = self.writer
|
|
||||||
|
|
||||||
v = json.dumps(self.asdict(), separators=(",", ":"))
|
|
||||||
|
|
||||||
token_address_bytes = bytes.fromhex(strip_0x(token_address))
|
|
||||||
k = generate_metadata_pointer(token_address_bytes, MetadataPointer.TOKEN_META)
|
|
||||||
writer.write(k, v.encode("utf-8"))
|
|
||||||
|
|
||||||
token_symbol_bytes = token_symbol.encode("utf-8")
|
|
||||||
k = generate_metadata_pointer(
|
|
||||||
token_symbol_bytes, MetadataPointer.TOKEN_META_SYMBOL
|
|
||||||
)
|
|
||||||
writer.write(k, v.encode("utf-8"))
|
|
||||||
|
|
||||||
return (k, v)
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return object_to_str(self, ["name", "contact", "country_code", "location"])
|
|
||||||
|
|
@ -1,196 +0,0 @@
|
|||||||
# standard imports
|
|
||||||
import json
|
|
||||||
import logging
|
|
||||||
import os
|
|
||||||
import tempfile
|
|
||||||
|
|
||||||
# external imports
|
|
||||||
from hexathon import strip_0x
|
|
||||||
from cic_types import MetadataPointer
|
|
||||||
from cic_types.processor import generate_metadata_pointer
|
|
||||||
|
|
||||||
# local imports
|
|
||||||
from cic.contract.base import Data, data_dir
|
|
||||||
from cic.utils import object_to_str
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
logg = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
class Proof(Data):
|
|
||||||
"""Proof handles the immutable token proof data mapped to the initial token deployment.
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
:param path: Path to settings directory
|
|
||||||
:type path: str
|
|
||||||
:param attachments: List of attachment objects to include in the proof
|
|
||||||
:type attachments: cic.attachment.Attachment
|
|
||||||
:param writer: Writer interface receiving the output of the processor
|
|
||||||
:type writer: cic.writers.OutputWriter
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
path=".",
|
|
||||||
description="",
|
|
||||||
namespace="ge",
|
|
||||||
issuer="",
|
|
||||||
attachments=None,
|
|
||||||
writer=None,
|
|
||||||
interactive=False,
|
|
||||||
):
|
|
||||||
super(Proof, self).__init__()
|
|
||||||
self.proofs = []
|
|
||||||
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.temp_proof_path = tempfile.mkstemp()[1]
|
|
||||||
|
|
||||||
if interactive:
|
|
||||||
self.description = (
|
|
||||||
input(f"Enter Proof Description ({self.description}): ")
|
|
||||||
or self.description
|
|
||||||
)
|
|
||||||
self.namespace = (
|
|
||||||
input(f"Enter Proof Namespace ({self.namespace}): ") or self.namespace
|
|
||||||
)
|
|
||||||
self.issuer = input(f"Enter Proof Issuer ({self.issuer}): ") or self.issuer
|
|
||||||
|
|
||||||
def load(self):
|
|
||||||
"""Load proof data from settings."""
|
|
||||||
super(Proof, self).load()
|
|
||||||
|
|
||||||
f = open(self.proof_path, "r", encoding="utf-8")
|
|
||||||
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"]
|
|
||||||
|
|
||||||
if self.extra_attachments is not None:
|
|
||||||
a = self.extra_attachments.asdict()
|
|
||||||
for k in a.keys():
|
|
||||||
self.attachments[k] = a[k]
|
|
||||||
|
|
||||||
hshs = self.__get_ordered_hashes()
|
|
||||||
self.proofs = list(map(strip_0x, hshs))
|
|
||||||
|
|
||||||
self.inited = True
|
|
||||||
|
|
||||||
def start(self):
|
|
||||||
"""Initialize proof settings from template."""
|
|
||||||
super(Proof, self).start()
|
|
||||||
|
|
||||||
proof_template_file_path = os.path.join(
|
|
||||||
data_dir, f"proof_template_v{self.version()}.json"
|
|
||||||
)
|
|
||||||
|
|
||||||
with open(proof_template_file_path, "r", encoding="utf-8") as f:
|
|
||||||
o = json.load(f)
|
|
||||||
|
|
||||||
o["issuer"] = self.issuer
|
|
||||||
o["description"] = self.description
|
|
||||||
o["namespace"] = self.namespace
|
|
||||||
|
|
||||||
with open(self.proof_path, "w", encoding="utf-8") as f:
|
|
||||||
json.dump(o, f, sort_keys=True, indent="\t")
|
|
||||||
|
|
||||||
def asdict(self):
|
|
||||||
"""Output proof state to dict."""
|
|
||||||
return {
|
|
||||||
"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):
|
|
||||||
ks = list(self.attachments.keys())
|
|
||||||
ks.sort()
|
|
||||||
|
|
||||||
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 root(self):
|
|
||||||
"""Calculate the root digest from the serialized proof object."""
|
|
||||||
v = self.asdict()
|
|
||||||
# b = cbor2.dumps(v)
|
|
||||||
b = json.dumps(v, separators=(",", ":"))
|
|
||||||
|
|
||||||
with open(self.temp_proof_path, "w", encoding="utf-8") as f:
|
|
||||||
f.write(b)
|
|
||||||
|
|
||||||
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
|
|
||||||
"""
|
|
||||||
if writer is None:
|
|
||||||
writer = self.writer
|
|
||||||
|
|
||||||
(k, v) = self.root()
|
|
||||||
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
|
|
||||||
)
|
|
||||||
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)
|
|
||||||
|
|
||||||
# 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()
|
|
||||||
|
|
||||||
# logg.debug('generated proof {} for hashes {}'.format(r_hex, hshs))
|
|
||||||
|
|
||||||
# writer.write(r_hex, hshs_cat)
|
|
||||||
|
|
||||||
o = self.asdict()
|
|
||||||
with open(self.proof_path, "w", encoding="utf-8") as f:
|
|
||||||
json.dump(o, f, sort_keys=True, indent="\t")
|
|
||||||
|
|
||||||
return root_key
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return object_to_str(
|
|
||||||
self, ["description", "issuer", "namespace", "version()", "proofs"]
|
|
||||||
)
|
|
@ -1,124 +0,0 @@
|
|||||||
# standard imports
|
|
||||||
import json
|
|
||||||
import os
|
|
||||||
|
|
||||||
# local imports
|
|
||||||
from cic.contract.base import Data, data_dir
|
|
||||||
from cic.contract.helpers import select_contract
|
|
||||||
|
|
||||||
|
|
||||||
class Token(Data):
|
|
||||||
"""Encapsulates the token data used by the extension to deploy and/or register token and token related applications on chain.
|
|
||||||
|
|
||||||
Token details (name, symbol etc) will be used to initialize the token settings when start is called. If load is called instead, any token detail parameters passed to the constructor will be overwritten by data stored in the settings.
|
|
||||||
|
|
||||||
:param path: Settings directory path
|
|
||||||
:type path: str
|
|
||||||
:param name: Token name
|
|
||||||
:type name: str
|
|
||||||
:param symbol: Token symbol
|
|
||||||
:type symbol: str
|
|
||||||
:param precision: Token value precision (number of decimals)
|
|
||||||
:type precision: int
|
|
||||||
:param supply: Token supply (in smallest precision units)
|
|
||||||
:type supply: int
|
|
||||||
:param code: Bytecode for token chain application
|
|
||||||
:type code: str (hex)
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
path=".",
|
|
||||||
name="Foo Token",
|
|
||||||
symbol="FOO",
|
|
||||||
precision=6,
|
|
||||||
supply=0,
|
|
||||||
code=None,
|
|
||||||
extra_args=[],
|
|
||||||
extra_args_types=[],
|
|
||||||
interactive=False,
|
|
||||||
):
|
|
||||||
super(Token, self).__init__()
|
|
||||||
self.name = name
|
|
||||||
self.symbol = symbol.upper()
|
|
||||||
self.supply = supply
|
|
||||||
self.precision = precision
|
|
||||||
self.code = code
|
|
||||||
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")
|
|
||||||
|
|
||||||
if interactive:
|
|
||||||
contract = select_contract()
|
|
||||||
self.code = contract["bin_path"]
|
|
||||||
self.extra_args = contract["extra_args"]
|
|
||||||
self.extra_args_types = contract["extra_args_types"]
|
|
||||||
|
|
||||||
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 = self.symbol.upper()
|
|
||||||
self.precision = input(f"Enter Token Precision ({self.precision}): ") or self.precision
|
|
||||||
self.supply = input(f"Enter Token Supply ({self.supply}): ") or self.supply
|
|
||||||
|
|
||||||
def load(self):
|
|
||||||
"""Load token data from settings."""
|
|
||||||
super(Token, self).load()
|
|
||||||
|
|
||||||
with open(self.token_path, "r", encoding="utf-8") as f:
|
|
||||||
o = json.load(f)
|
|
||||||
|
|
||||||
self.name = o["name"]
|
|
||||||
self.symbol = o["symbol"].upper()
|
|
||||||
self.precision = o["precision"]
|
|
||||||
self.code = o["code"]
|
|
||||||
self.supply = o["supply"]
|
|
||||||
extras = []
|
|
||||||
extra_types = []
|
|
||||||
token_extras: list = o["extra"]
|
|
||||||
if token_extras:
|
|
||||||
for idx, token_extra in enumerate(token_extras):
|
|
||||||
arg = token_extra.get("arg")
|
|
||||||
arg_type = token_extra.get("arg_type")
|
|
||||||
if arg and arg_type:
|
|
||||||
extras.append(arg)
|
|
||||||
extra_types.append(arg_type)
|
|
||||||
elif (arg and not arg_type) or (not arg and arg_type):
|
|
||||||
raise ValueError(
|
|
||||||
f"Extra contract args must have a 'arg' and 'arg_type', Please check {self.token_path}:extra[{idx}] "
|
|
||||||
)
|
|
||||||
self.extra_args = extras
|
|
||||||
self.extra_args_types = extra_types
|
|
||||||
self.inited = True
|
|
||||||
|
|
||||||
def start(self):
|
|
||||||
"""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, f"token_template_v{self.version()}.json"
|
|
||||||
)
|
|
||||||
with open(token_template_file_path, encoding="utf-8") as f:
|
|
||||||
o = json.load(f)
|
|
||||||
o["name"] = self.name
|
|
||||||
o["symbol"] = self.symbol.upper()
|
|
||||||
o["precision"] = self.precision
|
|
||||||
o["code"] = self.code
|
|
||||||
o["supply"] = self.supply
|
|
||||||
extra = []
|
|
||||||
for idx, extra_arg in enumerate(self.extra_args):
|
|
||||||
extra.append({"arg": extra_arg, "arg_type": self.extra_args_types[idx]})
|
|
||||||
if len(extra) != 0:
|
|
||||||
o["extra"] = extra
|
|
||||||
|
|
||||||
with open(self.token_path, "w", encoding="utf-8") as f:
|
|
||||||
json.dump(o, f, sort_keys=True, indent="\t")
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
s = f"name = {self.name}\n"
|
|
||||||
s += f"symbol = {self.symbol.upper()}\n"
|
|
||||||
s += f"precision = {self.precision}\n"
|
|
||||||
s += f"supply = {self.supply}\n"
|
|
||||||
for idx, extra in enumerate(self.extra_args):
|
|
||||||
s += f"extra_args[{idx}]({self.extra_args_types[idx]}) = {extra}\n"
|
|
||||||
return s
|
|
@ -1,13 +0,0 @@
|
|||||||
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",
|
|
||||||
},
|
|
||||||
]
|
|
@ -1,213 +0,0 @@
|
|||||||
# Standard
|
|
||||||
import importlib
|
|
||||||
import logging
|
|
||||||
import os
|
|
||||||
from typing import List
|
|
||||||
|
|
||||||
# External imports
|
|
||||||
from chainlib.chain import ChainSpec
|
|
||||||
from chainlib.cli.config import Config
|
|
||||||
|
|
||||||
# Local Modules
|
|
||||||
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.helpers import init_writers_from_config
|
|
||||||
from cic.contract.network import Network
|
|
||||||
from cic.contract.processor import ContractProcessor
|
|
||||||
from cic.writers import HTTPWriter, KeyedWriterFactory, MetadataWriter
|
|
||||||
from cic_types.ext.metadata import MetadataRequestsHandler
|
|
||||||
from cic_types.ext.metadata.signer import Signer as MetadataSigner
|
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
class Contract:
|
|
||||||
""" """
|
|
||||||
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
token: Token,
|
|
||||||
proof: Proof,
|
|
||||||
meta: Meta,
|
|
||||||
attachment: Attachment,
|
|
||||||
network: Network,
|
|
||||||
):
|
|
||||||
self.token = token
|
|
||||||
self.proof = proof
|
|
||||||
self.meta = meta
|
|
||||||
self.attachment = attachment
|
|
||||||
self.network = network
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
s = ""
|
|
||||||
s += f"\n[cic.header]\nversion = {self.proof.version()}\n\n"
|
|
||||||
s += f"[cic.token]\n{self.token}\n"
|
|
||||||
s += f"[cic.proof]\n{self.proof}\n"
|
|
||||||
s += f"[cic.meta]\n{self.meta}\n"
|
|
||||||
s += f"[cic.attachment]\n{self.attachment}\n"
|
|
||||||
s += f"[cic.network]\n{self.network}\n"
|
|
||||||
return s
|
|
||||||
|
|
||||||
|
|
||||||
def load_contract(directory) -> Contract:
|
|
||||||
token = Token(path=directory)
|
|
||||||
proof = Proof(path=directory)
|
|
||||||
meta = Meta(path=directory)
|
|
||||||
attachment = Attachment(path=directory)
|
|
||||||
network = Network(directory)
|
|
||||||
|
|
||||||
token.load()
|
|
||||||
proof.load()
|
|
||||||
meta.load()
|
|
||||||
attachment.load()
|
|
||||||
network.load()
|
|
||||||
return Contract(
|
|
||||||
token=token, proof=proof, meta=meta, attachment=attachment, network=network
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def generate_contract(
|
|
||||||
directory: str, targets: List[str], config, interactive=True
|
|
||||||
) -> Contract:
|
|
||||||
os.makedirs(directory)
|
|
||||||
log.info("Generating token")
|
|
||||||
token = Token(directory, interactive=interactive)
|
|
||||||
token.start()
|
|
||||||
|
|
||||||
log.info("Generating proof")
|
|
||||||
proof = Proof(directory, interactive=interactive)
|
|
||||||
proof.start()
|
|
||||||
|
|
||||||
log.info("Generating meta")
|
|
||||||
meta = Meta(directory, interactive=interactive)
|
|
||||||
meta.start()
|
|
||||||
|
|
||||||
log.info("Generating attachment")
|
|
||||||
attachment = Attachment(directory, interactive=interactive)
|
|
||||||
|
|
||||||
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")
|
|
||||||
keys = cmd_mod.list_keys(config, signer_hint)
|
|
||||||
if len(keys) > 1:
|
|
||||||
print("More than one key found, please select one:")
|
|
||||||
for idx, key in enumerate(keys):
|
|
||||||
print(f"{idx} - {key} ")
|
|
||||||
selecting_key = True
|
|
||||||
while selecting_key:
|
|
||||||
idx = int(input("Select key: "))
|
|
||||||
if keys[idx] is not None:
|
|
||||||
key_account_address = keys[idx]
|
|
||||||
selecting_key = False
|
|
||||||
else:
|
|
||||||
print("Invalid key, try again")
|
|
||||||
else:
|
|
||||||
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()
|
|
||||||
|
|
||||||
return Contract(
|
|
||||||
token=token, proof=proof, meta=meta, attachment=attachment, network=network
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def deploy_contract(
|
|
||||||
config: Config,
|
|
||||||
target: str,
|
|
||||||
contract_directory: str,
|
|
||||||
):
|
|
||||||
modname = f"cic.ext.{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
|
|
||||||
|
|
||||||
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:
|
|
||||||
MetadataRequestsHandler.base_url = metadata_endpoint
|
|
||||||
MetadataRequestsHandler.auth_token = metadata_auth_token
|
|
||||||
|
|
||||||
MetadataSigner.gpg_path = "/tmp"
|
|
||||||
MetadataSigner.key_file_path = config.get("AUTH_KEYFILE_PATH")
|
|
||||||
MetadataSigner.gpg_passphrase = config.get("AUTH_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, headers=headers),
|
|
||||||
)
|
|
||||||
cp = Proof(
|
|
||||||
path=contract_directory,
|
|
||||||
attachments=ca,
|
|
||||||
writer=writers["proof"](path=output_writer_path_meta, headers=headers),
|
|
||||||
)
|
|
||||||
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")
|
|
||||||
log.debug(f"using CHAIN_SPEC from config: {chain_spec}")
|
|
||||||
except KeyError:
|
|
||||||
chain_spec = cn.chain_spec
|
|
||||||
config.add(chain_spec, "CHAIN_SPEC", exists_ok=True)
|
|
||||||
log.debug(f"using CHAIN_SPEC: {str(chain_spec)} from network")
|
|
||||||
|
|
||||||
signer_hint = config.get("WALLET_KEY_FILE")
|
|
||||||
(rpc, signer) = cmd_mod.parse_adapter(config, signer_hint)
|
|
||||||
|
|
||||||
target_network_reference = cn.resource(target)
|
|
||||||
chain_spec = cn.chain_spec(target)
|
|
||||||
log.debug(
|
|
||||||
f'found reference {target_network_reference["contents"]} chain spec {chain_spec} for target {target}'
|
|
||||||
)
|
|
||||||
c = getattr(cmd_mod, "new")(
|
|
||||||
chain_spec,
|
|
||||||
target_network_reference["contents"],
|
|
||||||
cp,
|
|
||||||
signer_hint=signer,
|
|
||||||
rpc=rpc,
|
|
||||||
outputs_writer=writers["ext"](path=output_directory),
|
|
||||||
)
|
|
||||||
c.apply_token(ct)
|
|
||||||
|
|
||||||
p = ContractProcessor(proof=cp, attachment=ca, metadata=cm, extensions=[c])
|
|
||||||
p.process()
|
|
@ -1,212 +0,0 @@
|
|||||||
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]
|
|
@ -1,121 +0,0 @@
|
|||||||
# standard imports
|
|
||||||
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
|
|
||||||
from cic.writers import WritersType
|
|
||||||
|
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
# Download File from Url
|
|
||||||
def download_file(url: str, filename=None) -> str:
|
|
||||||
directory = tempfile.gettempdir()
|
|
||||||
filename = filename if filename else url.split("/")[-1]
|
|
||||||
log.debug(f"Downloading {filename}")
|
|
||||||
r = requests.get(url, allow_redirects=True)
|
|
||||||
content_hash = hashlib.md5(r.content).hexdigest()
|
|
||||||
path = os.path.join(directory, content_hash)
|
|
||||||
with open(path, "wb") as f:
|
|
||||||
f.write(r.content)
|
|
||||||
log.debug(f"{filename} downloaded to {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 select_contract():
|
|
||||||
print("Contracts:")
|
|
||||||
print("\t C - Custom (path/url to contract)")
|
|
||||||
for idx, contract in enumerate(CONTRACT_URLS):
|
|
||||||
print(f"\t {idx} - {contract['name']}")
|
|
||||||
|
|
||||||
val = input("Select contract (C,0,1..): ")
|
|
||||||
if val.isdigit() and int(val) < len(CONTRACT_URLS):
|
|
||||||
contract = CONTRACT_URLS[int(val)]
|
|
||||||
bin_path = os.path.abspath(download_file(contract["url"] + ".bin"))
|
|
||||||
json_path = download_file(contract["url"] + ".json")
|
|
||||||
|
|
||||||
elif val == "C":
|
|
||||||
possible_bin_location = input("Enter a path or url to a contract.bin: ")
|
|
||||||
if possible_bin_location.startswith("http"):
|
|
||||||
# possible_bin_location is url
|
|
||||||
bin_path = download_file(possible_bin_location)
|
|
||||||
else:
|
|
||||||
# possible_bin_location is path
|
|
||||||
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
|
|
||||||
else:
|
|
||||||
print("Invalid selection")
|
|
||||||
sys.exit(1)
|
|
||||||
contract_extra_args = []
|
|
||||||
contract_extra_args_types = []
|
|
||||||
|
|
||||||
if os.path.exists(json_path):
|
|
||||||
with open(json_path, encoding="utf-8") as f:
|
|
||||||
json_data = json.load(f)
|
|
||||||
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}): ")
|
|
||||||
contract_extra_args.append(val)
|
|
||||||
if arg_type == "uint128":
|
|
||||||
contract_extra_args_types.append("uint256")
|
|
||||||
else:
|
|
||||||
contract_extra_args_types.append(arg_type)
|
|
||||||
|
|
||||||
return {
|
|
||||||
"bin_path": bin_path,
|
|
||||||
"json_path": json_path,
|
|
||||||
"extra_args": contract_extra_args,
|
|
||||||
"extra_args_types": contract_extra_args_types,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class Writers(TypedDict):
|
|
||||||
meta: Union[WritersType, Callable[..., WritersType]]
|
|
||||||
attachment: Callable[..., WritersType]
|
|
||||||
proof: Callable[..., WritersType]
|
|
||||||
ext: Union[WritersType, Callable[..., WritersType]]
|
|
||||||
|
|
||||||
|
|
||||||
def init_writers_from_config(config) -> Writers:
|
|
||||||
writers = {}
|
|
||||||
writer_keys = ["meta", "attachment", "proof", "ext"]
|
|
||||||
for key in writer_keys:
|
|
||||||
writer_config_name = f"CIC_CORE_{key.upper()}_WRITER"
|
|
||||||
(module_name, attribute_name) = config.get(writer_config_name).rsplit(
|
|
||||||
".", maxsplit=1
|
|
||||||
)
|
|
||||||
mod = importlib.import_module(module_name)
|
|
||||||
writer = getattr(mod, attribute_name)
|
|
||||||
writers[key] = writer
|
|
||||||
|
|
||||||
return Writers(
|
|
||||||
meta=writers["meta"],
|
|
||||||
attachment=writers["attachment"],
|
|
||||||
proof=writers["proof"],
|
|
||||||
ext=writers["ext"],
|
|
||||||
)
|
|
@ -1,18 +1,5 @@
|
|||||||
[cic_core]
|
[cic_core]
|
||||||
meta_writer = cic.writers.KVWriter
|
meta_writer = cic.output.KVWriter
|
||||||
attachment_writer = cic.writers.KVWriter
|
attachment_writer = cic.output.KVWriter
|
||||||
proof_writer = cic.writers.KVWriter
|
proof_writer = cic.output.KVWriter
|
||||||
ext_writer = cic.writers.KVWriter
|
ext_writer = cic.output.KVWriter
|
||||||
|
|
||||||
[cic]
|
|
||||||
registry_address = 0xe3e3431BF25b06166513019Ed7B21598D27d05dC
|
|
||||||
|
|
||||||
[meta]
|
|
||||||
url = https://meta.sarafu.network
|
|
||||||
http_origin =
|
|
||||||
auth_token =
|
|
||||||
[auth]
|
|
||||||
type = gnupg
|
|
||||||
keyfile_path =
|
|
||||||
passphrase =
|
|
||||||
|
|
||||||
|
@ -2,5 +2,6 @@
|
|||||||
"name": "",
|
"name": "",
|
||||||
"location": "",
|
"location": "",
|
||||||
"country_code": "",
|
"country_code": "",
|
||||||
"contact": {}
|
"contact": {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,41 +1,43 @@
|
|||||||
# standard imports
|
# standard imports
|
||||||
|
import logging
|
||||||
import copy
|
import copy
|
||||||
import json
|
import json
|
||||||
import logging
|
|
||||||
|
|
||||||
# external imports
|
# external imports
|
||||||
from chainlib.chain import ChainSpec
|
from chainlib.chain import ChainSpec
|
||||||
from chainlib.eth.address import is_address, to_checksum_address
|
from chainlib.eth.tx import (
|
||||||
|
TxFormat,
|
||||||
|
TxFactory,
|
||||||
|
Tx,
|
||||||
|
receipt,
|
||||||
|
)
|
||||||
from chainlib.eth.connection import RPCConnection
|
from chainlib.eth.connection import RPCConnection
|
||||||
from chainlib.eth.contract import ABIContractEncoder, ABIContractType
|
from chainlib.eth.contract import (
|
||||||
|
ABIContractEncoder,
|
||||||
|
ABIContractType
|
||||||
|
)
|
||||||
from chainlib.eth.gas import OverrideGasOracle
|
from chainlib.eth.gas import OverrideGasOracle
|
||||||
from chainlib.eth.nonce import RPCNonceOracle
|
from chainlib.eth.nonce import RPCNonceOracle
|
||||||
from chainlib.eth.tx import Tx, TxFactory, TxFormat, receipt
|
from chainlib.eth.address import (
|
||||||
|
is_address,
|
||||||
|
to_checksum_address,
|
||||||
|
)
|
||||||
|
from hexathon import add_0x
|
||||||
|
from eth_token_index import TokenUniqueSymbolIndex
|
||||||
from eth_address_declarator import Declarator
|
from eth_address_declarator import Declarator
|
||||||
from eth_address_declarator.declarator import AddressDeclarator
|
from eth_address_declarator.declarator import AddressDeclarator
|
||||||
from eth_token_index import TokenUniqueSymbolIndex
|
|
||||||
from giftable_erc20_token import GiftableToken
|
from giftable_erc20_token import GiftableToken
|
||||||
from hexathon import add_0x, strip_0x
|
|
||||||
|
|
||||||
# local imports
|
# local imports
|
||||||
from cic.ext.eth.rpc import parse_adapter, list_keys
|
from cic.ext.eth.rpc import parse_adapter
|
||||||
from cic.extension import Extension
|
from cic.extension import Extension
|
||||||
|
|
||||||
|
|
||||||
logg = logging.getLogger(__name__)
|
logg = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class CICEth(Extension):
|
class CICEth(Extension):
|
||||||
def __init__(
|
|
||||||
self,
|
def __init__(self, chain_spec, resources, proof, signer=None, rpc=None, outputs_writer=None, fee_oracle=None):
|
||||||
chain_spec,
|
|
||||||
resources,
|
|
||||||
proof,
|
|
||||||
signer=None,
|
|
||||||
rpc=None,
|
|
||||||
outputs_writer=None,
|
|
||||||
fee_oracle=None,
|
|
||||||
):
|
|
||||||
|
|
||||||
"""Implementation for the eth extension.
|
"""Implementation for the eth extension.
|
||||||
|
|
||||||
@ -52,25 +54,19 @@ class CICEth(Extension):
|
|||||||
:param rpc: RPC adapter capable of submitting and querying the chain network node
|
:param rpc: RPC adapter capable of submitting and querying the chain network node
|
||||||
:type rpc: chainlib.connection.RPCConnection
|
:type rpc: chainlib.connection.RPCConnection
|
||||||
:param outputs_writer: Writer interface receiving the output of the processor
|
:param outputs_writer: Writer interface receiving the output of the processor
|
||||||
:type outputs_writer: cic.writers.OutputWriter
|
:type outputs_writer: cic.output.OutputWriter
|
||||||
:param fee_oracle: Fee oracle required by signer
|
:param fee_oracle: Fee oracle required by signer
|
||||||
:type fee_oracle: chainlib.fee.FeeOracle
|
:type fee_oracle: chainlib.fee.FeeOracle
|
||||||
"""
|
"""
|
||||||
super(CICEth, self).__init__(
|
super(CICEth, self).__init__(chain_spec, resources, proof, signer=signer, rpc=rpc, outputs_writer=outputs_writer)
|
||||||
chain_spec,
|
|
||||||
resources,
|
|
||||||
proof,
|
|
||||||
signer=signer,
|
|
||||||
rpc=rpc,
|
|
||||||
outputs_writer=outputs_writer,
|
|
||||||
)
|
|
||||||
self.fee_oracle = fee_oracle
|
self.fee_oracle = fee_oracle
|
||||||
self.tx_format = TxFormat.RAW_ARGS
|
self.tx_format = TxFormat.RAW_ARGS
|
||||||
if self.rpc is not None:
|
if self.rpc != None:
|
||||||
self.tx_format = TxFormat.JSONRPC
|
self.tx_format = TxFormat.JSONRPC
|
||||||
elif self.signer is not None:
|
elif self.signer != None:
|
||||||
self.tx_format = TxFormat.RLP_SIGNED
|
self.tx_format = TxFormat.RLP_SIGNED
|
||||||
|
|
||||||
|
|
||||||
def __detect_arg_type(self, v):
|
def __detect_arg_type(self, v):
|
||||||
typ = None
|
typ = None
|
||||||
try:
|
try:
|
||||||
@ -79,59 +75,59 @@ class CICEth(Extension):
|
|||||||
except TypeError:
|
except TypeError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
if typ is None:
|
if typ == None:
|
||||||
try:
|
try:
|
||||||
vv = strip_0x(v)
|
vv = strip_0x(v)
|
||||||
if is_address(vv):
|
if is_address(vv):
|
||||||
typ = ABIContractType.ADDRESS
|
typ = ABIContractType.ADDRESS
|
||||||
else:
|
else:
|
||||||
typ = ABIContractType.BYTES32
|
typ = ABIContractType.BYTES32
|
||||||
except ValueError:
|
except ValueError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
if typ is None:
|
if typ == None:
|
||||||
try:
|
try:
|
||||||
v.encode("utf-8")
|
v.encode('utf-8')
|
||||||
typ = ABIContractType.STRING
|
typ = ABIContractType.STRING
|
||||||
except ValueError:
|
except ValueError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
if typ is None:
|
if typ == None:
|
||||||
raise ValueError(
|
raise ValueError('cannot automatically determine type for value {}'.format(v))
|
||||||
f"cannot automatically determine type for value {v}"
|
|
||||||
)
|
|
||||||
|
|
||||||
logg.info(f"argument {v} parsed as abi contract type {typ.value}")
|
logg.info('argument {} parsed as abi contract type {}'.format(typ.value))
|
||||||
|
|
||||||
return typ
|
return typ
|
||||||
|
|
||||||
|
|
||||||
def __order_args(self):
|
def __order_args(self):
|
||||||
args = [
|
args = [
|
||||||
self.token_details["name"],
|
self.token_details['name'],
|
||||||
self.token_details["symbol"],
|
self.token_details['symbol'],
|
||||||
self.token_details["precision"],
|
self.token_details['precision'],
|
||||||
]
|
]
|
||||||
args_types = [
|
args_types = [
|
||||||
ABIContractType.STRING.value,
|
ABIContractType.STRING.value,
|
||||||
ABIContractType.STRING.value,
|
ABIContractType.STRING.value,
|
||||||
ABIContractType.UINT256.value,
|
ABIContractType.UINT256.value,
|
||||||
]
|
]
|
||||||
|
|
||||||
for i, x in enumerate(self.token_details["extra"]):
|
for i, x in enumerate(self.token_details['extra']):
|
||||||
args.append(x)
|
args.append(x)
|
||||||
typ = None
|
typ = None
|
||||||
if self.token_details["extra_types"] is not None:
|
if self.token_details['extra_types'] != None:
|
||||||
typ = self.token_details["extra_types"][i]
|
typ = self.token_details['extra_types'][i]
|
||||||
else:
|
else:
|
||||||
typ = self.__detect_arg_type(x)
|
typ = self.__detect_arg_type(x)
|
||||||
args_types.append(typ)
|
args_types.append(typ)
|
||||||
|
|
||||||
positions = self.token_details["positions"]
|
positions = self.token_details['positions']
|
||||||
if positions is None:
|
if positions == None:
|
||||||
positions = list(range(len(args)))
|
positions = list(range(len(args)))
|
||||||
|
|
||||||
return (args, args_types, positions)
|
return (args, args_types, positions)
|
||||||
|
|
||||||
|
|
||||||
def add_outputs(self, k, v):
|
def add_outputs(self, k, v):
|
||||||
"""Adds given key/value pair to outputs array.
|
"""Adds given key/value pair to outputs array.
|
||||||
|
|
||||||
@ -140,9 +136,10 @@ class CICEth(Extension):
|
|||||||
:param v: Output value
|
:param v: Output value
|
||||||
:param v: bytes or str
|
:param v: bytes or str
|
||||||
"""
|
"""
|
||||||
logg.debug(f"adding outputs {k} {v}")
|
logg.debug('adding outputs {} {}'.format(k, v))
|
||||||
self.outputs.append((k, v))
|
self.outputs.append((k, v))
|
||||||
|
|
||||||
|
|
||||||
def get_outputs(self):
|
def get_outputs(self):
|
||||||
"""Get wrapper for outputs captured from processing.
|
"""Get wrapper for outputs captured from processing.
|
||||||
|
|
||||||
@ -151,13 +148,14 @@ class CICEth(Extension):
|
|||||||
"""
|
"""
|
||||||
return self.outputs
|
return self.outputs
|
||||||
|
|
||||||
|
|
||||||
def process_token(self, writer=None):
|
def process_token(self, writer=None):
|
||||||
"""Deploy token, and optionally mint token supply to token deployer account.
|
"""Deploy token, and optionally mint token supply to token deployer account.
|
||||||
|
|
||||||
:param writer: Writer interface receiving the output of the processor step
|
:param writer: Writer interface receiving the output of the processor step
|
||||||
:type writer: cic.writers.OutputWriter
|
:type writer: cic.output.OutputWriter
|
||||||
"""
|
"""
|
||||||
if writer is None:
|
if writer == None:
|
||||||
writer = self.outputs_writer
|
writer = self.outputs_writer
|
||||||
|
|
||||||
(args, args_types, positions) = self.__order_args()
|
(args, args_types, positions) = self.__order_args()
|
||||||
@ -168,186 +166,140 @@ class CICEth(Extension):
|
|||||||
getattr(enc, args_types[i])(args[i])
|
getattr(enc, args_types[i])(args[i])
|
||||||
|
|
||||||
code = enc.get()
|
code = enc.get()
|
||||||
if self.token_code is not None:
|
if self.token_code != None:
|
||||||
code = self.token_code + code
|
code = self.token_code + code
|
||||||
|
|
||||||
logg.debug(f"resource {self.resources}")
|
logg.debug('resource {}'.format(self.resources))
|
||||||
signer_address = add_0x(
|
signer_address = add_0x(to_checksum_address(self.resources['token']['key_account']))
|
||||||
to_checksum_address(self.resources["token"]["key_account"])
|
|
||||||
)
|
|
||||||
nonce_oracle = None
|
nonce_oracle = None
|
||||||
if self.rpc is not None:
|
if self.rpc != None:
|
||||||
nonce_oracle = RPCNonceOracle(signer_address, conn=self.rpc)
|
nonce_oracle = RPCNonceOracle(signer_address, conn=self.rpc)
|
||||||
|
|
||||||
c = TxFactory(
|
c = TxFactory(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle, gas_oracle=self.fee_oracle)
|
||||||
self.chain_spec,
|
|
||||||
signer=self.signer,
|
|
||||||
nonce_oracle=nonce_oracle,
|
|
||||||
gas_oracle=self.fee_oracle,
|
|
||||||
)
|
|
||||||
tx = c.template(signer_address, None, use_nonce=True)
|
tx = c.template(signer_address, None, use_nonce=True)
|
||||||
tx = c.set_code(tx, code)
|
tx = c.set_code(tx, code)
|
||||||
o = c.finalize(tx, self.tx_format)
|
o = c.finalize(tx, self.tx_format)
|
||||||
|
|
||||||
token_address_tx = None
|
token_address_tx = None
|
||||||
r = None
|
r = None
|
||||||
if self.rpc is not None:
|
if self.rpc != None:
|
||||||
r = self.rpc.do(o[1])
|
r = self.rpc.do(o[1])
|
||||||
token_address_tx = r
|
token_address_tx = r
|
||||||
o = self.rpc.wait(r)
|
o = self.rpc.wait(r)
|
||||||
o = Tx.src_normalize(o)
|
o = Tx.src_normalize(o)
|
||||||
self.token_address = o["contract_address"]
|
self.token_address = o['contract_address']
|
||||||
elif self.signer is not None:
|
elif self.signer != None:
|
||||||
r = o[1]
|
r = o[1]
|
||||||
token_address_tx = r
|
token_address_tx = r
|
||||||
|
|
||||||
if r is None:
|
if r == None:
|
||||||
r = code
|
r = code
|
||||||
writer.write("token", r.encode("utf-8"))
|
writer.write('token', r.encode('utf-8'))
|
||||||
writer.write("token_address", self.token_address.encode("utf-8"))
|
writer.write('token_address', self.token_address.encode('utf-8'))
|
||||||
self.add_outputs("token", r)
|
self.add_outputs('token', r)
|
||||||
|
|
||||||
if int(self.token_details["supply"]) > 0:
|
if self.token_details['supply'] > 0:
|
||||||
c = GiftableToken(
|
c = GiftableToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle, gas_oracle=self.fee_oracle)
|
||||||
self.chain_spec,
|
o = c.mint_to(self.token_address, self.resources['token']['key_account'], self.resources['token']['key_account'], self.token_details['supply'])
|
||||||
signer=self.signer,
|
|
||||||
nonce_oracle=nonce_oracle,
|
|
||||||
gas_oracle=self.fee_oracle,
|
|
||||||
)
|
|
||||||
o = c.mint_to(
|
|
||||||
self.token_address,
|
|
||||||
self.resources["token"]["key_account"],
|
|
||||||
self.resources["token"]["key_account"],
|
|
||||||
self.token_details["supply"],
|
|
||||||
)
|
|
||||||
r = None
|
r = None
|
||||||
if self.rpc is not None:
|
if self.rpc != None:
|
||||||
r = self.rpc.do(o[1])
|
r = self.rpc.do(o[1])
|
||||||
self.rpc.wait(r)
|
self.rpc.wait(r)
|
||||||
writer.write("token_supply", r.encode("utf-8"))
|
writer.write('token_supply', r.encode('utf-8'))
|
||||||
elif self.signer is not None:
|
elif self.signer != None:
|
||||||
r = o[1]
|
r = o[1]
|
||||||
writer.write(
|
writer.write('token_supply', json.dumps(r).encode('utf-8'))
|
||||||
"token_supply", json.dumps(r, separators=(",", ":")).encode("utf-8")
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
r = o
|
r = o
|
||||||
writer.write("token_supply", r.encode("utf-8"))
|
writer.write('token_supply', r.encode('utf-8'))
|
||||||
|
|
||||||
return token_address_tx
|
return token_address_tx
|
||||||
|
|
||||||
|
|
||||||
def process_token_index(self, writer=None):
|
def process_token_index(self, writer=None):
|
||||||
"""Register deployed token with token index.
|
"""Register deployed token with token index.
|
||||||
|
|
||||||
:param writer: Writer interface receiving the output of the processor step
|
:param writer: Writer interface receiving the output of the processor step
|
||||||
:type writer: cic.writers.OutputWriter
|
:type writer: cic.output.OutputWriter
|
||||||
"""
|
"""
|
||||||
if writer is None:
|
if writer == None:
|
||||||
writer = self.outputs_writer
|
writer = self.outputs_writer
|
||||||
|
|
||||||
signer_address = add_0x(
|
signer_address = add_0x(to_checksum_address(self.resources['token_index']['key_account']))
|
||||||
to_checksum_address(self.resources["token_index"]["key_account"])
|
contract_address = add_0x(to_checksum_address(self.resources['token_index']['reference']))
|
||||||
)
|
|
||||||
contract_address = add_0x(
|
|
||||||
to_checksum_address(self.resources["token_index"]["reference"])
|
|
||||||
)
|
|
||||||
|
|
||||||
gas_oracle = OverrideGasOracle(
|
gas_oracle = OverrideGasOracle(limit=TokenUniqueSymbolIndex.gas(), conn=self.rpc)
|
||||||
limit=TokenUniqueSymbolIndex.gas(), conn=self.rpc
|
|
||||||
)
|
|
||||||
nonce_oracle = None
|
nonce_oracle = None
|
||||||
if self.rpc is not None:
|
if self.rpc != None:
|
||||||
nonce_oracle = RPCNonceOracle(add_0x(signer_address), conn=self.rpc)
|
nonce_oracle = RPCNonceOracle(add_0x(signer_address), conn=self.rpc)
|
||||||
c = TokenUniqueSymbolIndex(
|
c = TokenUniqueSymbolIndex(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle, gas_oracle=gas_oracle)
|
||||||
self.chain_spec,
|
|
||||||
signer=self.signer,
|
|
||||||
nonce_oracle=nonce_oracle,
|
|
||||||
gas_oracle=gas_oracle,
|
|
||||||
)
|
|
||||||
|
|
||||||
o = c.register(
|
o = c.register(contract_address, signer_address, self.token_address, tx_format=self.tx_format)
|
||||||
contract_address,
|
|
||||||
signer_address,
|
|
||||||
self.token_address,
|
|
||||||
tx_format=self.tx_format,
|
|
||||||
)
|
|
||||||
r = None
|
r = None
|
||||||
if self.rpc is not None:
|
if self.rpc != None:
|
||||||
r = self.rpc.do(o[1])
|
r = self.rpc.do(o[1])
|
||||||
self.rpc.wait(r)
|
self.rpc.wait(r)
|
||||||
elif self.signer is not None:
|
elif self.signer != None:
|
||||||
r = o[1]
|
r = o[1]
|
||||||
else:
|
else:
|
||||||
r = o
|
r = o
|
||||||
|
|
||||||
writer.write("token_index", r.encode("utf-8"))
|
writer.write('token_index', r.encode('utf-8'))
|
||||||
self.add_outputs("token_index", r)
|
self.add_outputs('token_index', r)
|
||||||
return r
|
return r
|
||||||
|
|
||||||
|
|
||||||
def process_address_declarator(self, writer=None):
|
def process_address_declarator(self, writer=None):
|
||||||
"""Register token proofs with address declarator.
|
"""Register token proofs with address declarator.
|
||||||
|
|
||||||
:param writer: Writer interface receiving the output of the processor step
|
:param writer: Writer interface receiving the output of the processor step
|
||||||
:type writer: cic.writers.OutputWriter
|
:type writer: cic.output.OutputWriter
|
||||||
"""
|
"""
|
||||||
if writer is None:
|
if writer == None:
|
||||||
writer = self.outputs_writer
|
writer = self.outputs_writer
|
||||||
|
|
||||||
signer_address = add_0x(
|
signer_address = add_0x(to_checksum_address(self.resources['address_declarator']['key_account']))
|
||||||
to_checksum_address(self.resources["address_declarator"]["key_account"])
|
contract_address = add_0x(to_checksum_address(self.resources['address_declarator']['reference']))
|
||||||
)
|
|
||||||
contract_address = add_0x(
|
|
||||||
to_checksum_address(self.resources["address_declarator"]["reference"])
|
|
||||||
)
|
|
||||||
|
|
||||||
gas_oracle = OverrideGasOracle(limit=AddressDeclarator.gas(), conn=self.rpc)
|
gas_oracle = OverrideGasOracle(limit=AddressDeclarator.gas(), conn=self.rpc)
|
||||||
nonce_oracle = None
|
nonce_oracle = None
|
||||||
if self.rpc is not None:
|
if self.rpc != None:
|
||||||
nonce_oracle = RPCNonceOracle(signer_address, conn=self.rpc)
|
nonce_oracle = RPCNonceOracle(signer_address, conn=self.rpc)
|
||||||
c = Declarator(
|
c = Declarator(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle, gas_oracle=gas_oracle)
|
||||||
self.chain_spec,
|
|
||||||
signer=self.signer,
|
|
||||||
nonce_oracle=nonce_oracle,
|
|
||||||
gas_oracle=gas_oracle,
|
|
||||||
)
|
|
||||||
|
|
||||||
results = []
|
results = []
|
||||||
# (main_proof, all_proofs) = self.proof.get()
|
#(main_proof, all_proofs) = self.proof.get()
|
||||||
|
|
||||||
# for proof in all_proofs:
|
#for proof in all_proofs:
|
||||||
# logg.debug('proof {} '.format(proof))
|
#logg.debug('proof {} '.format(proof))
|
||||||
|
|
||||||
(k, v) = self.proof.root()
|
(k, v) = self.proof.root()
|
||||||
|
|
||||||
fk = "address_declarator_" + k
|
fk = 'address_declarator_' + k
|
||||||
o = c.add_declaration(
|
o = c.add_declaration(contract_address, signer_address, self.token_address, k, tx_format=self.tx_format)
|
||||||
contract_address,
|
|
||||||
signer_address,
|
|
||||||
self.token_address,
|
|
||||||
k,
|
|
||||||
tx_format=self.tx_format,
|
|
||||||
)
|
|
||||||
r = None
|
r = None
|
||||||
if self.rpc is not None:
|
if self.rpc != None:
|
||||||
r = self.rpc.do(o[1])
|
r = self.rpc.do(o[1])
|
||||||
self.rpc.wait(r)
|
self.rpc.wait(r)
|
||||||
elif self.signer is not None:
|
elif self.signer != None:
|
||||||
r = o[1]
|
r = o[1]
|
||||||
else:
|
else:
|
||||||
r = o
|
r = o
|
||||||
self.add_outputs(fk, r)
|
self.add_outputs(fk, r)
|
||||||
results.append(r)
|
results.append(r)
|
||||||
v = r.encode("utf-8")
|
v = r.encode('utf-8')
|
||||||
if writer is not None:
|
if writer != None:
|
||||||
writer.write(fk, v)
|
writer.write(fk, v)
|
||||||
|
|
||||||
return results
|
return results
|
||||||
|
|
||||||
|
|
||||||
def prepare_extension(self):
|
def prepare_extension(self):
|
||||||
"""Sets token address for extension if defined in settings."""
|
"""Sets token address for extension if defined in settings.
|
||||||
|
"""
|
||||||
super(CICEth, self).prepare_extension()
|
super(CICEth, self).prepare_extension()
|
||||||
|
|
||||||
if self.token_address is not None:
|
if self.token_address != None:
|
||||||
self.token_address = add_0x(to_checksum_address(self.token_address))
|
self.token_address = add_0x(to_checksum_address(self.token_address))
|
||||||
|
|
||||||
|
|
||||||
@ -356,11 +308,4 @@ def new(chain_spec, resources, proof, signer_hint=None, rpc=None, outputs_writer
|
|||||||
|
|
||||||
See CICEth constructor for details.
|
See CICEth constructor for details.
|
||||||
"""
|
"""
|
||||||
return CICEth(
|
return CICEth(chain_spec, resources, proof, signer=signer_hint, rpc=rpc, outputs_writer=outputs_writer)
|
||||||
chain_spec,
|
|
||||||
resources,
|
|
||||||
proof,
|
|
||||||
signer=signer_hint,
|
|
||||||
rpc=rpc,
|
|
||||||
outputs_writer=outputs_writer,
|
|
||||||
)
|
|
||||||
|
@ -1,19 +1,17 @@
|
|||||||
# standard imports
|
# standard imports
|
||||||
from getpass import getpass
|
|
||||||
import logging
|
|
||||||
import os
|
|
||||||
import stat
|
import stat
|
||||||
|
import os
|
||||||
|
import logging
|
||||||
|
|
||||||
# external imports
|
# external imports
|
||||||
from funga.eth.keystore.dict import DictKeystore
|
from funga.eth.keystore.dict import DictKeystore
|
||||||
from funga.eth.signer import EIP155Signer
|
from funga.eth.signer import EIP155Signer
|
||||||
from chainlib.cli import Wallet
|
|
||||||
from chainlib.eth.cli import Rpc
|
from chainlib.eth.cli import Rpc
|
||||||
|
from chainlib.cli import Wallet
|
||||||
|
|
||||||
# local imports
|
# local imports
|
||||||
from cic.keystore import KeystoreDirectory
|
from cic.keystore import KeystoreDirectory
|
||||||
|
|
||||||
|
|
||||||
logg = logging.getLogger(__name__)
|
logg = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@ -22,11 +20,9 @@ class EthKeystoreDirectory(DictKeystore, KeystoreDirectory):
|
|||||||
|
|
||||||
TODO: Move to funga
|
TODO: Move to funga
|
||||||
"""
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
def get_passphrase():
|
|
||||||
return getpass('Enter passphrase: ')
|
|
||||||
|
|
||||||
def parse_adapter(config, signer_hint):
|
def parse_adapter(config, signer_hint):
|
||||||
"""Determine and instantiate signer and rpc from configuration.
|
"""Determine and instantiate signer and rpc from configuration.
|
||||||
|
|
||||||
@ -40,14 +36,14 @@ def parse_adapter(config, signer_hint):
|
|||||||
:return: RPC interface, signer interface
|
:return: RPC interface, signer interface
|
||||||
"""
|
"""
|
||||||
keystore = None
|
keystore = None
|
||||||
if signer_hint is None:
|
if signer_hint == None:
|
||||||
logg.info("signer hint missing")
|
logg.info('signer hint missing')
|
||||||
return None
|
return None
|
||||||
st = os.stat(signer_hint)
|
st = os.stat(signer_hint)
|
||||||
if stat.S_ISDIR(st.st_mode):
|
if stat.S_ISDIR(st.st_mode):
|
||||||
logg.debug("signer hint is directory")
|
logg.debug('signer hint is directory')
|
||||||
keystore = EthKeystoreDirectory()
|
keystore = EthKeystoreDirectory()
|
||||||
keystore.process_dir(signer_hint, password_retriever=get_passphrase)
|
keystore.process_dir(signer_hint)
|
||||||
|
|
||||||
w = Wallet(EIP155Signer, keystore=keystore)
|
w = Wallet(EIP155Signer, keystore=keystore)
|
||||||
signer = EIP155Signer(keystore)
|
signer = EIP155Signer(keystore)
|
||||||
@ -55,17 +51,3 @@ def parse_adapter(config, signer_hint):
|
|||||||
rpc.connect_by_config(config)
|
rpc.connect_by_config(config)
|
||||||
|
|
||||||
return (rpc.conn, signer)
|
return (rpc.conn, signer)
|
||||||
|
|
||||||
# TODO Find a better place for this
|
|
||||||
def list_keys(config, signer_hint):
|
|
||||||
keystore = None
|
|
||||||
if signer_hint is None:
|
|
||||||
logg.info("signer hint missing")
|
|
||||||
return None
|
|
||||||
st = os.stat(signer_hint)
|
|
||||||
if stat.S_ISDIR(st.st_mode):
|
|
||||||
logg.debug("signer hint is directory")
|
|
||||||
keystore = EthKeystoreDirectory()
|
|
||||||
keystore.process_dir(signer_hint, default_password=config.get('WALLET_PASSPHRASE', ''), password_retriever=get_passphrase)
|
|
||||||
|
|
||||||
return keystore.list()
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
# external imports
|
# external imports
|
||||||
from chainlib.eth.connection import RPCConnection
|
|
||||||
from cic_eth_registry import CICRegistry
|
from cic_eth_registry import CICRegistry
|
||||||
|
from chainlib.eth.connection import RPCConnection
|
||||||
|
|
||||||
|
|
||||||
def extension_start(network, *args, **kwargs):
|
def extension_start(network, *args, **kwargs):
|
||||||
@ -9,26 +9,18 @@ def extension_start(network, *args, **kwargs):
|
|||||||
:param network: Network object to read and write settings from
|
:param network: Network object to read and write settings from
|
||||||
:type network: cic.network.Network
|
:type network: cic.network.Network
|
||||||
"""
|
"""
|
||||||
CICRegistry.address = kwargs.get("registry_address")
|
CICRegistry.address = kwargs['registry_address']
|
||||||
key_account_address = kwargs.get("key_account_address")
|
|
||||||
RPCConnection.register_location(
|
|
||||||
kwargs.get("rpc_provider"), kwargs.get("chain_spec")
|
|
||||||
)
|
|
||||||
conn = RPCConnection.connect(kwargs.get("chain_spec"))
|
|
||||||
|
|
||||||
registry = CICRegistry(kwargs.get("chain_spec"), conn)
|
RPCConnection.register_location(kwargs['rpc_provider'], kwargs['chain_spec'])
|
||||||
|
conn = RPCConnection.connect(kwargs['chain_spec'])
|
||||||
|
|
||||||
address_declarator = registry.by_name("AddressDeclarator")
|
registry = CICRegistry(kwargs['chain_spec'], conn)
|
||||||
network.resource_set(
|
|
||||||
"eth", "address_declarator", address_declarator, key_account=key_account_address
|
|
||||||
)
|
|
||||||
|
|
||||||
token_index = registry.by_name("TokenRegistry")
|
address_declarator = registry.by_name('AddressDeclarator')
|
||||||
network.resource_set(
|
network.resource_set('eth', 'address_declarator', address_declarator)
|
||||||
"eth", "token_index", token_index, key_account=key_account_address
|
|
||||||
)
|
|
||||||
|
|
||||||
network.resource_set("eth", "token", None, key_account=key_account_address)
|
token_index = registry.by_name('TokenRegistry')
|
||||||
|
network.resource_set('eth', 'token_index', token_index)
|
||||||
|
|
||||||
network.set("eth", kwargs["chain_spec"])
|
network.set('eth', kwargs['chain_spec'])
|
||||||
network.save()
|
network.save()
|
||||||
|
123
cic/extension.py
123
cic/extension.py
@ -5,8 +5,7 @@ import logging
|
|||||||
from hexathon import valid as valid_hex
|
from hexathon import valid as valid_hex
|
||||||
|
|
||||||
# local imports
|
# local imports
|
||||||
from cic.writers import StdoutWriter
|
from cic.output import StdoutWriter
|
||||||
from cic.contract.components.token import Token
|
|
||||||
|
|
||||||
logg = logging.getLogger(__name__)
|
logg = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -25,18 +24,9 @@ class Extension:
|
|||||||
:param rpc: RPC adapter capable of submitting and querying the chain network node
|
:param rpc: RPC adapter capable of submitting and querying the chain network node
|
||||||
:type rpc: chainlib.connection.RPCConnection
|
:type rpc: chainlib.connection.RPCConnection
|
||||||
:param writer: Writer interface receiving the output of the processor
|
:param writer: Writer interface receiving the output of the processor
|
||||||
:type writer: cic.writers.OutputWriter
|
:type writer: cic.output.OutputWriter
|
||||||
"""
|
"""
|
||||||
|
def __init__(self, chain_spec, resources, proof, signer=None, rpc=None, outputs_writer=StdoutWriter()):
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
chain_spec,
|
|
||||||
resources,
|
|
||||||
proof,
|
|
||||||
signer=None,
|
|
||||||
rpc=None,
|
|
||||||
outputs_writer=StdoutWriter(),
|
|
||||||
):
|
|
||||||
self.resources = resources
|
self.resources = resources
|
||||||
self.proof = proof
|
self.proof = proof
|
||||||
self.chain_spec = chain_spec
|
self.chain_spec = chain_spec
|
||||||
@ -48,8 +38,9 @@ class Extension:
|
|||||||
self.outputs = []
|
self.outputs = []
|
||||||
self.outputs_writer = outputs_writer
|
self.outputs_writer = outputs_writer
|
||||||
|
|
||||||
|
|
||||||
# TODO: apply / prepare token can be factored out
|
# TODO: apply / prepare token can be factored out
|
||||||
def apply_token(self, token: Token):
|
def apply_token(self, token):
|
||||||
"""Initialize extension with token data from settings.
|
"""Initialize extension with token data from settings.
|
||||||
|
|
||||||
:param token: Token object
|
:param token: Token object
|
||||||
@ -57,27 +48,10 @@ class Extension:
|
|||||||
:rtype: dict
|
:rtype: dict
|
||||||
:returns: Token data state of extension after load
|
:returns: Token data state of extension after load
|
||||||
"""
|
"""
|
||||||
return self.prepare_token(
|
return self.prepare_token(token.name, token.symbol, token.precision, token.code, token.supply)
|
||||||
token.name,
|
|
||||||
token.symbol,
|
|
||||||
token.precision,
|
|
||||||
token.code,
|
|
||||||
token.supply,
|
|
||||||
token.extra_args,
|
|
||||||
token.extra_args_types,
|
|
||||||
)
|
|
||||||
|
|
||||||
def prepare_token(
|
|
||||||
self,
|
def prepare_token(self, name, symbol, precision, code, supply, extra=[], extra_types=[], positions=None):
|
||||||
name,
|
|
||||||
symbol,
|
|
||||||
precision,
|
|
||||||
code,
|
|
||||||
supply,
|
|
||||||
extra=None,
|
|
||||||
extra_types=None,
|
|
||||||
positions=None,
|
|
||||||
):
|
|
||||||
"""Initialize extension token data.
|
"""Initialize extension token data.
|
||||||
|
|
||||||
:param name: Token name
|
:param name: Token name
|
||||||
@ -100,20 +74,22 @@ class Extension:
|
|||||||
:returns: Token data state of extension after load
|
:returns: Token data state of extension after load
|
||||||
"""
|
"""
|
||||||
self.token_details = {
|
self.token_details = {
|
||||||
"name": name,
|
'name': name,
|
||||||
"symbol": symbol,
|
'symbol': symbol,
|
||||||
"precision": precision,
|
'precision': precision,
|
||||||
"code": code,
|
'code': code,
|
||||||
"supply": supply,
|
'supply': supply,
|
||||||
"extra": extra or [],
|
'extra': extra,
|
||||||
"extra_types": extra_types or [],
|
'extra_types': extra_types,
|
||||||
"positions": positions,
|
'positions': positions,
|
||||||
}
|
}
|
||||||
logg.debug(f"token details: {self.token_details}")
|
|
||||||
return self.token_details
|
return self.token_details
|
||||||
|
|
||||||
|
|
||||||
def prepare_extension(self):
|
def prepare_extension(self):
|
||||||
"""Prepare extension for publishing (noop)"""
|
"""Prepare extension for publishing (noop)
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
def parse_code_as_file(self, v):
|
def parse_code_as_file(self, v):
|
||||||
@ -125,14 +101,17 @@ class Extension:
|
|||||||
:type v: str
|
:type v: str
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
f = open(v, "r", encoding="utf-8")
|
f = open(v, 'r')
|
||||||
r = f.read()
|
r = f.read()
|
||||||
f.close()
|
f.close()
|
||||||
self.parse_code_as_hex(r)
|
self.parse_code_as_hex(r)
|
||||||
except FileNotFoundError as e:
|
except FileNotFoundError as e:
|
||||||
logg.debug(f"could not parse code as file: {e}")
|
logg.debug('could not parse code as file: {}'.format(e))
|
||||||
|
pass
|
||||||
except IsADirectoryError as e:
|
except IsADirectoryError as e:
|
||||||
logg.debug(f"could not parse code as file: {e}")
|
logg.debug('could not parse code as file: {}'.format(e))
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
def parse_code_as_hex(self, v):
|
def parse_code_as_hex(self, v):
|
||||||
"""Helper method to load application bytecode from hex data into extension token data state.
|
"""Helper method to load application bytecode from hex data into extension token data state.
|
||||||
@ -145,7 +124,9 @@ class Extension:
|
|||||||
try:
|
try:
|
||||||
self.token_code = valid_hex(v)
|
self.token_code = valid_hex(v)
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
logg.debug(f"could not parse code as hex: {e}")
|
logg.debug('could not parse code as hex: {}'.format(e))
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
def load_code(self, hint=None):
|
def load_code(self, hint=None):
|
||||||
"""Attempt to load token application bytecode using token settings.
|
"""Attempt to load token application bytecode using token settings.
|
||||||
@ -155,57 +136,57 @@ class Extension:
|
|||||||
:rtype: str (hex)
|
:rtype: str (hex)
|
||||||
:return: Bytecode loaded into extension token data state
|
:return: Bytecode loaded into extension token data state
|
||||||
"""
|
"""
|
||||||
code = self.token_details["code"]
|
code = self.token_details['code']
|
||||||
if hint == "hex":
|
if hint == 'hex':
|
||||||
self.token_code = valid_hex(code)
|
self.token_code = valid_hex(code)
|
||||||
|
|
||||||
for m in [
|
for m in [
|
||||||
self.parse_code_as_hex,
|
self.parse_code_as_hex,
|
||||||
self.parse_code_as_file,
|
self.parse_code_as_file,
|
||||||
]:
|
]:
|
||||||
m(code)
|
m(code)
|
||||||
if self.token_code is not None:
|
if self.token_code != None:
|
||||||
break
|
break
|
||||||
|
|
||||||
if self.token_code is None:
|
if self.token_code == None:
|
||||||
raise RuntimeError("could not successfully parse token code")
|
raise RuntimeError('could not successfully parse token code')
|
||||||
|
|
||||||
return self.token_code
|
return self.token_code
|
||||||
|
|
||||||
|
|
||||||
def process(self, writer=None):
|
def process(self, writer=None):
|
||||||
"""Adapter used by Processor to process the extensions implementing the Extension base class.
|
"""Adapter used by Processor to process the extensions implementing the Extension base class.
|
||||||
|
|
||||||
Requires either token address or a valid token code reference to have been included in settings.
|
Requires either token address or a valid token code reference to have been included in settings. If token address is not set, the token application code will be deployed.
|
||||||
If token address is not set, the token application code will be deployed.
|
|
||||||
|
|
||||||
:param writer: Writer to use for publishing.
|
:param writer: Writer to use for publishing.
|
||||||
:type writer: cic.writers.OutputWriter
|
:type writer: cic.output.OutputWriter
|
||||||
:rtype: tuple
|
:rtype: tuple
|
||||||
:return: Token address, token symbol
|
:return: Token address, token symbol
|
||||||
"""
|
"""
|
||||||
if writer is None:
|
if writer == None:
|
||||||
writer = self.outputs_writer
|
writer = self.outputs_writer
|
||||||
|
|
||||||
tasks = []
|
tasks = []
|
||||||
self.token_address = self.resources["token"]["reference"]
|
self.token_address = self.resources['token']['reference']
|
||||||
|
|
||||||
# TODO: get token details when token address is not none
|
# TODO: get token details when token address is not none
|
||||||
if self.token_address is None:
|
if self.token_address == None:
|
||||||
if self.token_details["code"] is None:
|
if self.token_details['code'] == None:
|
||||||
raise RuntimeError("neither token address nor token code has been set")
|
raise RuntimeError('neither token address nor token code has been set')
|
||||||
self.load_code()
|
self.load_code()
|
||||||
tasks.append("token")
|
tasks.append('token')
|
||||||
|
|
||||||
for k in self.resources.keys():
|
for k in self.resources.keys():
|
||||||
if k == "token":
|
if k == 'token':
|
||||||
continue
|
continue
|
||||||
if self.resources[k]["reference"] is not None:
|
if self.resources[k]['reference'] != None:
|
||||||
tasks.append(k)
|
tasks.append(k)
|
||||||
|
|
||||||
self.prepare_extension()
|
self.prepare_extension()
|
||||||
|
|
||||||
for task in tasks:
|
for task in tasks:
|
||||||
logg.debug(f"extension adapter process {task}")
|
logg.debug('extension adapter process {}'.format(task))
|
||||||
_r = getattr(self, "process_" + task)(writer=writer)
|
r = getattr(self, 'process_' + task)(writer=writer)
|
||||||
|
|
||||||
return (self.token_address, self.token_details.get("symbol"))
|
return (self.token_address, self.token_details.get('symbol'))
|
||||||
|
10
cic/hash.py
Normal file
10
cic/hash.py
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
class Hasher:
|
||||||
|
|
||||||
|
def __basehasher(self, v):
|
||||||
|
h = hashlib.sha256()
|
||||||
|
h.update(v)
|
||||||
|
return h.digest()
|
||||||
|
|
||||||
|
|
||||||
|
def hash(self, v):
|
||||||
|
return self.__basehasher(v)
|
@ -23,9 +23,9 @@ class KeystoreDirectory(Keystore):
|
|||||||
except IsADirectoryError:
|
except IsADirectoryError:
|
||||||
pass
|
pass
|
||||||
except KeyfileError as e:
|
except KeyfileError as e:
|
||||||
logg.warning(f'file {fp} could not be parsed as keyfile: {e}')
|
logg.warning('file {} could not be parsed as keyfile: {}'.format(fp, e))
|
||||||
except DecryptError as e:
|
except DecryptError as e:
|
||||||
if password_retriever is None:
|
if password_retriever == None:
|
||||||
raise e
|
raise e
|
||||||
password = password_retriever()
|
password = password_retriever()
|
||||||
self.import_keystore_file(fp, password=password)
|
self.import_keystore_file(fp, password=password)
|
||||||
|
141
cic/meta.py
Normal file
141
cic/meta.py
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
# standard imports
|
||||||
|
import os
|
||||||
|
import json
|
||||||
|
import logging
|
||||||
|
import base64
|
||||||
|
|
||||||
|
# external imports
|
||||||
|
from cic_types import MetadataPointer
|
||||||
|
from cic_types.processor import generate_metadata_pointer
|
||||||
|
from cic_types.ext.metadata import MetadataRequestsHandler
|
||||||
|
from hexathon import strip_0x
|
||||||
|
|
||||||
|
# local imports
|
||||||
|
from .base import (
|
||||||
|
Data,
|
||||||
|
data_dir,
|
||||||
|
)
|
||||||
|
from cic.output import OutputWriter
|
||||||
|
logg = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class Meta(Data):
|
||||||
|
"""Serialize and publish metadata for token.
|
||||||
|
|
||||||
|
The token metadata is any mutable data that is not part of the initial token proof, but published simultaneously as the token nonetheless.
|
||||||
|
|
||||||
|
:param path: Path to settings directory
|
||||||
|
:type path: str
|
||||||
|
:param writer: Writer interface receiving the output of the processor
|
||||||
|
:type writer: cic.output.OutputWriter
|
||||||
|
"""
|
||||||
|
def __init__(self, path='.', writer=None):
|
||||||
|
super(Meta, self).__init__()
|
||||||
|
self.name = None
|
||||||
|
self.contact = {}
|
||||||
|
self.path = path
|
||||||
|
self.writer = writer
|
||||||
|
self.meta_path = os.path.join(self.path, 'meta.json')
|
||||||
|
|
||||||
|
|
||||||
|
def load(self):
|
||||||
|
"""Load metadata from settings.
|
||||||
|
"""
|
||||||
|
super(Meta, self).load()
|
||||||
|
|
||||||
|
f = open(self.meta_path, 'r')
|
||||||
|
o = json.load(f)
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
self.name = o['name']
|
||||||
|
self.contact = o['contact']
|
||||||
|
|
||||||
|
self.inited = True
|
||||||
|
|
||||||
|
|
||||||
|
def start(self):
|
||||||
|
"""Initialize metadata settings from template.
|
||||||
|
"""
|
||||||
|
super(Meta, self).start()
|
||||||
|
|
||||||
|
meta_template_file_path = os.path.join(data_dir, 'meta_template_v{}.json'.format(self.version()))
|
||||||
|
|
||||||
|
f = open(meta_template_file_path)
|
||||||
|
o = json.load(f)
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
f = open(self.meta_path, 'w')
|
||||||
|
json.dump(o, f, sort_keys=True, indent="\t")
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
|
||||||
|
def reference(self, token_address):
|
||||||
|
"""Calculate the mutable reference for the token metadata.
|
||||||
|
"""
|
||||||
|
token_address_bytes = bytes.fromhex(strip_0x(token_address))
|
||||||
|
return generate_metadata_pointer(token_address_bytes, MetadataPointer.TOKEN_META)
|
||||||
|
|
||||||
|
|
||||||
|
def asdict(self):
|
||||||
|
"""Output proof state to dict.
|
||||||
|
"""
|
||||||
|
return {
|
||||||
|
'name': self.name,
|
||||||
|
'contact': self.contact,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def process(self, token_address=None, token_symbol=None, writer=None):
|
||||||
|
"""Serialize and publish metadata.
|
||||||
|
|
||||||
|
See cic.processor.Processor.process
|
||||||
|
"""
|
||||||
|
if writer == None:
|
||||||
|
writer = self.writer
|
||||||
|
|
||||||
|
v = json.dumps(self.asdict())
|
||||||
|
|
||||||
|
token_address_bytes = bytes.fromhex(strip_0x(token_address))
|
||||||
|
k = generate_metadata_pointer(token_address_bytes, MetadataPointer.TOKEN_META)
|
||||||
|
writer.write(k, v.encode('utf-8'))
|
||||||
|
|
||||||
|
token_symbol_bytes = token_symbol.encode('utf-8')
|
||||||
|
k = generate_metadata_pointer(token_symbol_bytes, MetadataPointer.TOKEN_META_SYMBOL)
|
||||||
|
writer.write(k, v.encode('utf-8'))
|
||||||
|
|
||||||
|
return (k, v)
|
||||||
|
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
s = "contact.name = {}\n".format(self.name)
|
||||||
|
|
||||||
|
for k in self.contact.keys():
|
||||||
|
if self.contact[k] == '':
|
||||||
|
continue
|
||||||
|
s += "contact.{} = {}\n".format(k.lower(), self.contact[k])
|
||||||
|
|
||||||
|
return s
|
||||||
|
|
||||||
|
|
||||||
|
class MetadataWriter(OutputWriter):
|
||||||
|
"""Custom writer for publishing data under immutable content-addressed pointers in the cic-meta storage backend.
|
||||||
|
|
||||||
|
Data that is not utf-8 will be converted to base64 before publishing.
|
||||||
|
|
||||||
|
Implements cic.output.OutputWriter
|
||||||
|
"""
|
||||||
|
|
||||||
|
def write(self, k, v):
|
||||||
|
rq = MetadataRequestsHandler(MetadataPointer.NONE, bytes.fromhex(k))
|
||||||
|
try:
|
||||||
|
v = v.decode('utf-8')
|
||||||
|
v = json.loads(v)
|
||||||
|
logg.debug('metadatawriter bindecode {} {}'.format(k, v))
|
||||||
|
except UnicodeDecodeError:
|
||||||
|
v = base64.b64encode(v).decode('utf-8')
|
||||||
|
v = json.loads(json.dumps(v))
|
||||||
|
logg.debug('metadatawriter b64encode {} {}'.format(k, v))
|
||||||
|
r = rq.create(v)
|
||||||
|
logg.info('metadata submitted at {}'.format(k))
|
||||||
|
return r
|
||||||
|
|
@ -1,12 +1,16 @@
|
|||||||
# standard imports
|
# standard imports
|
||||||
|
import os
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import os
|
|
||||||
|
|
||||||
# external imports
|
# external imports
|
||||||
from chainlib.chain import ChainSpec
|
from chainlib.chain import ChainSpec
|
||||||
|
|
||||||
# local imports
|
# local imports
|
||||||
from cic.contract.base import Data, data_dir
|
from .base import (
|
||||||
|
Data,
|
||||||
|
data_dir,
|
||||||
|
)
|
||||||
|
|
||||||
logg = logging.getLogger(__name__)
|
logg = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -34,8 +38,9 @@ class Network(Data):
|
|||||||
"""
|
"""
|
||||||
super(Network, self).load()
|
super(Network, self).load()
|
||||||
|
|
||||||
with open(self.network_path, 'r', encoding='utf-8') as f:
|
f = open(self.network_path, 'r')
|
||||||
o = json.load(f)
|
o = json.load(f)
|
||||||
|
f.close()
|
||||||
|
|
||||||
self.resources = o['resources']
|
self.resources = o['resources']
|
||||||
|
|
||||||
@ -49,10 +54,11 @@ class Network(Data):
|
|||||||
"""
|
"""
|
||||||
super(Network, self).load()
|
super(Network, self).load()
|
||||||
|
|
||||||
network_template_file_path = os.path.join(data_dir, f'network_template_v{self.version()}.json')
|
network_template_file_path = os.path.join(data_dir, 'network_template_v{}.json'.format(self.version()))
|
||||||
|
|
||||||
with open(network_template_file_path, encoding='utf-8') as f:
|
f = open(network_template_file_path)
|
||||||
o_part = json.load(f)
|
o_part = json.load(f)
|
||||||
|
f.close()
|
||||||
|
|
||||||
self.resources = {}
|
self.resources = {}
|
||||||
for v in self.targets:
|
for v in self.targets:
|
||||||
@ -64,10 +70,11 @@ class Network(Data):
|
|||||||
def save(self):
|
def save(self):
|
||||||
"""Save network settings to file.
|
"""Save network settings to file.
|
||||||
"""
|
"""
|
||||||
with open(self.network_path, 'w', encoding='utf-8') as f:
|
f = open(self.network_path, 'w')
|
||||||
json.dump({
|
json.dump({
|
||||||
'resources': self.resources,
|
'resources': self.resources,
|
||||||
}, f, sort_keys=True, indent="\t")
|
}, f, sort_keys=True, indent="\t")
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
|
||||||
def resource(self, k):
|
def resource(self, k):
|
||||||
@ -79,8 +86,8 @@ class Network(Data):
|
|||||||
:return: Extension settings
|
:return: Extension settings
|
||||||
"""
|
"""
|
||||||
v = self.resources.get(k)
|
v = self.resources.get(k)
|
||||||
if v is None:
|
if v == None:
|
||||||
raise AttributeError(f'No defined reference for {k}')
|
raise AttributeError('no defined reference for {}'.format(k))
|
||||||
return v
|
return v
|
||||||
|
|
||||||
|
|
||||||
@ -125,19 +132,17 @@ class Network(Data):
|
|||||||
"""
|
"""
|
||||||
chain_spec_dict = chain_spec.asdict()
|
chain_spec_dict = chain_spec.asdict()
|
||||||
for k in chain_spec_dict.keys():
|
for k in chain_spec_dict.keys():
|
||||||
logg.debug(f'resources: {self.resources}')
|
logg.debug('resources {}'.format(self.resources))
|
||||||
self.resources[resource_key]['chain_spec'][k] = chain_spec_dict[k]
|
self.resources[resource_key]['chain_spec'][k] = chain_spec_dict[k]
|
||||||
|
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
s = ''
|
s = ''
|
||||||
for resource in self.resources.keys():
|
for k in self.resources.keys():
|
||||||
chainspec = ChainSpec.from_dict(self.resources[resource]['chain_spec'])
|
for kk in self.resources[k]['contents'].keys():
|
||||||
s += f'{resource}.chain_spec: {str(chainspec)}\n'
|
v = self.resources[k]['contents'][kk]
|
||||||
for content_key in self.resources[resource]['contents'].keys():
|
if v == None:
|
||||||
content_value = self.resources[resource]['contents'][content_key]
|
v = ''
|
||||||
if content_value is None:
|
s += '{}.{} = {}\n'.format(k, kk, v)
|
||||||
content_value = ''
|
|
||||||
s += f'{resource}.contents.{content_key} = {json.dumps(content_value, indent=4, sort_keys=True)}\n'
|
|
||||||
|
|
||||||
return s
|
return s
|
95
cic/output.py
Normal file
95
cic/output.py
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
# standard imports
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import logging
|
||||||
|
import urllib.request
|
||||||
|
|
||||||
|
logg = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class OutputWriter:
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def write(self, k, v):
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
|
||||||
|
class StdoutWriter(OutputWriter):
|
||||||
|
|
||||||
|
def write(self, k, v):
|
||||||
|
sys.stdout.write('{}\t{}\n'.format(k, v))
|
||||||
|
|
||||||
|
|
||||||
|
class KVWriter(OutputWriter):
|
||||||
|
|
||||||
|
def __init__(self, path=None, *args, **kwargs):
|
||||||
|
try:
|
||||||
|
os.stat(path)
|
||||||
|
except FileNotFoundError:
|
||||||
|
os.makedirs(path)
|
||||||
|
self.path = path
|
||||||
|
|
||||||
|
|
||||||
|
def write(self, k, v):
|
||||||
|
fp = os.path.join(self.path, str(k))
|
||||||
|
logg.debug('path write {} {}'.format(fp, str(v)))
|
||||||
|
f = open(fp, 'wb')
|
||||||
|
f.write(v)
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
|
||||||
|
class HTTPWriter(OutputWriter):
|
||||||
|
|
||||||
|
def __init__(self, path=None, *args, **kwargs):
|
||||||
|
super(HTTPWriter, self).__init__(*args, **kwargs)
|
||||||
|
self.path = path
|
||||||
|
|
||||||
|
|
||||||
|
def write(self, k, v):
|
||||||
|
path = self.path
|
||||||
|
if k != None:
|
||||||
|
path = os.path.join(path, k)
|
||||||
|
logg.debug('http writer post {}'.format(path))
|
||||||
|
rq = urllib.request.Request(path, method='POST', data=v)
|
||||||
|
r = urllib.request.urlopen(rq)
|
||||||
|
logg.info('http writer submitted at {}'.format(r.read()))
|
||||||
|
|
||||||
|
|
||||||
|
class KeyedWriter(OutputWriter):
|
||||||
|
|
||||||
|
def __init__(self, writer_keyed, writer_immutable):
|
||||||
|
self.writer_keyed = writer_keyed
|
||||||
|
self.writer_immutable = writer_immutable
|
||||||
|
|
||||||
|
|
||||||
|
def write(self, k, v):
|
||||||
|
logg.debug('writing keywriter {} {}'.format(k, v))
|
||||||
|
if isinstance(v, str):
|
||||||
|
v = v.encode('utf-8')
|
||||||
|
if self.writer_keyed != None:
|
||||||
|
self.writer_keyed.write(k, v)
|
||||||
|
if self.writer_immutable != None:
|
||||||
|
self.writer_immutable.write(None, v)
|
||||||
|
|
||||||
|
|
||||||
|
class KeyedWriterFactory:
|
||||||
|
|
||||||
|
def __init__(self, key_writer_constructor, immutable_writer_constructor, *args, **kwargs):
|
||||||
|
self.key_writer_constructor = key_writer_constructor
|
||||||
|
self.immutable_writer_constructor = immutable_writer_constructor
|
||||||
|
self.x = {}
|
||||||
|
for k in kwargs.keys():
|
||||||
|
logg.debug('adding key {} t keyed writer factory'.format(k))
|
||||||
|
self.x[k] = kwargs[k]
|
||||||
|
|
||||||
|
|
||||||
|
def new(self, path=None, *args, **kwargs):
|
||||||
|
writer_keyed = None
|
||||||
|
writer_immutable = None
|
||||||
|
if self.key_writer_constructor != None:
|
||||||
|
writer_keyed = self.key_writer_constructor(path, **self.x)
|
||||||
|
if self.immutable_writer_constructor != None:
|
||||||
|
writer_immutable = self.immutable_writer_constructor(path, **self.x)
|
||||||
|
return KeyedWriter(writer_keyed, writer_immutable)
|
@ -4,7 +4,7 @@ import logging
|
|||||||
logg = logging.getLogger(__name__)
|
logg = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class ContractProcessor:
|
class Processor:
|
||||||
"""Drives the serialization and publishing of contracts, proofs and metadata for the token.
|
"""Drives the serialization and publishing of contracts, proofs and metadata for the token.
|
||||||
|
|
||||||
:param proof: Proof object to publish
|
:param proof: Proof object to publish
|
||||||
@ -14,37 +14,31 @@ class ContractProcessor:
|
|||||||
:param metadata: Metadata object to publish
|
:param metadata: Metadata object to publish
|
||||||
:type metadata: cic.meta.Meta
|
:type metadata: cic.meta.Meta
|
||||||
:param writer: Writer interface receiving the output of the processor
|
:param writer: Writer interface receiving the output of the processor
|
||||||
:type writer: cic.writers.OutputWriter
|
:type writer: cic.output.OutputWriter
|
||||||
:param extensions: Extension contexts to publish to
|
:param extensions: Extension contexts to publish to
|
||||||
:type extensions: list of cic.extension.Extension
|
:type extensions: list of cic.extension.Extension
|
||||||
"""
|
"""
|
||||||
|
def __init__(self, proof=None, attachment=None, metadata=None, outputs_writer=None, extensions=[]):
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
proof=None,
|
|
||||||
attachment=None,
|
|
||||||
metadata=None,
|
|
||||||
outputs_writer=None,
|
|
||||||
extensions=[],
|
|
||||||
):
|
|
||||||
self.token_address = None
|
self.token_address = None
|
||||||
self.extensions = extensions
|
self.extensions = extensions
|
||||||
self.cores = {
|
self.cores = {
|
||||||
"metadata": metadata,
|
'metadata': metadata,
|
||||||
"attachment": attachment,
|
'attachment': attachment,
|
||||||
"proof": proof,
|
'proof': proof,
|
||||||
}
|
}
|
||||||
self.outputs = []
|
self.outputs = []
|
||||||
self.__outputs_writer = outputs_writer
|
self.__outputs_writer = outputs_writer
|
||||||
|
|
||||||
|
|
||||||
def writer(self):
|
def writer(self):
|
||||||
"""Return the writer instance that the process is using.
|
"""Return the writer instance that the process is using.
|
||||||
|
|
||||||
:rtype: cic.writers.OutputWriter
|
:rtype: cic.output.OutputWriter
|
||||||
:return: Writer
|
:return: Writer
|
||||||
"""
|
"""
|
||||||
return self.__outputs_writer
|
return self.__outputs_writer
|
||||||
|
|
||||||
|
|
||||||
def get_outputs(self):
|
def get_outputs(self):
|
||||||
"""Return all written outputs.
|
"""Return all written outputs.
|
||||||
|
|
||||||
@ -59,6 +53,7 @@ class ContractProcessor:
|
|||||||
outputs += self.outputs
|
outputs += self.outputs
|
||||||
return outputs
|
return outputs
|
||||||
|
|
||||||
|
|
||||||
def process(self, writer=None):
|
def process(self, writer=None):
|
||||||
"""Serializes and publishes all token data.
|
"""Serializes and publishes all token data.
|
||||||
|
|
||||||
@ -67,27 +62,22 @@ class ContractProcessor:
|
|||||||
All output written to the publish writer will also be cached so that it subsequently be recalled using the get_outputs method.
|
All output written to the publish writer will also be cached so that it subsequently be recalled using the get_outputs method.
|
||||||
|
|
||||||
:param writer: Writer to use for publishing.
|
:param writer: Writer to use for publishing.
|
||||||
:type writer: cic.writers.OutputWriter
|
:type writer: cic.output.OutputWriter
|
||||||
"""
|
"""
|
||||||
|
|
||||||
tasks = [
|
tasks = [
|
||||||
"attachment",
|
'attachment',
|
||||||
"proof",
|
'proof',
|
||||||
"metadata",
|
'metadata',
|
||||||
]
|
]
|
||||||
|
|
||||||
for ext in self.extensions:
|
for ext in self.extensions:
|
||||||
(token_address, token_symbol) = ext.process()
|
(token_address, token_symbol) = ext.process()
|
||||||
|
|
||||||
for task in tasks:
|
for task in tasks:
|
||||||
a = self.cores.get(task)
|
a = self.cores.get(task)
|
||||||
if a is None:
|
if a == None:
|
||||||
logg.debug(f'skipping missing task receiver "{task}"')
|
logg.debug('skipping missing task receiver "{}"'.format(task))
|
||||||
continue
|
continue
|
||||||
logg.debug(f'Processing "{ext}:{task}"')
|
v = a.process(token_address=token_address, token_symbol=token_symbol, writer=self.__outputs_writer)
|
||||||
v = a.process(
|
|
||||||
token_address=token_address,
|
|
||||||
token_symbol=token_symbol,
|
|
||||||
writer=self.__outputs_writer,
|
|
||||||
)
|
|
||||||
self.outputs.append(v)
|
self.outputs.append(v)
|
181
cic/proof.py
Normal file
181
cic/proof.py
Normal file
@ -0,0 +1,181 @@
|
|||||||
|
# standard imports
|
||||||
|
import os
|
||||||
|
import json
|
||||||
|
import logging
|
||||||
|
import tempfile
|
||||||
|
import cbor2
|
||||||
|
|
||||||
|
# external imports
|
||||||
|
from hexathon import strip_0x
|
||||||
|
from cic_types import MetadataPointer
|
||||||
|
from cic_types.processor import generate_metadata_pointer
|
||||||
|
from cic_types.ext.metadata import MetadataRequestsHandler
|
||||||
|
|
||||||
|
# local imports
|
||||||
|
from .base import *
|
||||||
|
from cic.output import OutputWriter
|
||||||
|
|
||||||
|
logg = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class Proof(Data):
|
||||||
|
"""Proof handles the immutable token proof data mapped to the initial token deployment.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
:param path: Path to settings directory
|
||||||
|
:type path: str
|
||||||
|
:param attachments: List of attachment objects to include in the proof
|
||||||
|
:type attachments: cic.attachment.Attachment
|
||||||
|
:param writer: Writer interface receiving the output of the processor
|
||||||
|
:type writer: cic.output.OutputWriter
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, path='.', attachments=None, writer=None):
|
||||||
|
super(Proof, self).__init__()
|
||||||
|
self.proofs = []
|
||||||
|
self.namespace = 'ge'
|
||||||
|
self.description = None
|
||||||
|
self.issuer = None
|
||||||
|
self.path = path
|
||||||
|
self.writer = writer
|
||||||
|
self.extra_attachments = attachments
|
||||||
|
self.attachments = {}
|
||||||
|
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.
|
||||||
|
"""
|
||||||
|
super(Proof, self).load()
|
||||||
|
|
||||||
|
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']
|
||||||
|
|
||||||
|
if self.extra_attachments != None:
|
||||||
|
a = self.extra_attachments.asdict()
|
||||||
|
for k in a.keys():
|
||||||
|
self.attachments[k] = a[k]
|
||||||
|
|
||||||
|
hshs = self.__get_ordered_hashes()
|
||||||
|
self.proofs = list(map(strip_0x, hshs))
|
||||||
|
|
||||||
|
self.inited = True
|
||||||
|
|
||||||
|
|
||||||
|
def start(self):
|
||||||
|
"""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()))
|
||||||
|
|
||||||
|
f = open(proof_template_file_path)
|
||||||
|
o = json.load(f)
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
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.
|
||||||
|
"""
|
||||||
|
return {
|
||||||
|
'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):
|
||||||
|
ks = list(self.attachments.keys())
|
||||||
|
ks.sort()
|
||||||
|
|
||||||
|
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 root(self):
|
||||||
|
"""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.write(b)
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
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
|
||||||
|
"""
|
||||||
|
if writer == None:
|
||||||
|
writer = self.writer
|
||||||
|
|
||||||
|
(k, v) = self.root()
|
||||||
|
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)
|
||||||
|
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)
|
||||||
|
|
||||||
|
# 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()
|
||||||
|
|
||||||
|
#logg.debug('generated proof {} for hashes {}'.format(r_hex, hshs))
|
||||||
|
|
||||||
|
#writer.write(r_hex, hshs_cat)
|
||||||
|
|
||||||
|
o = self.asdict()
|
||||||
|
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)
|
@ -1,97 +1,63 @@
|
|||||||
# standard imports
|
# standard imports
|
||||||
import importlib
|
|
||||||
import logging
|
|
||||||
import os
|
import os
|
||||||
|
import logging
|
||||||
|
import argparse
|
||||||
import sys
|
import sys
|
||||||
|
import importlib
|
||||||
|
|
||||||
# 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
|
|
||||||
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.wizard as cmd_wizard
|
import cic.cmd.ext as cmd_ext
|
||||||
from cic.config import ensure_base_configs
|
import cic.cmd.export as cmd_export
|
||||||
|
|
||||||
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, arg_flags=arg_flags, description='CIC cli tool for generating and publishing tokens')
|
||||||
env=os.environ,
|
|
||||||
arg_flags=arg_flags,
|
|
||||||
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"
|
|
||||||
)
|
|
||||||
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 == 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)
|
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
modname = f"cic.cmd.{args.command}"
|
modname = 'cic.cmd.{}'.format(args.command)
|
||||||
logg.debug(f"using module {modname}")
|
logg.debug('using module {}'.format(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:
|
||||||
logg.exception(e)
|
logg.exception(e) #'{}'.format(e))
|
||||||
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()
|
||||||
|
100
cic/token.py
Normal file
100
cic/token.py
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
# standard imports
|
||||||
|
import os
|
||||||
|
import json
|
||||||
|
|
||||||
|
# local imports
|
||||||
|
from .base import (
|
||||||
|
Data,
|
||||||
|
data_dir,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class Token(Data):
|
||||||
|
"""Encapsulates the token data used by the extension to deploy and/or register token and token related applications on chain.
|
||||||
|
|
||||||
|
Token details (name, symbol etc) will be used to initialize the token settings when start is called. If load is called instead, any token detail parameters passed to the constructor will be overwritten by data stored in the settings.
|
||||||
|
|
||||||
|
:param path: Settings directory path
|
||||||
|
:type path: str
|
||||||
|
:param name: Token name
|
||||||
|
:type name: str
|
||||||
|
:param symbol: Token symbol
|
||||||
|
:type symbol: str
|
||||||
|
:param precision: Token value precision (number of decimals)
|
||||||
|
:type precision: int
|
||||||
|
:param supply: Token supply (in smallest precision units)
|
||||||
|
:type supply: int
|
||||||
|
: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):
|
||||||
|
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.path = path
|
||||||
|
self.token_path = os.path.join(self.path, 'token.json')
|
||||||
|
|
||||||
|
|
||||||
|
def load(self):
|
||||||
|
"""Load token data from settings.
|
||||||
|
"""
|
||||||
|
super(Token, self).load()
|
||||||
|
|
||||||
|
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']
|
||||||
|
extras = []
|
||||||
|
extra_types = []
|
||||||
|
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')
|
||||||
|
if arg:
|
||||||
|
extras.append(arg)
|
||||||
|
if arg_type:
|
||||||
|
extra_types.append(arg_type)
|
||||||
|
self.extra_args = extras
|
||||||
|
self.extra_args_types = extra_types
|
||||||
|
self.inited = True
|
||||||
|
|
||||||
|
|
||||||
|
def start(self):
|
||||||
|
"""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()))
|
||||||
|
|
||||||
|
f = open(token_template_file_path)
|
||||||
|
o = json.load(f)
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
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')
|
||||||
|
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)
|
||||||
|
return s
|
24
cic/utils.py
24
cic/utils.py
@ -1,24 +0,0 @@
|
|||||||
def object_to_str(obj, keys):
|
|
||||||
"""Return a string representation of an object."""
|
|
||||||
s = ""
|
|
||||||
for key in keys:
|
|
||||||
value = eval("obj." + key)
|
|
||||||
key = key.replace("()", "")
|
|
||||||
if isinstance(value, str):
|
|
||||||
s += f"{key} = {value}\n"
|
|
||||||
elif isinstance(value, list):
|
|
||||||
for idx, vv in enumerate(value):
|
|
||||||
if not vv:
|
|
||||||
s += f"{key}[{idx}] = \n"
|
|
||||||
continue
|
|
||||||
s += f"{key}[{idx}] = {vv}\n"
|
|
||||||
elif isinstance(value, dict):
|
|
||||||
for vv_key in value.keys():
|
|
||||||
vv_value = value[vv_key]
|
|
||||||
if not vv_value:
|
|
||||||
s += f"{key}.{vv_key} = \n"
|
|
||||||
continue
|
|
||||||
s += f"{key}.{vv_key} = {vv_value}\n"
|
|
||||||
else:
|
|
||||||
s += f"{key} = {str(value)}\n"
|
|
||||||
return s
|
|
125
cic/writers.py
125
cic/writers.py
@ -1,125 +0,0 @@
|
|||||||
# standard imports
|
|
||||||
import base64
|
|
||||||
import json
|
|
||||||
import logging
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
import urllib.request
|
|
||||||
from typing import Dict, Type, Union
|
|
||||||
|
|
||||||
from cic_types.ext.metadata import MetadataPointer, MetadataRequestsHandler
|
|
||||||
|
|
||||||
logg = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
class OutputWriter:
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def write(self, k, v):
|
|
||||||
raise NotImplementedError()
|
|
||||||
|
|
||||||
|
|
||||||
class StdoutWriter(OutputWriter):
|
|
||||||
def write(self, k, v):
|
|
||||||
sys.stdout.write(f"{k}\t{v}\n")
|
|
||||||
|
|
||||||
|
|
||||||
class KVWriter(OutputWriter):
|
|
||||||
def __init__(self, path=None, *args, **kwargs):
|
|
||||||
try:
|
|
||||||
os.stat(path)
|
|
||||||
except FileNotFoundError:
|
|
||||||
os.makedirs(path)
|
|
||||||
self.path = path
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
def write(self, k, v):
|
|
||||||
fp = os.path.join(self.path, str(k))
|
|
||||||
logg.debug(f"path write {fp} {str(v)}")
|
|
||||||
f = open(fp, "wb")
|
|
||||||
f.write(v)
|
|
||||||
f.close()
|
|
||||||
|
|
||||||
|
|
||||||
class HTTPWriter(OutputWriter):
|
|
||||||
def __init__(self, path=None, headers: Dict[str, str] = None, *args, **kwargs):
|
|
||||||
super(HTTPWriter, self).__init__(*args, **kwargs)
|
|
||||||
self.path = path
|
|
||||||
self.headers = headers
|
|
||||||
|
|
||||||
def write(self, k, v):
|
|
||||||
path = self.path
|
|
||||||
if k is not None:
|
|
||||||
path = os.path.join(path, k)
|
|
||||||
logg.debug(f"HTTPWriter POST {path} data: {v}, headers: {self.headers}")
|
|
||||||
rq = urllib.request.Request(path, method="POST", data=v, headers=self.headers)
|
|
||||||
r = urllib.request.urlopen(rq)
|
|
||||||
logg.info(f"http writer submitted at {r.read()}")
|
|
||||||
|
|
||||||
|
|
||||||
class KeyedWriter(OutputWriter):
|
|
||||||
def __init__(self, writer_keyed, writer_immutable):
|
|
||||||
self.writer_keyed = writer_keyed
|
|
||||||
self.writer_immutable = writer_immutable
|
|
||||||
super().__init__()
|
|
||||||
|
|
||||||
def write(self, k, v):
|
|
||||||
logg.debug(f"writing keywriter key: {k} value: {v}")
|
|
||||||
if isinstance(v, str):
|
|
||||||
v = v.encode("utf-8")
|
|
||||||
if self.writer_keyed is not None:
|
|
||||||
self.writer_keyed.write(k, v)
|
|
||||||
if self.writer_immutable is not None:
|
|
||||||
self.writer_immutable.write(None, v)
|
|
||||||
|
|
||||||
|
|
||||||
class KeyedWriterFactory:
|
|
||||||
def __init__(
|
|
||||||
self, key_writer_constructor, immutable_writer_constructor, *_args, **kwargs
|
|
||||||
):
|
|
||||||
self.key_writer_constructor = key_writer_constructor
|
|
||||||
self.immutable_writer_constructor = immutable_writer_constructor
|
|
||||||
self.x = {}
|
|
||||||
for k, v in kwargs.items():
|
|
||||||
logg.debug(f"adding key {k} t keyed writer factory")
|
|
||||||
self.x[k] = v
|
|
||||||
|
|
||||||
def new(self, path=None, headers: Dict[str, str] = None, *_args, **_kwargs):
|
|
||||||
writer_keyed = None
|
|
||||||
writer_immutable = None
|
|
||||||
if self.key_writer_constructor is not None:
|
|
||||||
writer_keyed = self.key_writer_constructor(path, **self.x)
|
|
||||||
if self.immutable_writer_constructor is not None:
|
|
||||||
writer_immutable = self.immutable_writer_constructor(
|
|
||||||
path, headers, **self.x
|
|
||||||
)
|
|
||||||
return KeyedWriter(writer_keyed, writer_immutable)
|
|
||||||
|
|
||||||
|
|
||||||
class MetadataWriter(OutputWriter):
|
|
||||||
"""Custom writer for publishing data under immutable content-addressed pointers in the cic-meta storage backend.
|
|
||||||
|
|
||||||
Data that is not utf-8 will be converted to base64 before publishing.
|
|
||||||
|
|
||||||
Implements cic.writers.OutputWriter
|
|
||||||
"""
|
|
||||||
|
|
||||||
def write(self, k, v):
|
|
||||||
rq = MetadataRequestsHandler(MetadataPointer.NONE, bytes.fromhex(k))
|
|
||||||
try:
|
|
||||||
v = v.decode("utf-8")
|
|
||||||
v = json.loads(v)
|
|
||||||
logg.debug(f"metadatawriter bindecode {k} {v}")
|
|
||||||
except UnicodeDecodeError:
|
|
||||||
v = base64.b64encode(v).decode("utf-8")
|
|
||||||
v = json.loads(json.dumps(v, separators=(",", ":")))
|
|
||||||
logg.debug(f"metadatawriter b64encode {k} {v}")
|
|
||||||
r = rq.create(v)
|
|
||||||
logg.info(f"metadata submitted at {k}")
|
|
||||||
return r
|
|
||||||
|
|
||||||
|
|
||||||
WritersType = Union[
|
|
||||||
Type[OutputWriter], Type[KeyedWriter], Type[MetadataWriter], Type[OutputWriter]
|
|
||||||
]
|
|
7
eth_requirements.txt
Normal file
7
eth_requirements.txt
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
chainlib-eth~=0.0.13
|
||||||
|
funga-eth~=0.5.1
|
||||||
|
eth-token-index~=0.2.4
|
||||||
|
eth-address-index~=0.2.4
|
||||||
|
okota~=0.2.5a1
|
||||||
|
cic_eth_registry~=0.6.2
|
||||||
|
cic_contracts~=0.0.5
|
2920
poetry.lock
generated
2920
poetry.lock
generated
File diff suppressed because it is too large
Load Diff
@ -1,99 +0,0 @@
|
|||||||
[tool.poetry]
|
|
||||||
name = "cic-cli"
|
|
||||||
version = "0.5.5"
|
|
||||||
description = "Generic cli tooling for the CIC token network"
|
|
||||||
authors = [
|
|
||||||
"Louis Holbrook <dev@holbrook.no>",
|
|
||||||
"William Luke <williamluke4@gmail.com>",
|
|
||||||
]
|
|
||||||
license = "GPL-3.0-or-later"
|
|
||||||
readme = "README.md"
|
|
||||||
repository = "https://git.grassecon.net/cicnet/cic-cli"
|
|
||||||
classifiers = [
|
|
||||||
"Programming Language :: Python :: 3",
|
|
||||||
"Operating System :: OS Independent",
|
|
||||||
"Development Status :: 3 - Alpha",
|
|
||||||
"Environment :: Console",
|
|
||||||
"Intended Audience :: Developers",
|
|
||||||
"License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)",
|
|
||||||
"Topic :: Internet",
|
|
||||||
]
|
|
||||||
keywords = ["dlt", "blockchain", "cryptocurrency"]
|
|
||||||
packages = [
|
|
||||||
{ include = "cic" },
|
|
||||||
{ include = "cic/runnable/*.py" },
|
|
||||||
{ include = "cic/ext/**/*.py" },
|
|
||||||
{ include = "cic/cmd/**/*.py" },
|
|
||||||
]
|
|
||||||
|
|
||||||
[tool.poetry.scripts]
|
|
||||||
cic = 'cic.runnable.cic_cmd:main'
|
|
||||||
|
|
||||||
[[tool.poetry.source]]
|
|
||||||
name = "grassroots_"
|
|
||||||
url = "https://pip.grassrootseconomics.net/"
|
|
||||||
default = false
|
|
||||||
secondary = true
|
|
||||||
|
|
||||||
[[tool.poetry.source]]
|
|
||||||
name = "pypi_"
|
|
||||||
url = "https://pypi.org/simple/"
|
|
||||||
default = true
|
|
||||||
secondary = false
|
|
||||||
|
|
||||||
|
|
||||||
[tool.poetry.dependencies]
|
|
||||||
python = "^3.8"
|
|
||||||
funga-eth = "^0.6.0"
|
|
||||||
cic-types = "^0.2.7"
|
|
||||||
confini = "^0.6.0"
|
|
||||||
chainlib = "~0.1.0"
|
|
||||||
cbor2 = "~5.4.1"
|
|
||||||
|
|
||||||
chainlib-eth = { version = "~0.1.1", optional = true }
|
|
||||||
eth-token-index = { version = "^0.3.0", optional = true }
|
|
||||||
eth-address-index = { version = "~0.5.0", optional = true }
|
|
||||||
okota = { version = "^0.4.0", optional = true }
|
|
||||||
cic-eth-registry = { version = "^0.6.9", optional = true }
|
|
||||||
cic-contracts = { version = "~0.1.0", optional = true }
|
|
||||||
|
|
||||||
|
|
||||||
[tool.poetry.dev-dependencies]
|
|
||||||
pytest = "6.2.5"
|
|
||||||
pytest-cov = "2.10.1"
|
|
||||||
python-semantic-release = "^7.25.2"
|
|
||||||
pylint = "^2.12.2"
|
|
||||||
black = { version = "^22.1.0", allow-prereleases = true }
|
|
||||||
eth_tester = "0.5.0b3"
|
|
||||||
py-evm = "0.3.0a20"
|
|
||||||
rlp = "2.0.1"
|
|
||||||
mypy = "^0.961"
|
|
||||||
|
|
||||||
[tool.poetry.extras]
|
|
||||||
eth = [
|
|
||||||
"chainlib-eth",
|
|
||||||
"eth-token-index",
|
|
||||||
"eth-address-index",
|
|
||||||
"okota",
|
|
||||||
"cic_eth_registry",
|
|
||||||
"cic_contracts",
|
|
||||||
]
|
|
||||||
|
|
||||||
[build-system]
|
|
||||||
requires = ["poetry-core>=1.0.0"]
|
|
||||||
build-backend = "poetry.core.masonry.api"
|
|
||||||
|
|
||||||
[tool.pytest.ini_options]
|
|
||||||
addopts = "--cov=cic --cov-report term-missing -v"
|
|
||||||
testpaths = ["tests"]
|
|
||||||
|
|
||||||
[tool.semantic_release]
|
|
||||||
version_variable = ["cic/__init__.py:__version__", "pyproject.toml:version"]
|
|
||||||
version_source = "commit"
|
|
||||||
branch = "master"
|
|
||||||
upload_to_repository = true
|
|
||||||
upload_to_release = true
|
|
||||||
build_command = "pip install poetry && poetry build"
|
|
||||||
hvcs = "gitea"
|
|
||||||
hvcs_domain = "git.grassecon.net"
|
|
||||||
check_build_status = false
|
|
5
requirements.txt
Normal file
5
requirements.txt
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
funga-eth~=0.5.1
|
||||||
|
cic-types~=0.2.1a5
|
||||||
|
confini~=0.5.1
|
||||||
|
chainlib~=0.0.13
|
||||||
|
cbor2==5.4.1
|
17
run_tests.sh
Normal file
17
run_tests.sh
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -a
|
||||||
|
set -e
|
||||||
|
set -x
|
||||||
|
default_pythonpath=$PYTHONPATH:.
|
||||||
|
export PYTHONPATH=${default_pythonpath:-.}
|
||||||
|
>&2 echo using pythonpath $PYTHONPATH
|
||||||
|
for f in `ls tests/*.py`; do
|
||||||
|
python $f
|
||||||
|
done
|
||||||
|
for f in `ls tests/eth/*.py`; do
|
||||||
|
python $f
|
||||||
|
done
|
||||||
|
set +x
|
||||||
|
set +e
|
||||||
|
set +a
|
32
setup.cfg
Normal file
32
setup.cfg
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
[metadata]
|
||||||
|
name = cic
|
||||||
|
version = 0.0.2
|
||||||
|
description = Generic cli tooling for the CIC token network
|
||||||
|
author = Louis Holbrook
|
||||||
|
author_email = dev@holbrook.no
|
||||||
|
url = https://git.grassecon.net/cic-cli.git
|
||||||
|
keywords =
|
||||||
|
dlt
|
||||||
|
blockchain
|
||||||
|
cryptocurrency
|
||||||
|
classifiers =
|
||||||
|
Programming Language :: Python :: 3
|
||||||
|
Operating System :: OS Independent
|
||||||
|
Development Status :: 3 - Alpha
|
||||||
|
Environment :: Console
|
||||||
|
Intended Audience :: Developers
|
||||||
|
License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
|
||||||
|
Topic :: Internet
|
||||||
|
license = GPL3
|
||||||
|
licence_files =
|
||||||
|
LICENSE.txt
|
||||||
|
|
||||||
|
|
||||||
|
[options]
|
||||||
|
python_requires = >= 3.8
|
||||||
|
include_package_data = True
|
||||||
|
packages =
|
||||||
|
cic
|
||||||
|
cic.runnable
|
||||||
|
cic.ext.eth
|
||||||
|
cic.cmd
|
35
setup.py
Normal file
35
setup.py
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
from setuptools import setup
|
||||||
|
import configparser
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
requirements = []
|
||||||
|
f = open('requirements.txt', 'r')
|
||||||
|
while True:
|
||||||
|
l = f.readline()
|
||||||
|
if l == '':
|
||||||
|
break
|
||||||
|
requirements.append(l.rstrip())
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
eth_requirements = []
|
||||||
|
f = open('eth_requirements.txt', 'r')
|
||||||
|
while True:
|
||||||
|
l = f.readline()
|
||||||
|
if l == '':
|
||||||
|
break
|
||||||
|
eth_requirements.append(l.rstrip())
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
|
||||||
|
setup(
|
||||||
|
install_requires=requirements,
|
||||||
|
extras_require={
|
||||||
|
'eth': eth_requirements,
|
||||||
|
},
|
||||||
|
entry_points={
|
||||||
|
'console_scripts': [
|
||||||
|
'cic-cli=cic.runnable.cic_cmd:main',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
)
|
7
test_requirements.txt
Normal file
7
test_requirements.txt
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
eth-erc20>=0.1.2a3,<0.2.0
|
||||||
|
eth_tester==0.5.0b3
|
||||||
|
py-evm==0.3.0a20
|
||||||
|
rlp==2.0.1
|
||||||
|
chainlib-eth>=0.0.10a2,<0.1.0
|
||||||
|
eth-address-index>=0.2.4a1,<0.3.0
|
||||||
|
okota>=0.2.4a6,<0.3.0
|
@ -1,40 +1,42 @@
|
|||||||
# standard imports
|
# standard imports
|
||||||
import os
|
import os
|
||||||
import random
|
|
||||||
import tempfile
|
import tempfile
|
||||||
|
import logging
|
||||||
import unittest
|
import unittest
|
||||||
|
import random
|
||||||
|
|
||||||
# external imports
|
# external imports
|
||||||
from hexathon import add_0x
|
from hexathon import add_0x
|
||||||
|
|
||||||
# local imports
|
# local imports
|
||||||
from cic.writers import KVWriter
|
from cic.output import KVWriter
|
||||||
from cic.contract.components.attachment import Attachment
|
from cic.processor import Processor
|
||||||
from cic.contract.components.proof import Proof
|
from cic.attachment import Attachment
|
||||||
from cic.contract.processor import ContractProcessor
|
from cic import Proof
|
||||||
|
|
||||||
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')
|
||||||
|
|
||||||
proof_hash = "0f6fc017f29caf512c0feaaf83bc10614b488311cace2973dc248dc24b01e04f"
|
proof_hash = '0f6fc017f29caf512c0feaaf83bc10614b488311cace2973dc248dc24b01e04f'
|
||||||
foo_hash = "2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae"
|
foo_hash = '2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae'
|
||||||
bar_hash = "fcde2b2edba56bf408601fb721fe9b5c338d10ee429ea04fae5511b68fbf8fb9"
|
bar_hash = 'fcde2b2edba56bf408601fb721fe9b5c338d10ee429ea04fae5511b68fbf8fb9'
|
||||||
root_merged_hash = "2a27a488377c753fffea58ad535cfdacc2fcb5cf0ae495ec71d88e31757ec0c3"
|
root_merged_hash = '795fed550ada0ec1eea4309a282f5910bc3bdb3a9762c7d9cc25d6de71c45096'
|
||||||
root_unmerged_hash = "14dc271290eca763e99c2e7c21c541bded86fb803c6b01bac28cd367db34399c"
|
root_unmerged_hash = '5dc81e51703e624f498663e7d5d70429b824e9ff60f92b61fe47eb6862a971b4'
|
||||||
|
|
||||||
|
|
||||||
class TestCICBase(unittest.TestCase):
|
class TestCICBase(unittest.TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(TestCICBase, self).setUp()
|
super(TestCICBase, self).setUp()
|
||||||
random.seed(42)
|
random.seed(42)
|
||||||
|
|
||||||
f = open("/dev/urandom", "rb")
|
f = open('/dev/urandom', 'rb')
|
||||||
addresses = []
|
addresses = []
|
||||||
for _i in range(3):
|
for i in range(3):
|
||||||
address_bytes = f.read(32)
|
address_bytes = f.read(32)
|
||||||
addresses.append(add_0x(address_bytes.hex()))
|
addresses.append(add_0x(address_bytes.hex()))
|
||||||
|
|
||||||
self.token_symbol = "FOO"
|
self.token_symbol = 'FOO'
|
||||||
|
|
||||||
token_address_bytes = f.read(20)
|
token_address_bytes = f.read(20)
|
||||||
token_index_address_bytes = f.read(20)
|
token_index_address_bytes = f.read(20)
|
||||||
@ -48,23 +50,23 @@ class TestCICBase(unittest.TestCase):
|
|||||||
|
|
||||||
self.outputs_dir = tempfile.mkdtemp()
|
self.outputs_dir = tempfile.mkdtemp()
|
||||||
self.outputs_writer = KVWriter(self.outputs_dir)
|
self.outputs_writer = KVWriter(self.outputs_dir)
|
||||||
self.core_processor = ContractProcessor(outputs_writer=self.outputs_writer)
|
self.core_processor = Processor(outputs_writer=self.outputs_writer)
|
||||||
|
|
||||||
self.resources = {
|
self.resources = {
|
||||||
"token": {
|
'token': {
|
||||||
"reference": self.token_address,
|
'reference': self.token_address,
|
||||||
"key_address": addresses[0],
|
'key_address': addresses[0],
|
||||||
},
|
},
|
||||||
"token_index": {
|
'token_index': {
|
||||||
"reference": self.token_index_address,
|
'reference': self.token_index_address,
|
||||||
"key_address": addresses[1],
|
'key_address': addresses[1],
|
||||||
},
|
},
|
||||||
"address_declarator": {
|
'address_declarator': {
|
||||||
"reference": self.address_declarator_address,
|
'reference': self.address_declarator_address,
|
||||||
"key_address": addresses[2],
|
'key_address': addresses[2],
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
proof_dir = os.path.join(test_data_dir, "proof")
|
proof_dir = os.path.join(test_data_dir, 'proof')
|
||||||
attach = Attachment(path=proof_dir)
|
attach = Attachment(path=proof_dir)
|
||||||
attach.load()
|
attach.load()
|
||||||
self.proofs = Proof(proof_dir, attachments=attach)
|
self.proofs = Proof(proof_dir, attachments=attach)
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# standard imports
|
# standard imports import unittestimport logging
|
||||||
import random
|
import random
|
||||||
import os
|
import os
|
||||||
import logging
|
import logging
|
||||||
@ -24,10 +24,10 @@ from cic_contracts.writer import CICWriter
|
|||||||
|
|
||||||
# local imports
|
# local imports
|
||||||
from cic.ext.eth import CICEth
|
from cic.ext.eth import CICEth
|
||||||
from cic.writers import KVWriter
|
from cic import Proof
|
||||||
from cic.contract.processor import ContractProcessor
|
from cic.attachment import Attachment
|
||||||
from cic.contract.components.proof import Proof
|
from cic.output import KVWriter
|
||||||
from cic.contract.components.attachment import Attachment
|
from cic.processor import Processor
|
||||||
|
|
||||||
|
|
||||||
# test imports
|
# test imports
|
||||||
@ -127,4 +127,4 @@ class TestCICEthTokenBase(TestCICEthBase):
|
|||||||
self.token_precision = 8
|
self.token_precision = 8
|
||||||
self.token_supply = 1073741824
|
self.token_supply = 1073741824
|
||||||
|
|
||||||
self.core_processor = ContractProcessor(outputs_writer=self.outputs_writer, extensions=[self.adapter])
|
self.core_processor = Processor(outputs_writer=self.outputs_writer, extensions=[self.adapter])
|
||||||
|
@ -27,8 +27,8 @@ from giftable_erc20_token import GiftableToken
|
|||||||
|
|
||||||
# local imports
|
# local imports
|
||||||
from cic.ext.eth import CICEth
|
from cic.ext.eth import CICEth
|
||||||
from cic.contract.processor import ContractProcessor
|
from cic.processor import Processor
|
||||||
from cic.contract.components.token import Token
|
from cic.token import Token
|
||||||
|
|
||||||
# test imports
|
# test imports
|
||||||
from tests.eth.base_eth import TestCICEthTokenBase
|
from tests.eth.base_eth import TestCICEthTokenBase
|
||||||
@ -46,7 +46,7 @@ class TestCICEthRPC(TestCICEthTokenBase):
|
|||||||
gas_oracle = RPCGasOracle(self.rpc)
|
gas_oracle = RPCGasOracle(self.rpc)
|
||||||
|
|
||||||
self.adapter = CICEth(self.chain_spec, self.resources, self.proofs, signer=self.signer, rpc=self.rpc, fee_oracle=gas_oracle, outputs_writer=self.outputs_writer)
|
self.adapter = CICEth(self.chain_spec, self.resources, self.proofs, signer=self.signer, rpc=self.rpc, fee_oracle=gas_oracle, outputs_writer=self.outputs_writer)
|
||||||
self.core_processor = ContractProcessor(outputs_writer=self.outputs_writer, extensions=[self.adapter])
|
self.core_processor = Processor(outputs_writer=self.outputs_writer, extensions=[self.adapter])
|
||||||
|
|
||||||
|
|
||||||
def test_rpc_process_notoken(self):
|
def test_rpc_process_notoken(self):
|
||||||
|
@ -5,7 +5,7 @@ import os
|
|||||||
|
|
||||||
# local imports
|
# local imports
|
||||||
from cic.ext.eth import CICEth
|
from cic.ext.eth import CICEth
|
||||||
from cic.contract.processor import ContractProcessor
|
from cic.processor import Processor
|
||||||
|
|
||||||
# tests imports
|
# tests imports
|
||||||
from tests.eth.base_eth import TestCICEthBase
|
from tests.eth.base_eth import TestCICEthBase
|
||||||
|
@ -11,7 +11,7 @@ from hexathon import (
|
|||||||
|
|
||||||
# local imports
|
# local imports
|
||||||
from cic.ext.eth import CICEth
|
from cic.ext.eth import CICEth
|
||||||
from cic.contract.processor import ContractProcessor
|
from cic.processor import Processor
|
||||||
|
|
||||||
# tests imports
|
# tests imports
|
||||||
from tests.eth.base_eth import TestCICEthBase
|
from tests.eth.base_eth import TestCICEthBase
|
||||||
@ -25,7 +25,7 @@ class TestCICEthSign(TestCICEthBase):
|
|||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(TestCICEthSign, self).setUp()
|
super(TestCICEthSign, self).setUp()
|
||||||
self.adapter = CICEth(self.chain_spec, self.resources, self.proofs, signer=self.signer)
|
self.adapter = CICEth(self.chain_spec, self.resources, self.proofs, signer=self.signer)
|
||||||
self.core_processor = ContractProcessor(outputs_writer=self.outputs_writer, extensions=[self.adapter])
|
self.core_processor = Processor(outputs_writer=self.outputs_writer, extensions=[self.adapter])
|
||||||
|
|
||||||
|
|
||||||
def test_sign_token_index(self):
|
def test_sign_token_index(self):
|
||||||
|
@ -1,138 +0,0 @@
|
|||||||
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
|
|
@ -1,26 +1,23 @@
|
|||||||
# standard imports
|
# standard imports
|
||||||
import logging
|
|
||||||
import os
|
import os
|
||||||
import unittest
|
import unittest
|
||||||
|
import logging
|
||||||
|
|
||||||
# local imports
|
# local imports
|
||||||
from funga.error import DecryptError
|
|
||||||
from funga.eth.keystore.dict import DictKeystore
|
|
||||||
from hexathon import uniform as hex_uniform
|
|
||||||
|
|
||||||
# external imports
|
|
||||||
from cic.keystore import KeystoreDirectory
|
from cic.keystore import KeystoreDirectory
|
||||||
|
from funga.eth.keystore.dict import DictKeystore
|
||||||
|
from funga.error import DecryptError
|
||||||
|
from hexathon import uniform as hex_uniform
|
||||||
|
|
||||||
# test imports
|
# test imports
|
||||||
from tests.base_cic import test_base_dir
|
from tests.base_cic import test_base_dir
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
logging = logging.getLogger()
|
||||||
|
|
||||||
script_dir = test_base_dir
|
script_dir = test_base_dir
|
||||||
|
|
||||||
|
|
||||||
def pass_getter():
|
def pass_getter():
|
||||||
return "test"
|
return 'test'
|
||||||
|
|
||||||
|
|
||||||
class EthKeystoreDirectory(DictKeystore, KeystoreDirectory):
|
class EthKeystoreDirectory(DictKeystore, KeystoreDirectory):
|
||||||
@ -28,25 +25,25 @@ class EthKeystoreDirectory(DictKeystore, KeystoreDirectory):
|
|||||||
|
|
||||||
|
|
||||||
class TestKeyfile(unittest.TestCase):
|
class TestKeyfile(unittest.TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.path = os.path.join(script_dir, "testdata", "keystore")
|
self.path = os.path.join(script_dir, 'testdata', 'keystore')
|
||||||
self.keystore = EthKeystoreDirectory()
|
self.keystore = EthKeystoreDirectory()
|
||||||
|
|
||||||
|
|
||||||
def test_keystore_bogus(self):
|
def test_keystore_bogus(self):
|
||||||
bogus_path = os.path.join(self.path, "bogus")
|
bogus_path = os.path.join(self.path, 'bogus')
|
||||||
self.keystore.process_dir(bogus_path)
|
self.keystore.process_dir(bogus_path)
|
||||||
|
|
||||||
|
|
||||||
def test_keystore_ok(self):
|
def test_keystore_ok(self):
|
||||||
ok_path = os.path.join(self.path, "ok")
|
ok_path = os.path.join(self.path, 'ok')
|
||||||
with self.assertRaises(DecryptError):
|
with self.assertRaises(DecryptError):
|
||||||
self.keystore.process_dir(ok_path) # wrong password
|
self.keystore.process_dir(ok_path) # wrong password
|
||||||
self.keystore.process_dir(ok_path, default_password="test")
|
self.keystore.process_dir(ok_path, default_password='test')
|
||||||
self.keystore.process_dir(ok_path, password_retriever=pass_getter)
|
self.keystore.process_dir(ok_path, password_retriever=pass_getter)
|
||||||
self.assertTrue(
|
self.assertTrue(hex_uniform('cc4f82F5DacDE395E1E0CFc4d62827C8B8B5688C') in self.keystore.list())
|
||||||
hex_uniform("cc4f82F5DacDE395E1E0CFc4d62827C8B8B5688C")
|
|
||||||
in self.keystore.list()
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
@ -7,49 +7,25 @@ import os
|
|||||||
from hexathon import strip_0x
|
from hexathon import strip_0x
|
||||||
|
|
||||||
# local imports
|
# local imports
|
||||||
from cic.contract.components.meta import Meta
|
from cic.meta import Meta
|
||||||
|
|
||||||
# test imports
|
# test imports
|
||||||
from tests.base_cic import TestCICBase, test_data_dir
|
from tests.base_cic import (
|
||||||
|
TestCICBase,
|
||||||
|
test_data_dir,
|
||||||
|
)
|
||||||
|
|
||||||
logging.basicConfig(level=logging.DEBUG)
|
logging.basicConfig(level=logging.DEBUG)
|
||||||
logg = logging.getLogger()
|
logg = logging.getLogger()
|
||||||
|
|
||||||
|
|
||||||
class TestCICMeta(TestCICBase):
|
class TestCICMeta(TestCICBase):
|
||||||
|
|
||||||
def test_meta(self):
|
def test_meta(self):
|
||||||
fp = os.path.join(test_data_dir, "proof")
|
fp = os.path.join(test_data_dir, 'proof')
|
||||||
m = Meta(fp)
|
m = Meta(fp)
|
||||||
m.load()
|
m.load()
|
||||||
self.assertEquals(
|
|
||||||
str(m),
|
|
||||||
"""name = Test
|
|
||||||
contact.phone = 0700-123456
|
|
||||||
country_code = KE
|
|
||||||
location = Kilifi
|
|
||||||
""",
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_meta_with_initial_values(self):
|
|
||||||
fp = os.path.join(test_data_dir, "proof")
|
|
||||||
m = Meta(
|
|
||||||
fp,
|
|
||||||
name="TestName",
|
|
||||||
location="TestLocation",
|
|
||||||
country_code="TestCC",
|
|
||||||
contact={
|
|
||||||
"phone": "0723578455158",
|
|
||||||
},
|
|
||||||
)
|
|
||||||
self.assertEquals(
|
|
||||||
str(m),
|
|
||||||
"""name = TestName
|
|
||||||
contact.phone = 0723578455158
|
|
||||||
country_code = TestCC
|
|
||||||
location = TestLocation
|
|
||||||
""",
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
@ -7,7 +7,7 @@ import logging
|
|||||||
from hexathon import strip_0x
|
from hexathon import strip_0x
|
||||||
|
|
||||||
# local imports
|
# local imports
|
||||||
from cic.writers import KVWriter
|
from cic.output import KVWriter
|
||||||
|
|
||||||
# test imports
|
# test imports
|
||||||
from tests.base_cic import TestCICBase
|
from tests.base_cic import TestCICBase
|
@ -3,12 +3,17 @@ import unittest
|
|||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import json
|
import json
|
||||||
|
import sys
|
||||||
|
|
||||||
|
# external imports
|
||||||
|
from hexathon import strip_0x
|
||||||
|
|
||||||
# local imports
|
# local imports
|
||||||
from cic.contract.processor import ContractProcessor
|
from cic import Proof
|
||||||
from cic.contract.components.proof import Proof
|
from cic.processor import Processor
|
||||||
from cic.contract.components.attachment import Attachment
|
from cic.attachment import Attachment
|
||||||
from cic.contract.components.meta import Meta
|
from cic.meta import Meta
|
||||||
|
from cic.output import KVWriter
|
||||||
|
|
||||||
# test imports
|
# test imports
|
||||||
from tests.base_cic import (
|
from tests.base_cic import (
|
||||||
@ -23,65 +28,66 @@ logg.setLevel(logging.DEBUG)
|
|||||||
|
|
||||||
|
|
||||||
class MockExt:
|
class MockExt:
|
||||||
|
|
||||||
def __init__(self, address):
|
def __init__(self, address):
|
||||||
self.address = address
|
self.address = address
|
||||||
|
|
||||||
def process(self):
|
def process(self):
|
||||||
return (self.address, "foo")
|
return (self.address, 'foo')
|
||||||
|
|
||||||
|
|
||||||
class TestCICProcessor(TestCICBase):
|
class TestCICProcessor(TestCICBase):
|
||||||
|
|
||||||
def test_processor_meta(self):
|
def test_processor_meta(self):
|
||||||
fp = os.path.join(test_data_dir, "proof")
|
fp = os.path.join(test_data_dir, 'proof')
|
||||||
m = Meta(fp)
|
m = Meta(fp)
|
||||||
m.load()
|
m.load()
|
||||||
|
|
||||||
mock_ext = MockExt(self.token_address)
|
mock_ext = MockExt(self.token_address)
|
||||||
p = ContractProcessor(
|
p = Processor(metadata=m, outputs_writer=self.outputs_writer, extensions=[mock_ext])
|
||||||
metadata=m, outputs_writer=self.outputs_writer, extensions=[mock_ext]
|
|
||||||
)
|
|
||||||
p.token_address = self.token_address
|
p.token_address = self.token_address
|
||||||
p.process()
|
p.process()
|
||||||
|
|
||||||
meta_reference = m.reference(self.token_address)
|
meta_reference = m.reference(self.token_address)
|
||||||
|
|
||||||
fp = os.path.join(self.outputs_dir, meta_reference)
|
fp = os.path.join(self.outputs_dir, meta_reference)
|
||||||
with open(fp, "r", encoding="utf-8") as f:
|
f = open(fp, 'r')
|
||||||
o = json.load(f)
|
o = json.load(f)
|
||||||
|
f.close()
|
||||||
self.assertEqual(m.asdict(), o)
|
self.assertEqual(m.asdict(), o)
|
||||||
|
|
||||||
|
|
||||||
def test_processor_attachment(self):
|
def test_processor_attachment(self):
|
||||||
fp = os.path.join(test_data_dir, "proof")
|
fp = os.path.join(test_data_dir, 'proof')
|
||||||
m = Attachment(fp)
|
m = Attachment(fp)
|
||||||
m.load()
|
m.load()
|
||||||
|
|
||||||
mock_ext = MockExt(self.token_address)
|
mock_ext = MockExt(self.token_address)
|
||||||
p = ContractProcessor(
|
p = Processor(attachment=m, outputs_writer=self.outputs_writer, extensions=[mock_ext])
|
||||||
attachment=m, outputs_writer=self.outputs_writer, extensions=[mock_ext]
|
|
||||||
)
|
|
||||||
p.process()
|
p.process()
|
||||||
|
|
||||||
for _k in list(m.contents.keys()):
|
|
||||||
|
for k in list(m.contents.keys()):
|
||||||
os.stat(fp)
|
os.stat(fp)
|
||||||
|
|
||||||
|
|
||||||
def test_processor_proof_noattachment(self):
|
def test_processor_proof_noattachment(self):
|
||||||
fp = os.path.join(test_data_dir, "proof")
|
fp = os.path.join(test_data_dir, 'proof')
|
||||||
m = Proof(fp)
|
m = Proof(fp)
|
||||||
|
|
||||||
ap = os.path.join(test_data_dir, "proof_empty")
|
ap = os.path.join(test_data_dir, 'proof_empty')
|
||||||
m.extra_attachments = Attachment(ap)
|
m.extra_attachments = Attachment(ap)
|
||||||
m.load()
|
m.load()
|
||||||
|
|
||||||
mock_ext = MockExt(self.token_address)
|
mock_ext = MockExt(self.token_address)
|
||||||
p = ContractProcessor(
|
p = Processor(proof=m, outputs_writer=self.outputs_writer, extensions=[mock_ext])
|
||||||
proof=m, outputs_writer=self.outputs_writer, extensions=[mock_ext]
|
|
||||||
)
|
|
||||||
p.process()
|
p.process()
|
||||||
|
|
||||||
self.assertEqual(p.outputs[0], root_unmerged_hash)
|
self.assertEqual(p.outputs[0], root_unmerged_hash)
|
||||||
|
|
||||||
|
|
||||||
def test_processor_proof_attachment(self):
|
def test_processor_proof_attachment(self):
|
||||||
fp = os.path.join(test_data_dir, "proof")
|
fp = os.path.join(test_data_dir, 'proof')
|
||||||
|
|
||||||
ma = Attachment(fp)
|
ma = Attachment(fp)
|
||||||
ma.load()
|
ma.load()
|
||||||
@ -90,13 +96,11 @@ class TestCICProcessor(TestCICBase):
|
|||||||
mp.load()
|
mp.load()
|
||||||
|
|
||||||
mock_ext = MockExt(self.token_address)
|
mock_ext = MockExt(self.token_address)
|
||||||
p = ContractProcessor(
|
p = Processor(proof=mp, outputs_writer=self.outputs_writer, extensions=[mock_ext])
|
||||||
proof=mp, outputs_writer=self.outputs_writer, extensions=[mock_ext]
|
|
||||||
)
|
|
||||||
p.process()
|
p.process()
|
||||||
|
|
||||||
self.assertEqual(p.outputs[0], root_merged_hash)
|
self.assertEqual(p.outputs[0], root_merged_hash)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
@ -4,35 +4,23 @@ import unittest
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
# local imports
|
# local imports
|
||||||
from cic.contract.components.proof import Proof
|
from cic import Proof
|
||||||
from cic.contract.components.attachment import Attachment
|
from cic.attachment import Attachment
|
||||||
|
|
||||||
# test imports
|
# test imports
|
||||||
from tests.base_cic import test_data_dir, TestCICBase, root_merged_hash
|
from tests.base_cic import (
|
||||||
|
test_data_dir,
|
||||||
|
TestCICBase,
|
||||||
|
root_merged_hash,
|
||||||
|
)
|
||||||
|
|
||||||
logging.basicConfig(level=logging.DEBUG)
|
logging.basicConfig(level=logging.DEBUG)
|
||||||
logg = logging.getLogger()
|
logg = logging.getLogger()
|
||||||
|
|
||||||
class TestProof(TestCICBase):
|
class TestProof(TestCICBase):
|
||||||
def test_proof_load(self):
|
|
||||||
proof_path = os.path.join(test_data_dir, "proof")
|
|
||||||
attach = Attachment(proof_path, writer=self.outputs_writer)
|
|
||||||
attach.load()
|
|
||||||
c = Proof(path=proof_path, attachments=attach)
|
|
||||||
c.load()
|
|
||||||
self.assertEquals(
|
|
||||||
str(c),
|
|
||||||
"""description = foo bar baz
|
|
||||||
issuer = the man
|
|
||||||
namespace = ge
|
|
||||||
version = 0
|
|
||||||
proofs[0] = 2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae
|
|
||||||
proofs[1] = fcde2b2edba56bf408601fb721fe9b5c338d10ee429ea04fae5511b68fbf8fb9
|
|
||||||
""",
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_proof_serialize_merge(self):
|
def test_proof_serialize_merge(self):
|
||||||
proof_path = os.path.join(test_data_dir, "proof")
|
proof_path = os.path.join(test_data_dir, 'proof')
|
||||||
|
|
||||||
attach = Attachment(proof_path, writer=self.outputs_writer)
|
attach = Attachment(proof_path, writer=self.outputs_writer)
|
||||||
attach.load()
|
attach.load()
|
||||||
@ -43,5 +31,5 @@ proofs[1] = fcde2b2edba56bf408601fb721fe9b5c338d10ee429ea04fae5511b68fbf8fb9
|
|||||||
self.assertEqual(v, root_merged_hash)
|
self.assertEqual(v, root_merged_hash)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
7
tests/testdata/proof/meta.json
vendored
7
tests/testdata/proof/meta.json
vendored
@ -1,8 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "Test",
|
"name": "",
|
||||||
"location": "Kilifi",
|
"location": "",
|
||||||
"country_code": "KE",
|
"country_code": "",
|
||||||
"contact": {
|
"contact": {
|
||||||
"phone": "0700-123456"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
2
tests/testdata/voucher/bondi.csv
vendored
2
tests/testdata/voucher/bondi.csv
vendored
@ -1,2 +0,0 @@
|
|||||||
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
|
|
|
2
tests/testdata/voucher/bondi_giftable.csv
vendored
2
tests/testdata/voucher/bondi_giftable.csv
vendored
@ -1,2 +0,0 @@
|
|||||||
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
|
|
|
Loading…
Reference in New Issue
Block a user