Compare commits
35 Commits
lash/ussd-
...
lash/new-c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ed55c62347
|
||
|
|
73fbc48603
|
||
| 7fe5b6bea3 | |||
| fc27dd6826 | |||
| 75262dae5d | |||
| c59f9da0fc | |||
| 20a26045eb | |||
| 09a79e10d5 | |||
| c0dff41b3c | |||
| 18be4c48a7 | |||
| 432dbe9fee | |||
| 93338aebea | |||
| 097a80e8de | |||
| bd4e5b0a40 | |||
| 4927d92215 | |||
| 650472252d | |||
| 746196d2b1 | |||
| 842cbadf00 | |||
| 3661e48fd1 | |||
| f136504988 | |||
| f6a7956fdf | |||
| 562292bd01 | |||
| 3cdf7b9965 | |||
| 16d88d389b | |||
|
29da44bb9f
|
|||
| 15445b8d0f | |||
| bb90ceea0b | |||
| 8904e2abb1 | |||
| 94d3e61d0c | |||
|
|
fc08f3d17a
|
||
|
|
8da5219290 | ||
|
|
5f01135b04 | ||
|
|
543c6249b9 | ||
|
|
db4f8f8955 | ||
|
|
0c45e12ce1 |
@@ -1,5 +1,5 @@
|
||||
[database]
|
||||
NAME=cic-eth
|
||||
NAME=cic_cache
|
||||
USER=postgres
|
||||
PASSWORD=
|
||||
HOST=localhost
|
||||
|
||||
@@ -36,7 +36,7 @@ script_location = .
|
||||
# output_encoding = utf-8
|
||||
|
||||
#sqlalchemy.url = driver://user:pass@localhost/dbname
|
||||
sqlalchemy.url = postgresql+psycopg2://postgres@localhost:5432/cic-cache
|
||||
sqlalchemy.url = postgresql+psycopg2://postgres@localhost:5432/cic_cache
|
||||
|
||||
|
||||
[post_write_hooks]
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
CREATE DATABASE "cic-cache";
|
||||
CREATE DATABASE "cic-eth";
|
||||
CREATE DATABASE "cic-notify";
|
||||
CREATE DATABASE "cic-meta";
|
||||
CREATE DATABASE "cic-signer";
|
||||
@@ -25,6 +25,7 @@ licence_files =
|
||||
python_requires = >= 3.6
|
||||
packages =
|
||||
cic_cache
|
||||
cic_cache.tasks
|
||||
cic_cache.db
|
||||
cic_cache.db.models
|
||||
cic_cache.runnable
|
||||
@@ -33,5 +34,6 @@ scripts =
|
||||
|
||||
[options.entry_points]
|
||||
console_scripts =
|
||||
cic-cache-tracker = cic_cache.runnable.tracker:main
|
||||
cic-cache-server = cic_cache.runnable.server:main
|
||||
cic-cache-trackerd = cic_cache.runnable.tracker:main
|
||||
cic-cache-serverd = cic_cache.runnable.server:main
|
||||
cic-cache-taskerd = cic_cache.runnable.tasker:main
|
||||
|
||||
@@ -10,12 +10,15 @@ from cic_registry import zero_address
|
||||
from cic_eth.db.enum import LockEnum
|
||||
from cic_eth.db.models.base import SessionBase
|
||||
from cic_eth.db.models.lock import Lock
|
||||
from cic_eth.task import (
|
||||
CriticalSQLAlchemyTask,
|
||||
)
|
||||
from cic_eth.error import LockedError
|
||||
|
||||
celery_app = celery.current_app
|
||||
logg = logging.getLogger()
|
||||
|
||||
@celery_app.task()
|
||||
@celery_app.task(base=CriticalSQLAlchemyTask)
|
||||
def lock(chained_input, chain_str, address=zero_address, flags=LockEnum.ALL, tx_hash=None):
|
||||
"""Task wrapper to set arbitrary locks
|
||||
|
||||
@@ -33,7 +36,7 @@ def lock(chained_input, chain_str, address=zero_address, flags=LockEnum.ALL, tx_
|
||||
return chained_input
|
||||
|
||||
|
||||
@celery_app.task()
|
||||
@celery_app.task(base=CriticalSQLAlchemyTask)
|
||||
def unlock(chained_input, chain_str, address=zero_address, flags=LockEnum.ALL):
|
||||
"""Task wrapper to reset arbitrary locks
|
||||
|
||||
@@ -51,7 +54,7 @@ def unlock(chained_input, chain_str, address=zero_address, flags=LockEnum.ALL):
|
||||
return chained_input
|
||||
|
||||
|
||||
@celery_app.task()
|
||||
@celery_app.task(base=CriticalSQLAlchemyTask)
|
||||
def lock_send(chained_input, chain_str, address=zero_address, tx_hash=None):
|
||||
"""Task wrapper to set send lock
|
||||
|
||||
@@ -67,7 +70,7 @@ def lock_send(chained_input, chain_str, address=zero_address, tx_hash=None):
|
||||
return chained_input
|
||||
|
||||
|
||||
@celery_app.task()
|
||||
@celery_app.task(base=CriticalSQLAlchemyTask)
|
||||
def unlock_send(chained_input, chain_str, address=zero_address):
|
||||
"""Task wrapper to reset send lock
|
||||
|
||||
@@ -83,7 +86,7 @@ def unlock_send(chained_input, chain_str, address=zero_address):
|
||||
return chained_input
|
||||
|
||||
|
||||
@celery_app.task()
|
||||
@celery_app.task(base=CriticalSQLAlchemyTask)
|
||||
def lock_queue(chained_input, chain_str, address=zero_address, tx_hash=None):
|
||||
"""Task wrapper to set queue direct lock
|
||||
|
||||
@@ -99,7 +102,7 @@ def lock_queue(chained_input, chain_str, address=zero_address, tx_hash=None):
|
||||
return chained_input
|
||||
|
||||
|
||||
@celery_app.task()
|
||||
@celery_app.task(base=CriticalSQLAlchemyTask)
|
||||
def unlock_queue(chained_input, chain_str, address=zero_address):
|
||||
"""Task wrapper to reset queue direct lock
|
||||
|
||||
@@ -115,7 +118,7 @@ def unlock_queue(chained_input, chain_str, address=zero_address):
|
||||
return chained_input
|
||||
|
||||
|
||||
@celery_app.task()
|
||||
@celery_app.task(base=CriticalSQLAlchemyTask)
|
||||
def check_lock(chained_input, chain_str, lock_flags, address=None):
|
||||
session = SessionBase.create_session()
|
||||
r = Lock.check(chain_str, lock_flags, address=zero_address, session=session)
|
||||
|
||||
@@ -82,6 +82,7 @@ class Api:
|
||||
:returns: uuid of root task
|
||||
:rtype: celery.Task
|
||||
"""
|
||||
raise NotImplementedError('out of service until new DEX migration is done')
|
||||
s_check = celery.signature(
|
||||
'cic_eth.admin.ctrl.check_lock',
|
||||
[
|
||||
@@ -143,6 +144,7 @@ class Api:
|
||||
:returns: uuid of root task
|
||||
:rtype: celery.Task
|
||||
"""
|
||||
raise NotImplementedError('out of service until new DEX migration is done')
|
||||
s_check = celery.signature(
|
||||
'cic_eth.admin.ctrl.check_lock',
|
||||
[
|
||||
@@ -340,11 +342,6 @@ class Api:
|
||||
],
|
||||
queue=self.queue,
|
||||
)
|
||||
s_nonce = celery.signature(
|
||||
'cic_eth.eth.tx.reserve_nonce',
|
||||
[],
|
||||
queue=self.queue,
|
||||
)
|
||||
s_account = celery.signature(
|
||||
'cic_eth.eth.account.create',
|
||||
[
|
||||
@@ -352,12 +349,18 @@ class Api:
|
||||
],
|
||||
queue=self.queue,
|
||||
)
|
||||
s_nonce.link(s_account)
|
||||
s_check.link(s_nonce)
|
||||
s_check.link(s_account)
|
||||
if self.callback_param != None:
|
||||
s_account.link(self.callback_success)
|
||||
|
||||
if register:
|
||||
s_nonce = celery.signature(
|
||||
'cic_eth.eth.tx.reserve_nonce',
|
||||
[
|
||||
'ACCOUNTS_INDEX_WRITER',
|
||||
],
|
||||
queue=self.queue,
|
||||
)
|
||||
s_register = celery.signature(
|
||||
'cic_eth.eth.account.register',
|
||||
[
|
||||
@@ -365,7 +368,8 @@ class Api:
|
||||
],
|
||||
queue=self.queue,
|
||||
)
|
||||
s_account.link(s_register)
|
||||
s_nonce.link(s_register)
|
||||
s_account.link(s_nonce)
|
||||
|
||||
t = s_check.apply_async(queue=self.queue)
|
||||
return t
|
||||
@@ -390,7 +394,9 @@ class Api:
|
||||
)
|
||||
s_nonce = celery.signature(
|
||||
'cic_eth.eth.tx.reserve_nonce',
|
||||
[],
|
||||
[
|
||||
'GAS_GIFTER',
|
||||
],
|
||||
queue=self.queue,
|
||||
)
|
||||
s_refill = celery.signature(
|
||||
|
||||
@@ -66,3 +66,17 @@ class LockedError(Exception):
|
||||
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class SignerError(Exception):
|
||||
"""Exception raised when signer is unavailable or generates an error
|
||||
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class EthError(Exception):
|
||||
"""Exception raised when unspecified error from evm node is encountered
|
||||
|
||||
"""
|
||||
pass
|
||||
|
||||
@@ -21,8 +21,14 @@ from cic_eth.db.models.base import SessionBase
|
||||
from cic_eth.db.models.role import AccountRole
|
||||
from cic_eth.db.models.tx import TxCache
|
||||
from cic_eth.eth.util import unpack_signed_raw_tx
|
||||
from cic_eth.error import RoleMissingError
|
||||
from cic_eth.task import CriticalSQLAlchemyTask
|
||||
from cic_eth.error import (
|
||||
RoleMissingError,
|
||||
SignerError,
|
||||
)
|
||||
from cic_eth.task import (
|
||||
CriticalSQLAlchemyTask,
|
||||
CriticalSQLAlchemyAndSignerTask,
|
||||
)
|
||||
|
||||
#logg = logging.getLogger(__name__)
|
||||
logg = logging.getLogger()
|
||||
@@ -139,7 +145,7 @@ def unpack_gift(data):
|
||||
|
||||
|
||||
# TODO: Separate out nonce initialization task
|
||||
@celery_app.task(base=CriticalSQLAlchemyTask)
|
||||
@celery_app.task(base=CriticalSQLAlchemyAndSignerTask)
|
||||
def create(password, chain_str):
|
||||
"""Creates and stores a new ethereum account in the keystore.
|
||||
|
||||
@@ -154,7 +160,13 @@ def create(password, chain_str):
|
||||
"""
|
||||
chain_spec = ChainSpec.from_chain_str(chain_str)
|
||||
c = RpcClient(chain_spec)
|
||||
a = c.w3.eth.personal.new_account(password)
|
||||
a = None
|
||||
try:
|
||||
a = c.w3.eth.personal.new_account(password)
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
if a == None:
|
||||
raise SignerError('create account')
|
||||
logg.debug('created account {}'.format(a))
|
||||
|
||||
# Initialize nonce provider record for account
|
||||
@@ -165,7 +177,7 @@ def create(password, chain_str):
|
||||
return a
|
||||
|
||||
|
||||
@celery_app.task(bind=True, throws=(RoleMissingError,), base=CriticalSQLAlchemyTask)
|
||||
@celery_app.task(bind=True, throws=(RoleMissingError,), base=CriticalSQLAlchemyAndSignerTask)
|
||||
def register(self, account_address, chain_str, writer_address=None):
|
||||
"""Creates a transaction to add the given address to the accounts index.
|
||||
|
||||
@@ -215,7 +227,7 @@ def register(self, account_address, chain_str, writer_address=None):
|
||||
return account_address
|
||||
|
||||
|
||||
@celery_app.task(bind=True, base=CriticalSQLAlchemyTask)
|
||||
@celery_app.task(bind=True, base=CriticalSQLAlchemyAndSignerTask)
|
||||
def gift(self, account_address, chain_str):
|
||||
"""Creates a transaction to invoke the faucet contract for the given address.
|
||||
|
||||
|
||||
@@ -54,6 +54,7 @@ class GasOracle():
|
||||
"""
|
||||
session = SessionBase.create_session()
|
||||
a = AccountRole.get_address('GAS_GIFTER', session)
|
||||
logg.debug('gasgifter {}'.format(a))
|
||||
session.close()
|
||||
return a
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ from cic_registry.chain import ChainSpec
|
||||
# local imports
|
||||
from cic_eth.eth import RpcClient
|
||||
from cic_eth.queue.tx import create as queue_create
|
||||
from cic_eth.error import SignerError
|
||||
|
||||
celery_app = celery.current_app
|
||||
logg = celery_app.log.get_default_logger()
|
||||
@@ -26,7 +27,13 @@ def sign_tx(tx, chain_str):
|
||||
"""
|
||||
chain_spec = ChainSpec.from_chain_str(chain_str)
|
||||
c = RpcClient(chain_spec)
|
||||
tx_transfer_signed = c.w3.eth.sign_transaction(tx)
|
||||
tx_transfer_signed = None
|
||||
try:
|
||||
tx_transfer_signed = c.w3.eth.sign_transaction(tx)
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
if tx_transfer_signed == None:
|
||||
raise SignerError('sign tx')
|
||||
logg.debug('tx_transfer_signed {}'.format(tx_transfer_signed))
|
||||
tx_hash = c.w3.keccak(hexstr=tx_transfer_signed['raw'])
|
||||
tx_hash_hex = tx_hash.hex()
|
||||
|
||||
@@ -24,6 +24,7 @@ from cic_eth.ext.address import translate_address
|
||||
from cic_eth.task import (
|
||||
CriticalSQLAlchemyTask,
|
||||
CriticalWeb3Task,
|
||||
CriticalSQLAlchemyAndSignerTask,
|
||||
)
|
||||
|
||||
celery_app = celery.current_app
|
||||
@@ -212,7 +213,7 @@ def balance(tokens, holder_address, chain_str):
|
||||
return tokens
|
||||
|
||||
|
||||
@celery_app.task(bind=True, base=CriticalSQLAlchemyTask)
|
||||
@celery_app.task(bind=True, base=CriticalSQLAlchemyAndSignerTask)
|
||||
def transfer(self, tokens, holder_address, receiver_address, value, chain_str):
|
||||
"""Transfer ERC20 tokens between addresses
|
||||
|
||||
@@ -268,7 +269,7 @@ def transfer(self, tokens, holder_address, receiver_address, value, chain_str):
|
||||
return tx_hash_hex
|
||||
|
||||
|
||||
@celery_app.task(bind=True, base=CriticalSQLAlchemyTask)
|
||||
@celery_app.task(bind=True, base=CriticalSQLAlchemyAndSignerTask)
|
||||
def approve(self, tokens, holder_address, spender_address, value, chain_str):
|
||||
"""Approve ERC20 transfer on behalf of holder address
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ from cic_eth.db import Otx, SessionBase
|
||||
from cic_eth.db.models.tx import TxCache
|
||||
from cic_eth.db.models.nonce import NonceReservation
|
||||
from cic_eth.db.models.lock import Lock
|
||||
from cic_eth.db.models.role import AccountRole
|
||||
from cic_eth.db.enum import (
|
||||
LockEnum,
|
||||
StatusBits,
|
||||
@@ -36,6 +37,9 @@ from cic_eth.admin.ctrl import lock_send
|
||||
from cic_eth.task import (
|
||||
CriticalSQLAlchemyTask,
|
||||
CriticalWeb3Task,
|
||||
CriticalWeb3AndSignerTask,
|
||||
CriticalSQLAlchemyAndSignerTask,
|
||||
CriticalSQLAlchemyAndWeb3Task,
|
||||
)
|
||||
|
||||
celery_app = celery.current_app
|
||||
@@ -45,7 +49,7 @@ MAX_NONCE_ATTEMPTS = 3
|
||||
|
||||
|
||||
# TODO this function is too long
|
||||
@celery_app.task(bind=True, throws=(OutOfGasError), base=CriticalSQLAlchemyTask)
|
||||
@celery_app.task(bind=True, throws=(OutOfGasError), base=CriticalSQLAlchemyAndWeb3Task)
|
||||
def check_gas(self, tx_hashes, chain_str, txs=[], address=None, gas_required=None):
|
||||
"""Check the gas level of the sender address of a transaction.
|
||||
|
||||
@@ -73,6 +77,9 @@ def check_gas(self, tx_hashes, chain_str, txs=[], address=None, gas_required=Non
|
||||
if address == None:
|
||||
address = o['address']
|
||||
|
||||
if not web3.Web3.isChecksumAddress(address):
|
||||
raise ValueError('invalid address {}'.format(address))
|
||||
|
||||
chain_spec = ChainSpec.from_chain_str(chain_str)
|
||||
|
||||
queue = self.request.delivery_info['routing_key']
|
||||
@@ -81,7 +88,12 @@ def check_gas(self, tx_hashes, chain_str, txs=[], address=None, gas_required=Non
|
||||
c = RpcClient(chain_spec)
|
||||
|
||||
# TODO: it should not be necessary to pass address explicitly, if not passed should be derived from the tx
|
||||
balance = c.w3.eth.getBalance(address)
|
||||
balance = 0
|
||||
try:
|
||||
balance = c.w3.eth.getBalance(address)
|
||||
except ValueError as e:
|
||||
raise EthError('balance call for {}'.format())
|
||||
|
||||
logg.debug('address {} has gas {} needs {}'.format(address, balance, gas_required))
|
||||
|
||||
if gas_required > balance:
|
||||
@@ -418,7 +430,7 @@ def send(self, txs, chain_str):
|
||||
|
||||
# TODO: if this method fails the nonce will be out of sequence. session needs to be extended to include the queue create, so that nonce is rolled back if the second sql query fails. Better yet, split each state change into separate tasks.
|
||||
# TODO: method is too long, factor out code for clarity
|
||||
@celery_app.task(bind=True, throws=(web3.exceptions.TransactionNotFound,), base=CriticalWeb3Task)
|
||||
@celery_app.task(bind=True, throws=(web3.exceptions.TransactionNotFound,), base=CriticalWeb3AndSignerTask)
|
||||
def refill_gas(self, recipient_address, chain_str):
|
||||
"""Executes a native token transaction to fund the recipient's gas expenditures.
|
||||
|
||||
@@ -511,7 +523,7 @@ def refill_gas(self, recipient_address, chain_str):
|
||||
return tx_send_gas_signed['raw']
|
||||
|
||||
|
||||
@celery_app.task(bind=True)
|
||||
@celery_app.task(bind=True, base=CriticalSQLAlchemyAndSignerTask)
|
||||
def resend_with_higher_gas(self, txold_hash_hex, chain_str, gas=None, default_factor=1.1):
|
||||
"""Create a new transaction from an existing one with same nonce and higher gas price.
|
||||
|
||||
@@ -588,11 +600,23 @@ def resend_with_higher_gas(self, txold_hash_hex, chain_str, gas=None, default_fa
|
||||
|
||||
|
||||
@celery_app.task(bind=True, base=CriticalSQLAlchemyTask)
|
||||
def reserve_nonce(self, chained_input, address=None):
|
||||
def reserve_nonce(self, chained_input, signer=None):
|
||||
session = SessionBase.create_session()
|
||||
|
||||
if address == None:
|
||||
address = None
|
||||
if signer == None:
|
||||
address = chained_input
|
||||
logg.debug('non-explicit address for reserve nonce, using arg head {}'.format(chained_input))
|
||||
else:
|
||||
if web3.Web3.isChecksumAddress(signer):
|
||||
address = signer
|
||||
logg.debug('explicit address for reserve nonce {}'.format(signer))
|
||||
else:
|
||||
address = AccountRole.get_address(signer, session=session)
|
||||
logg.debug('role for reserve nonce {} -> {}'.format(signer, address))
|
||||
|
||||
if not web3.Web3.isChecksumAddress(address):
|
||||
raise ValueError('invalid result when resolving address for nonce {}'.format(address))
|
||||
|
||||
root_id = self.request.root_id
|
||||
nonce = NonceReservation.next(address, root_id)
|
||||
|
||||
@@ -5,6 +5,7 @@ import os
|
||||
import logging
|
||||
import uuid
|
||||
import json
|
||||
from xdg.BaseDirectory import xdg_config_home
|
||||
|
||||
import celery
|
||||
from cic_eth.api import Api
|
||||
|
||||
@@ -25,7 +25,7 @@ logging.getLogger('urllib3').setLevel(logging.WARNING)
|
||||
|
||||
|
||||
default_abi_dir = '/usr/share/local/cic/solidity/abi'
|
||||
default_config_dir = os.path.join('/usr/local/etc/cic-eth')
|
||||
default_config_dir = os.environ.get('CONFINI_DIR', '/usr/local/etc/cic')
|
||||
|
||||
argparser = argparse.ArgumentParser()
|
||||
argparser.add_argument('-p', '--provider', dest='p', default='http://localhost:8545', type=str, help='Web3 provider url (http only)')
|
||||
|
||||
@@ -22,7 +22,7 @@ logg = logging.getLogger()
|
||||
logging.getLogger('web3').setLevel(logging.WARNING)
|
||||
logging.getLogger('urllib3').setLevel(logging.WARNING)
|
||||
|
||||
default_config_dir = os.path.join('/usr/local/etc/cic-eth')
|
||||
default_config_dir = os.environ.get('CONFINI_DIR', '/usr/local/etc/cic')
|
||||
|
||||
|
||||
argparser = argparse.ArgumentParser()
|
||||
|
||||
@@ -37,7 +37,7 @@ logging.getLogger('urllib3').setLevel(logging.WARNING)
|
||||
|
||||
|
||||
default_abi_dir = '/usr/share/local/cic/solidity/abi'
|
||||
default_config_dir = os.path.join('/usr/local/etc/cic-eth')
|
||||
default_config_dir = os.environ.get('CONFINI_DIR', '/usr/local/etc/cic')
|
||||
|
||||
argparser = argparse.ArgumentParser()
|
||||
argparser.add_argument('-p', '--provider', dest='p', type=str, help='Web3 provider url (http only)')
|
||||
|
||||
@@ -5,6 +5,12 @@ import requests
|
||||
import celery
|
||||
import sqlalchemy
|
||||
|
||||
# local imports
|
||||
from cic_eth.error import (
|
||||
SignerError,
|
||||
EthError,
|
||||
)
|
||||
|
||||
|
||||
class CriticalTask(celery.Task):
|
||||
retry_jitter = True
|
||||
@@ -30,4 +36,18 @@ class CriticalSQLAlchemyAndWeb3Task(CriticalTask):
|
||||
sqlalchemy.exc.DatabaseError,
|
||||
sqlalchemy.exc.TimeoutError,
|
||||
requests.exceptions.ConnectionError,
|
||||
EthError,
|
||||
)
|
||||
|
||||
class CriticalSQLAlchemyAndSignerTask(CriticalTask):
|
||||
autoretry_for = (
|
||||
sqlalchemy.exc.DatabaseError,
|
||||
sqlalchemy.exc.TimeoutError,
|
||||
SignerError,
|
||||
)
|
||||
|
||||
class CriticalWeb3AndSignerTask(CriticalTask):
|
||||
autoretry_for = (
|
||||
requests.exceptions.ConnectionError,
|
||||
SignerError,
|
||||
)
|
||||
|
||||
@@ -10,7 +10,7 @@ version = (
|
||||
0,
|
||||
10,
|
||||
0,
|
||||
'alpha.39',
|
||||
'alpha.41',
|
||||
)
|
||||
|
||||
version_object = semver.VersionInfo(
|
||||
|
||||
@@ -6,4 +6,4 @@ HOST=localhost
|
||||
PORT=5432
|
||||
ENGINE=postgresql
|
||||
DRIVER=psycopg2
|
||||
DEBUG=
|
||||
DEBUG=0
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
[tasks]
|
||||
transfer_callbacks = taskcall:cic_eth.callbacks.noop.noop
|
||||
trace_queue_status =
|
||||
trace_queue_status = 1
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
cic-base~=0.1.1a10
|
||||
cic-base~=0.1.1a20
|
||||
web3==5.12.2
|
||||
celery==4.4.7
|
||||
crypto-dev-signer~=0.4.13rc4
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
# external imports
|
||||
import celery
|
||||
|
||||
# local imports
|
||||
from cic_eth.db.models.debug import Debug
|
||||
|
||||
|
||||
def test_debug_alert(
|
||||
init_database,
|
||||
celery_session_worker,
|
||||
):
|
||||
|
||||
s = celery.signature(
|
||||
'cic_eth.admin.debug.alert',
|
||||
[
|
||||
'foo',
|
||||
'bar',
|
||||
'baz',
|
||||
],
|
||||
queue=None,
|
||||
)
|
||||
t = s.apply_async()
|
||||
r = t.get()
|
||||
assert r == 'foo'
|
||||
|
||||
q = init_database.query(Debug)
|
||||
q = q.filter(Debug.tag=='bar')
|
||||
o = q.first()
|
||||
assert o.description == 'baz'
|
||||
@@ -10,7 +10,7 @@ from cic_notify.error import PleaseCommitFirstError
|
||||
|
||||
logg = logging.getLogger()
|
||||
|
||||
version = (0, 4, 0, 'alpha.2')
|
||||
version = (0, 4, 0, 'alpha.3')
|
||||
|
||||
version_object = semver.VersionInfo(
|
||||
major=version[0],
|
||||
@@ -24,9 +24,6 @@ version_string = str(version_object)
|
||||
|
||||
def git_hash():
|
||||
import subprocess
|
||||
git_diff = subprocess.run(['git', 'diff'], capture_output=True)
|
||||
if len(git_diff.stdout) > 0:
|
||||
raise PleaseCommitFirstError()
|
||||
|
||||
git_hash = subprocess.run(['git', 'rev-parse', 'HEAD'], capture_output=True)
|
||||
git_hash_brief = git_hash.stdout.decode('utf-8')[:8]
|
||||
@@ -35,7 +32,7 @@ def git_hash():
|
||||
|
||||
try:
|
||||
version_git = git_hash()
|
||||
version_string += '.build.{}'.format(version_git)
|
||||
version_string += '+build.{}'.format(version_git)
|
||||
except FileNotFoundError:
|
||||
time_string_pair = str(time.time()).split('.')
|
||||
version_string += '+build.{}{:<09d}'.format(
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
celery~=4.4.7
|
||||
confini~=0.3.6a1
|
||||
alembic~=1.4.2
|
||||
celery~=4.4.7
|
||||
confini~=0.3.6rc3
|
||||
redis~=3.5.3
|
||||
semver==2.13.0
|
||||
@@ -1,6 +1,6 @@
|
||||
[metadata]
|
||||
name = cic-notify
|
||||
version= attr: cic_notify.version.__version_string__
|
||||
version= 0.4.0a2
|
||||
description = CIC notifications service
|
||||
author = Louis Holbrook
|
||||
author_email = dev@holbrook.no
|
||||
|
||||
@@ -14,7 +14,7 @@ from cic_ussd.db.models.user import User
|
||||
from cic_ussd.db.models.ussd_session import UssdSession
|
||||
from cic_ussd.db.models.task_tracker import TaskTracker
|
||||
from cic_ussd.menu.ussd_menu import UssdMenu
|
||||
from cic_ussd.processor import custom_display_text, process_request
|
||||
from cic_ussd.processor import custom_display_text, process_request, retrieve_most_recent_ussd_session
|
||||
from cic_ussd.redis import InMemoryStore
|
||||
from cic_ussd.session.ussd_session import UssdSession as InMemoryUssdSession
|
||||
from cic_ussd.validator import check_known_user, validate_response_type
|
||||
@@ -60,7 +60,8 @@ def create_ussd_session(
|
||||
phone: str,
|
||||
service_code: str,
|
||||
user_input: str,
|
||||
current_menu: str) -> InMemoryUssdSession:
|
||||
current_menu: str,
|
||||
session_data: Optional[dict] = None) -> InMemoryUssdSession:
|
||||
"""
|
||||
Creates a new ussd session
|
||||
:param external_session_id: Session id value provided by AT
|
||||
@@ -73,6 +74,8 @@ def create_ussd_session(
|
||||
:type user_input: str
|
||||
:param current_menu: Menu name that is currently being displayed on the ussd session
|
||||
:type current_menu: str
|
||||
:param session_data: Any additional data that was persisted during the user's interaction with the system.
|
||||
:type session_data: dict.
|
||||
:return: ussd session object
|
||||
:rtype: Session
|
||||
"""
|
||||
@@ -81,7 +84,8 @@ def create_ussd_session(
|
||||
msisdn=phone,
|
||||
user_input=user_input,
|
||||
state=current_menu,
|
||||
service_code=service_code
|
||||
service_code=service_code,
|
||||
session_data=session_data
|
||||
)
|
||||
return session
|
||||
|
||||
@@ -126,7 +130,9 @@ def create_or_update_session(
|
||||
phone=phone,
|
||||
service_code=service_code,
|
||||
user_input=user_input,
|
||||
current_menu=current_menu)
|
||||
current_menu=current_menu,
|
||||
session_data=session_data
|
||||
)
|
||||
return ussd_session
|
||||
|
||||
|
||||
@@ -338,14 +344,26 @@ def process_menu_interaction_requests(chain_str: str,
|
||||
user_input=user_input
|
||||
)
|
||||
|
||||
# create or update the ussd session as appropriate
|
||||
ussd_session = create_or_update_session(
|
||||
external_session_id=external_session_id,
|
||||
phone=phone_number,
|
||||
service_code=service_code,
|
||||
user_input=user_input,
|
||||
current_menu=current_menu.get('name')
|
||||
)
|
||||
last_ussd_session = retrieve_most_recent_ussd_session(phone_number=user.phone_number)
|
||||
|
||||
if last_ussd_session:
|
||||
# create or update the ussd session as appropriate
|
||||
ussd_session = create_or_update_session(
|
||||
external_session_id=external_session_id,
|
||||
phone=phone_number,
|
||||
service_code=service_code,
|
||||
user_input=user_input,
|
||||
current_menu=current_menu.get('name'),
|
||||
session_data=last_ussd_session.session_data
|
||||
)
|
||||
else:
|
||||
ussd_session = create_or_update_session(
|
||||
external_session_id=external_session_id,
|
||||
phone=phone_number,
|
||||
service_code=service_code,
|
||||
user_input=user_input,
|
||||
current_menu=current_menu.get('name')
|
||||
)
|
||||
|
||||
# define appropriate response
|
||||
response = custom_display_text(
|
||||
|
||||
@@ -6,7 +6,7 @@ from typing import Optional
|
||||
|
||||
# third party imports
|
||||
import celery
|
||||
from cic_types.models.person import Person
|
||||
from sqlalchemy import desc
|
||||
from tinydb.table import Document
|
||||
|
||||
# local imports
|
||||
@@ -315,6 +315,16 @@ def process_start_menu(display_key: str, user: User):
|
||||
)
|
||||
|
||||
|
||||
def retrieve_most_recent_ussd_session(phone_number: str) -> UssdSession:
|
||||
# get last ussd session based on user phone number
|
||||
last_ussd_session = UssdSession.session\
|
||||
.query(UssdSession)\
|
||||
.filter_by(msisdn=phone_number)\
|
||||
.order_by(desc(UssdSession.created))\
|
||||
.first()
|
||||
return last_ussd_session
|
||||
|
||||
|
||||
def process_request(user_input: str, user: User, ussd_session: Optional[dict] = None) -> Document:
|
||||
"""This function assesses a request based on the user from the request comes, the session_id and the user's
|
||||
input. It determines whether the request translates to a return to an existing session by checking whether the
|
||||
@@ -337,7 +347,23 @@ def process_request(user_input: str, user: User, ussd_session: Optional[dict] =
|
||||
return UssdMenu.find_by_name(name=successive_state)
|
||||
else:
|
||||
if user.has_valid_pin():
|
||||
return UssdMenu.find_by_name(name='start')
|
||||
last_ussd_session = retrieve_most_recent_ussd_session(phone_number=user.phone_number)
|
||||
|
||||
key = create_cached_data_key(
|
||||
identifier=blockchain_address_to_metadata_pointer(blockchain_address=user.blockchain_address),
|
||||
salt='cic.person'
|
||||
)
|
||||
user_metadata = get_cached_data(key=key)
|
||||
|
||||
if last_ussd_session:
|
||||
# get last state
|
||||
last_state = last_ussd_session.state
|
||||
logg.debug(f'LAST USSD SESSION STATE: {last_state}')
|
||||
# if last state is account_creation_prompt and metadata exists, show start menu
|
||||
if last_state == 'account_creation_prompt' and user_metadata is not None:
|
||||
return UssdMenu.find_by_name(name='start')
|
||||
else:
|
||||
return UssdMenu.find_by_name(name=last_state)
|
||||
else:
|
||||
if user.failed_pin_attempts >= 3 and user.get_account_status() == AccountStatus.LOCKED.name:
|
||||
return UssdMenu.find_by_name(name='exit_pin_blocked')
|
||||
|
||||
@@ -21,10 +21,10 @@ from confini import Config
|
||||
logging.basicConfig(level=logging.WARNING)
|
||||
logg = logging.getLogger()
|
||||
|
||||
config_dir = os.path.join(xdg_config_home, 'cli-ussd')
|
||||
default_config_dir = os.environ.get('CONFINI_DIR', '/usr/local/etc/cic')
|
||||
|
||||
argparser = argparse.ArgumentParser(description='CLI tool to interface a Sempo USSD session')
|
||||
argparser.add_argument('-c', type=str, default=config_dir, help='config root to use')
|
||||
argparser.add_argument('-c', type=str, default=default_config_dir, help='config root to use')
|
||||
#argparser.add_argument('-d', type=str, default='local', help='deployment name to interface (config root subdirectory)')
|
||||
argparser.add_argument('--host', type=str, default='localhost')
|
||||
argparser.add_argument('--port', type=int, default=9000)
|
||||
@@ -64,7 +64,7 @@ else:
|
||||
ssl = True
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
def main():
|
||||
|
||||
# TODO: improve url building
|
||||
url = 'http'
|
||||
@@ -101,3 +101,7 @@ if __name__ == "__main__":
|
||||
state = response_data[:3]
|
||||
out = response_data[4:]
|
||||
print(out)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,7 +1,7 @@
|
||||
# standard imports
|
||||
import semver
|
||||
|
||||
version = (0, 3, 0, 'alpha.1')
|
||||
version = (0, 3, 0, 'alpha.5')
|
||||
|
||||
version_object = semver.VersionInfo(
|
||||
major=version[0],
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
FROM python:3.8.5-alpine
|
||||
# FROM python:3.8.5-alpine
|
||||
FROM python:3.8.6-slim-buster
|
||||
|
||||
# set working directory
|
||||
WORKDIR /usr/src
|
||||
@@ -6,10 +7,8 @@ WORKDIR /usr/src
|
||||
# add args for installing from self-hosted packages
|
||||
ARG pip_extra_index_url_flag='--extra-index-url https://pip.grassrootseconomics.net:8433'
|
||||
|
||||
# add alpine sys packages
|
||||
RUN apk update && \
|
||||
apk add git linux-headers postgresql-dev gnupg bash
|
||||
RUN apk add --update musl-dev gcc libffi-dev
|
||||
RUN apt-get update && \
|
||||
apt install -y gcc gnupg libpq-dev wget make g++ gnupg bash procps git
|
||||
|
||||
# create secrets directory
|
||||
RUN mkdir -vp pgp/keys
|
||||
|
||||
@@ -16,9 +16,16 @@ div#session {
|
||||
<textarea id="monitor" disabled="1"></textarea>
|
||||
<div id="login">
|
||||
<label for="user">API username</label>
|
||||
<input type="text" id="user" name="user" type="text" /><br/>
|
||||
<input type="text" id="user" name="user" />
|
||||
<label for="user">API password</label>
|
||||
<input type="text" id="pass" name="pass" type="text" /><br/>
|
||||
<input type="text" id="pass" name="pass" /> <br/>
|
||||
|
||||
<label for="host">API host</label>
|
||||
<input type="text" id="host" name="host" />
|
||||
<label for="host">API port</label>
|
||||
<input type="text" id="port" name="port" />
|
||||
<label for="host">SSL</label>
|
||||
<input type="checkbox" id="ssl" name="ssl" checked="1"/> <br/>
|
||||
<hr/>
|
||||
<input type="text" id="phone" /> <button onclick="setPhone(document.getElementById('phone').value);" id="send_phone">set phone number</button>
|
||||
</div>
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
//var proto = 'http';
|
||||
//var host = 'localhost:9000';
|
||||
var ssl = false;
|
||||
var host = 'localhost';
|
||||
var port = 9000;
|
||||
var proto = 'https';
|
||||
var host = 'staging.sarafu.network';
|
||||
var user = 'admin_bert_token_inc.';
|
||||
var pass = '197781ed60bf16d5dc12d84e3df37e35';
|
||||
var serviceCode = '*483*061#';
|
||||
var user = 'foo';
|
||||
var pass = 'bar';
|
||||
var path = '/';
|
||||
var serviceCode = '*483*46#';
|
||||
|
||||
// cheekily stolen from https://www.tutorialspoint.com/how-to-create-guid-uuid-in-javascript
|
||||
function createUUID() {
|
||||
@@ -23,9 +25,17 @@ function send(s) {
|
||||
document.getElementById('send_input').disabled = true;
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.responseType = 'text';
|
||||
current_user = document.getElementById('user').value;
|
||||
current_pass = document.getElementById('pass').value;
|
||||
xhr.open('POST', proto + '://' + host + '/api/v1/ussd/kenya?username=' + current_user + '&password=' + current_pass, true);
|
||||
const current_user = document.getElementById('user').value;
|
||||
const current_pass = document.getElementById('pass').value;
|
||||
const current_host = document.getElementById('host').value;
|
||||
const current_port = document.getElementById('port').value;
|
||||
let current_scheme = 'http';
|
||||
if (document.getElementById('ssl').checked) {
|
||||
current_scheme += 's';
|
||||
}
|
||||
const url = current_scheme + '://' + current_host + ':' + current_port + '?username=' + current_user + '&password=' + current_pass
|
||||
console.debug('connecting to', url);
|
||||
xhr.open('POST', url, true);
|
||||
xhr.setRequestHeader('Content-Type', 'application/json');
|
||||
data = {
|
||||
sessionId: uuid,
|
||||
@@ -106,6 +116,8 @@ function abort() {
|
||||
window.addEventListener('load', () => {
|
||||
document.getElementById('user').value = user;
|
||||
document.getElementById('pass').value = pass;
|
||||
document.getElementById('host').value = host;
|
||||
document.getElementById('port').value = port;
|
||||
document.getElementById('phone').addEventListener('keyup', (e) => {
|
||||
if (e.keyCode == '13') {
|
||||
document.getElementById('input').value = '';
|
||||
|
||||
@@ -6,12 +6,11 @@ betterpath==0.2.2
|
||||
billiard==3.6.3.0
|
||||
celery==4.4.7
|
||||
cffi==1.14.3
|
||||
chainlib~=0.0.1a15
|
||||
cic-eth==0.10.0a39
|
||||
cic-notify==0.3.1
|
||||
cic-types==0.1.0a8
|
||||
cic-eth~=0.10.0a41
|
||||
cic-notify~=0.4.0a3
|
||||
cic-types~=0.1.0a8
|
||||
click==7.1.2
|
||||
confini==0.3.5
|
||||
confini~=0.3.6rc3
|
||||
cryptography==3.2.1
|
||||
faker==4.17.1
|
||||
iniconfig==1.1.1
|
||||
@@ -36,7 +35,6 @@ python-i18n==0.3.9
|
||||
pytz==2020.1
|
||||
PyYAML==5.3.1
|
||||
redis==3.5.3
|
||||
requests==2.24.0
|
||||
semver==2.13.0
|
||||
six==1.15.0
|
||||
SQLAlchemy==1.3.20
|
||||
@@ -46,4 +44,4 @@ transitions==0.8.4
|
||||
uWSGI==2.0.19.1
|
||||
vcversioner==2.16.0.0
|
||||
vine==1.3.0
|
||||
zope.interface==5.1.2
|
||||
zope.interface==5.1.2
|
||||
@@ -44,4 +44,5 @@ scripts =
|
||||
|
||||
[options.entry_points]
|
||||
console_scripts =
|
||||
cic-ussd-tasker = cic_ussd.runnable.tasker:main
|
||||
cic-ussd-tasker = cic_ussd.runnable.tasker:main
|
||||
cic-ussd-client = cic_ussd.runnable.client:main
|
||||
|
||||
@@ -1,14 +1,22 @@
|
||||
#FROM ethereum/solc:0.6.12
|
||||
FROM ethereum/solc:0.8.0
|
||||
# syntax = docker/dockerfile:1.2
|
||||
FROM python:3.8.6-slim-buster as compile-image
|
||||
|
||||
# The solc image messes up the alpine environment, so we have to go all over again
|
||||
FROM alpine
|
||||
COPY --from=0 /usr/bin/solc /usr/bin/solc
|
||||
RUN apt-get update
|
||||
RUN apt-get install -y --no-install-recommends git gcc g++ libpq-dev gawk jq telnet wget openssl iputils-ping gnupg socat bash procps make python2 cargo
|
||||
|
||||
RUN apk update && \
|
||||
apk add make git
|
||||
|
||||
WORKDIR /usr/src
|
||||
RUN apt-get install -y software-properties-common
|
||||
RUN add-apt-repository ppa:ethereum/ethereum
|
||||
RUN apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 1C52189C923F6CA9
|
||||
RUN apt-get update
|
||||
RUN apt-get install solc
|
||||
RUN pip install --upgrade pip
|
||||
|
||||
WORKDIR /root
|
||||
RUN mkdir -vp /usr/local/etc/cic
|
||||
|
||||
COPY contract-migration/nvm.sh .
|
||||
ENV CONFINI_DIR /usr/local/etc/cic/
|
||||
RUN mkdir -vp $CONFINI_DIR
|
||||
|
||||
ARG cic_config_commit=35c69ba75f00c8147150acf325565d5391cf25bf
|
||||
ARG cic_config_url=https://gitlab.com/grassrootseconomics/cic-config.git/
|
||||
@@ -16,11 +24,8 @@ RUN echo Install confini schema files && \
|
||||
git clone --depth 1 $cic_config_url cic-config && \
|
||||
cd cic-config && \
|
||||
git fetch --depth 1 origin $cic_config_commit && \
|
||||
git checkout $cic_config_commit && \
|
||||
mkdir -vp /usr/local/etc/cic && \
|
||||
cp -v *.ini /usr/local/etc/cic/
|
||||
ENV CONFINI_DIR /usr/local/etc/cic
|
||||
|
||||
git checkout $cic_config_commit && \
|
||||
cp -v *.ini $CONFINI_DIR
|
||||
|
||||
ARG cic_contracts_commit=698ef3a30fde8d7f2c498f1208fb0ff45d665501
|
||||
ARG cic_contracts_url=https://gitlab.com/grassrootseconomics/cic-contracts.git/
|
||||
@@ -31,30 +36,6 @@ RUN echo Install ABI collection for solidity interfaces used across all componen
|
||||
git checkout $cic_contracts_commit && \
|
||||
make install
|
||||
|
||||
#COPY ./Makefile ./cic-contracts/Makefile
|
||||
#COPY ./*.sol ./cic-contracts/
|
||||
|
||||
#RUN cd cic-contracts && \
|
||||
# make -B && make install -B
|
||||
|
||||
FROM python:3.8.6-slim-buster
|
||||
|
||||
COPY --from=1 /usr/local/share/cic/ /usr/local/share/cic/
|
||||
COPY --from=1 /usr/local/etc/ /usr/local/etc/
|
||||
|
||||
LABEL authors="Louis Holbrook <dev@holbrook.no> 0826EDA1702D1E87C6E2875121D2E7BB88C2A746"
|
||||
LABEL spdx-license-identifier="GPL-3.0-or-later"
|
||||
LABEL description="Base layer for buiding development images for the cic component suite"
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install -y git gcc g++ libpq-dev && \
|
||||
apt-get install -y vim gawk jq telnet openssl iputils-ping curl wget gnupg socat bash procps make python2 postgresql-client
|
||||
|
||||
|
||||
RUN echo installing nodejs tooling
|
||||
|
||||
COPY contract-migration/nvm.sh /root/
|
||||
|
||||
# Install nvm with node and npm
|
||||
# https://stackoverflow.com/questions/25899912/how-to-install-nvm-in-docker
|
||||
ENV NVM_DIR /root/.nvm
|
||||
@@ -65,67 +46,100 @@ RUN wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.37.2/install.sh |
|
||||
&& . $NVM_DIR/nvm.sh \
|
||||
&& nvm install $NODE_VERSION \
|
||||
&& nvm alias default $NODE_VERSION \
|
||||
&& nvm use $NODE_VERSION \
|
||||
# So many ridiculously stupid issues with node in docker that take oceans of absolutely wasted time to resolve
|
||||
# owner of these files is "1001" by default - wtf
|
||||
&& chown -R root:root "$NVM_DIR/versions/node/v$NODE_VERSION"
|
||||
&& nvm use $NODE_VERSION
|
||||
# && chown -R root:root "$NVM_DIR/versions/node/v$NODE_VERSION"
|
||||
|
||||
ENV NODE_PATH $NVM_DIR/versions/node//v$NODE_VERSION/lib/node_modules
|
||||
ENV PATH $NVM_DIR/versions/node//v$NODE_VERSION/bin:$PATH
|
||||
|
||||
# RUN pip install --user --extra-index-url $pip_extra_index_url cic-base[full_graph]==$cic_base_version
|
||||
|
||||
RUN useradd --create-home grassroots
|
||||
WORKDIR /home/grassroots
|
||||
USER grassroots
|
||||
|
||||
ARG pip_extra_index_url=https://pip.grassrootseconomics.net:8433
|
||||
ARG cic_base_version=0.1.1a23
|
||||
ARG cic_registry_version=0.5.3a24
|
||||
ARG cic_eth_version=0.10.0a41
|
||||
ARG chainlib_version=0.0.1a21
|
||||
ARG cic_contracts_version=0.0.2a2
|
||||
RUN pip install --user --extra-index-url $pip_extra_index_url cic-base[full_graph]==$cic_base_version \
|
||||
cic-registry==$cic_registry_version \
|
||||
cic-eth==$cic_eth_version \
|
||||
chainlib==$chainlib_version \
|
||||
cic-contracts==$cic_contracts_version
|
||||
|
||||
# ARG cic_bancor_url=https://gitlab.com/grassrootseconomics/cic-bancor.git/
|
||||
# ARG cic_bancor_contracts_url=https://github.com/bancorprotocol/contracts-solidity
|
||||
# RUN echo Compile and install bancor protocol contracts && \
|
||||
# git clone --depth 1 $cic_bancor_url cic-bancor && \
|
||||
# cd cic-bancor
|
||||
|
||||
# RUN cd cic-bancor/python && \
|
||||
# pip install --extra-index-url $pip_extra_index_url .
|
||||
|
||||
# This is a temporary solution for building the Bancor contracts using the bancor protocol repository truffle setup
|
||||
# We should instead flatten the files ourselves and build them with solc in the first image layer in this file
|
||||
ARG cic_bancor_commit=a04c7ae6882ea515938d852cc861d59a35070094
|
||||
ARG cic_bancor_url=https://gitlab.com/grassrootseconomics/cic-bancor.git/
|
||||
ARG cic_bancor_contracts_url=https://github.com/bancorprotocol/contracts-solidity
|
||||
RUN echo Compile and install bancor protocol contracts && \
|
||||
git clone --depth 1 $cic_bancor_url cic-bancor && \
|
||||
cd cic-bancor && \
|
||||
git fetch --depth 1 origin $cic_bancor_commit && \
|
||||
git checkout $cic_bancor_commit && \
|
||||
# Apparently the git version here doesn't have set-url as a command. *sigh*
|
||||
#if [ ! -z $cic_bancor_contracts_url ]; then
|
||||
# git submodule set-url bancor $cic_bancor_contracts_url
|
||||
#fi
|
||||
git submodule init && \
|
||||
git submodule update
|
||||
RUN cd root && \
|
||||
. $NVM_DIR/nvm.sh &&\
|
||||
nvm install $BANCOR_NODE_VERSION && \
|
||||
nvm use $BANCOR_NODE_VERSION && \
|
||||
cd - && \
|
||||
cd cic-bancor/bancor && \
|
||||
npm install --python=/usr/bin/python2 && \
|
||||
node_modules/truffle/build/cli.bundled.js compile && \
|
||||
mkdir -vp /usr/local/share/cic/bancor/solidity/build && \
|
||||
cp -vR solidity/build/contracts /usr/local/share/cic/bancor/solidity/build/
|
||||
RUN cd cic-bancor/python && \
|
||||
pip install --extra-index-url $pip_extra_index_url .
|
||||
# ARG cic_bancor_commit=a04c7ae6882ea515938d852cc861d59a35070094
|
||||
# ARG cic_bancor_url=https://gitlab.com/grassrootseconomics/cic-bancor.git/
|
||||
# ARG cic_bancor_contracts_url=https://github.com/bancorprotocol/contracts-solidity
|
||||
# RUN echo Compile and install bancor protocol contracts && \
|
||||
# git clone --depth 1 $cic_bancor_url cic-bancor && \
|
||||
# cd cic-bancor && \
|
||||
# git fetch --depth 1 origin $cic_bancor_commit && \
|
||||
# git checkout $cic_bancor_commit && \
|
||||
# # Apparently the git version here doesn't have set-url as a command. *sigh*
|
||||
# #if [ ! -z $cic_bancor_contracts_url ]; then
|
||||
# # git submodule set-url bancor $cic_bancor_contracts_url
|
||||
# #fi
|
||||
# git submodule init && \
|
||||
# git submodule update
|
||||
# RUN cd root && \
|
||||
# . $NVM_DIR/nvm.sh &&\
|
||||
# nvm install $BANCOR_NODE_VERSION && \
|
||||
# nvm use $BANCOR_NODE_VERSION && \
|
||||
# cd - && \
|
||||
# cd cic-bancor/bancor && \
|
||||
# npm install --python=/usr/bin/python2 && \
|
||||
# node_modules/truffle/build/cli.bundled.js compile && \
|
||||
# mkdir -vp /usr/local/share/cic/bancor/solidity/build && \
|
||||
# cp -vR solidity/build/contracts /usr/local/share/cic/bancor/solidity/build/
|
||||
# RUN cd cic-bancor/python && \
|
||||
# pip install --extra-index-url $pip_extra_index_url .
|
||||
|
||||
FROM python:3.8.6-slim-buster as runtime-image
|
||||
|
||||
RUN apt-get install -y cargo
|
||||
ARG cic_base_version=0.1.1a10
|
||||
RUN pip install --extra-index-url $pip_extra_index_url cic-base[full_graph]==$cic_base_version
|
||||
RUN apt-get update
|
||||
RUN apt-get install -y --no-install-recommends gnupg libpq-dev
|
||||
|
||||
ARG cic_registry_version=0.5.3a22
|
||||
RUN pip install --extra-index-url $pip_extra_index_url cic-registry==$cic_registry_version
|
||||
COPY --from=compile-image /usr/local/bin/ /usr/local/bin/
|
||||
COPY --from=compile-image /usr/local/etc/cic/ /usr/local/etc/cic/
|
||||
|
||||
WORKDIR /root
|
||||
RUN useradd --create-home grassroots
|
||||
WORKDIR /home/grassroots
|
||||
# COPY python dependencies to user dir
|
||||
COPY --from=compile-image /home/grassroots/.local .local
|
||||
ENV PATH=/home/grassroots/.local/bin:$PATH
|
||||
|
||||
COPY contract-migration/testdata/pgp testdata/pgp
|
||||
COPY contract-migration/wait-for-it.sh .
|
||||
RUN chmod +x ./wait-for-it.sh
|
||||
|
||||
# COPY contract-migration/.env_config_template .env_config_template
|
||||
# COPY contract-migration/.env_dockercompose_template .env_dockercompose_template
|
||||
|
||||
COPY contract-migration/reset.sh reset.sh
|
||||
COPY contract-migration/from_env.sh from_env.sh
|
||||
COPY contract-migration/seed_cic_eth.sh seed_cic_eth.sh
|
||||
COPY contract-migration/sarafu_declaration.json sarafu_declaration.json
|
||||
COPY contract-migration/keystore keystore
|
||||
COPY contract-migration/envlist .
|
||||
|
||||
ENTRYPOINT [ "/bin/bash" ]
|
||||
# RUN chown grassroots:grassroots .local/
|
||||
RUN chown grassroots:grassroots ./
|
||||
RUN chmod gu+x *.sh
|
||||
|
||||
RUN mkdir -p /tmp/cic/config
|
||||
RUN chown grassroots:grassroots /tmp/cic/config
|
||||
# A shared output dir for environment configs
|
||||
RUN chmod a+rwx /tmp/cic/config
|
||||
|
||||
USER grassroots
|
||||
|
||||
ENTRYPOINT [ ]
|
||||
|
||||
61
apps/contract-migration/envlist
Normal file
61
apps/contract-migration/envlist
Normal file
@@ -0,0 +1,61 @@
|
||||
SYNCER_LOOP_INTERVAL
|
||||
SSL_ENABLE_CLIENT
|
||||
SSL_CERT_FILE
|
||||
SSL_KEY_FILE
|
||||
SSL_PASSWORD
|
||||
SSL_CA_FILE
|
||||
BANCOR_DIR
|
||||
REDIS_HOST
|
||||
REDIS_PORT
|
||||
REDIS_DB
|
||||
PGP_EXPORTS_DIR
|
||||
PGP_PRIVATEKEY_FILE
|
||||
PGP_PASSPHRASE
|
||||
DATABASE_USER
|
||||
DATABASE_PASSWORD
|
||||
DATABASE_NAME
|
||||
DATABASE_HOST
|
||||
DATABASE_PORT
|
||||
DATABASE_ENGINE
|
||||
DATABASE_DRIVER
|
||||
DATABASE_DEBUG
|
||||
TASKS_AFRICASTALKING
|
||||
TASKS_SMS_DB
|
||||
TASKS_LOG
|
||||
TASKS_TRACE_QUEUE_STATUS
|
||||
TASKS_TRANSFER_CALLBACKS
|
||||
DEV_MNEMONIC
|
||||
DEV_ETH_RESERVE_ADDRESS
|
||||
DEV_ETH_ACCOUNTS_INDEX_ADDRESS
|
||||
DEV_ETH_RESERVE_AMOUNT
|
||||
DEV_ETH_ACCOUNT_BANCOR_DEPLOYER
|
||||
DEV_ETH_ACCOUNT_CONTRACT_DEPLOYER
|
||||
DEV_ETH_ACCOUNT_GAS_PROVIDER
|
||||
DEV_ETH_ACCOUNT_RESERVE_OWNER
|
||||
DEV_ETH_ACCOUNT_RESERVE_MINTER
|
||||
DEV_ETH_ACCOUNT_ACCOUNTS_INDEX_OWNER
|
||||
DEV_ETH_ACCOUNT_ACCOUNTS_INDEX_WRITER
|
||||
DEV_ETH_ACCOUNT_SARAFU_OWNER
|
||||
DEV_ETH_ACCOUNT_SARAFU_GIFTER
|
||||
DEV_ETH_ACCOUNT_APPROVAL_ESCROW_OWNER
|
||||
DEV_ETH_ACCOUNT_SINGLE_SHOT_FAUCET_OWNER
|
||||
DEV_ETH_SARAFU_TOKEN_NAME
|
||||
DEV_ETH_SARAFU_TOKEN_SYMBOL
|
||||
DEV_ETH_SARAFU_TOKEN_DECIMALS
|
||||
DEV_ETH_SARAFU_TOKEN_ADDRESS
|
||||
DEV_PGP_PUBLICKEYS_ACTIVE_FILE
|
||||
DEV_PGP_PUBLICKEYS_TRUSTED_FILE
|
||||
DEV_PGP_PUBLICKEYS_ENCRYPT_FILE
|
||||
CIC_REGISTRY_ADDRESS
|
||||
CIC_APPROVAL_ESCROW_ADDRESS
|
||||
CIC_TOKEN_INDEX_ADDRESS
|
||||
CIC_ACCOUNTS_INDEX_ADDRESS
|
||||
CIC_DECLARATOR_ADDRESS
|
||||
CIC_CHAIN_SPEC
|
||||
ETH_PROVIDER
|
||||
ETH_ABI_DIR
|
||||
SIGNER_SOCKET_PATH
|
||||
SIGNER_SECRET
|
||||
CELERY_BROKER_URL
|
||||
CELERY_RESULT_URL
|
||||
META_PROVIDER
|
||||
@@ -61,10 +61,10 @@ export DEV_ETH_ACCOUNTS_INDEX_ADDRESS=$CIC_ACCOUNTS_INDEX_ADDRESS
|
||||
export BANCOR_REGISTRY_ADDRESS=$BANCOR_REGISTRY_ADDRESS
|
||||
export CIC_REGISTRY_ADDRESS=$CIC_REGISTRY_ADDRESS
|
||||
export CIC_TRUST_ADDRESS=$DEV_ETH_ACCOUNT_CONTRACT_DEPLOYER
|
||||
|
||||
export CIC_DECLARATOR_ADDRESS=$CIC_DECLARATOR_ADDRESS
|
||||
EOF
|
||||
|
||||
cat $CIC_DATA_DIR/envlist | bash from_env.sh > $CIC_DATA_DIR/.env_all
|
||||
cat ./envlist | bash from_env.sh > $CIC_DATA_DIR/.env_all
|
||||
# popd
|
||||
|
||||
set +a
|
||||
|
||||
@@ -191,9 +191,16 @@ if __name__ == '__main__':
|
||||
|
||||
fa = open(os.path.join(user_dir, 'balances.csv'), 'w')
|
||||
|
||||
for i in range(user_count):
|
||||
|
||||
(eth, phone, o) = gen()
|
||||
i = 0
|
||||
while i < user_count:
|
||||
eth = None
|
||||
phone = None
|
||||
o = None
|
||||
try:
|
||||
(eth, phone, o) = gen()
|
||||
except Exception as e:
|
||||
logg.warning('generate failed, trying anew: {}'.format(e))
|
||||
continue
|
||||
uid = eth[2:].upper()
|
||||
|
||||
print(o)
|
||||
@@ -212,5 +219,7 @@ if __name__ == '__main__':
|
||||
amount = genAmount()
|
||||
fa.write('{},{}\n'.format(eth,amount))
|
||||
logg.debug('pidx {}, uid {}, eth {}, amount {}'.format(pidx, uid, eth, amount))
|
||||
|
||||
i += 1
|
||||
|
||||
fa.close()
|
||||
|
||||
@@ -74,7 +74,7 @@ new cic.PGPKeyStore(
|
||||
importMeta,
|
||||
);
|
||||
|
||||
const batchSize = 50;
|
||||
const batchSize = 16;
|
||||
const batchDelay = 1000;
|
||||
const total = parseInt(process.argv[3]);
|
||||
const workDir = path.join(process.argv[2], 'meta');
|
||||
|
||||
@@ -38,7 +38,7 @@ argparser.add_argument('--redis-host-callback', dest='redis_host_callback', defa
|
||||
argparser.add_argument('--redis-port-callback', dest='redis_port_callback', default=6379, type=int, help='redis port to use for callback')
|
||||
argparser.add_argument('--batch-size', dest='batch_size', default=50, type=int, help='burst size of sending transactions to node')
|
||||
argparser.add_argument('--batch-delay', dest='batch_delay', default=2, type=int, help='seconds delay between batches')
|
||||
argparser.add_argument('--timeout', default=20.0, type=float, help='Callback timeout')
|
||||
argparser.add_argument('--timeout', default=60.0, type=float, help='Callback timeout')
|
||||
argparser.add_argument('-q', type=str, default='cic-eth', help='Task queue')
|
||||
argparser.add_argument('-v', action='store_true', help='Be verbose')
|
||||
argparser.add_argument('-vv', action='store_true', help='Be more verbose')
|
||||
@@ -97,6 +97,7 @@ def register_eth(i, u):
|
||||
callback_queue=args.q,
|
||||
)
|
||||
t = api.create_account(register=True)
|
||||
logg.debug('register {} -> {}'.format(u, t))
|
||||
|
||||
while True:
|
||||
ps.get_message()
|
||||
@@ -112,11 +113,11 @@ def register_eth(i, u):
|
||||
r = json.loads(m['data'])
|
||||
address = r['result']
|
||||
break
|
||||
except TypeError as e:
|
||||
except Exception as e:
|
||||
if m == None:
|
||||
logg.critical('empty response from redis callback (did the service crash?)')
|
||||
logg.critical('empty response from redis callback (did the service crash?) {}'.format(e))
|
||||
else:
|
||||
logg.critical('unexpected response from redis callback: {}'.format(m))
|
||||
logg.critical('unexpected response from redis callback: {} {}'.format(m, e))
|
||||
sys.exit(1)
|
||||
logg.debug('[{}] register eth {} {}'.format(i, u, address))
|
||||
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
cic-base[full_graph]==0.1.1a12
|
||||
cic-eth==0.10.0a38
|
||||
cic-base[full_graph]==0.1.1a23
|
||||
cic-eth==0.10.0a41
|
||||
cic-types==0.1.0a8
|
||||
|
||||
6
apps/contract-migration/seed_cic_eth.sh
Normal file → Executable file
6
apps/contract-migration/seed_cic_eth.sh
Normal file → Executable file
@@ -26,14 +26,11 @@ env_out_file=${CIC_DATA_DIR}/.env_seed
|
||||
init_level_file=${CIC_DATA_DIR}/.init
|
||||
truncate $env_out_file -s 0
|
||||
|
||||
pip install --extra-index-url https://pip.grassrootseconomics.net:8433 chainlib==0.0.1a22
|
||||
|
||||
set -e
|
||||
set -a
|
||||
|
||||
# We need to not install these here...
|
||||
pip install --extra-index-url $DEV_PIP_EXTRA_INDEX_URL cic-eth==0.10.0a38 chainlib==0.0.1a19 cic-contracts==0.0.2a2
|
||||
pip install --extra-index-url $DEV_PIP_EXTRA_INDEX_URL --force-reinstall erc20-transfer-authorization==0.3.0a10
|
||||
|
||||
>&2 echo "create account for gas gifter"
|
||||
old_gas_provider=$DEV_ETH_ACCOUNT_GAS_PROVIDER
|
||||
DEV_ETH_ACCOUNT_GAS_GIFTER=`cic-eth-create $debug --redis-host-callback=$REDIS_HOST --redis-port-callback=$REDIS_PORT --no-register`
|
||||
@@ -129,6 +126,7 @@ export CIC_TOKEN_INDEX_ADDRESS=$CIC_TOKEN_INDEX_ADDRESS
|
||||
>&2 echo "add declarations for sarafu token"
|
||||
token_description_one=`sha256sum sarafu_declaration.json | awk '{ print $1; }'`
|
||||
token_description_two=0x54686973206973207468652053617261667520746f6b656e0000000000000000
|
||||
echo ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> foo $CIC_DECLARATOR_ADDRESSh"
|
||||
>&2 eth-address-declarator-add -y $keystore_file -i $CIC_CHAIN_SPEC -p $ETH_PROVIDER -r $CIC_DECLARATOR_ADDRESS -w $debug $DEV_ETH_SARAFU_TOKEN_ADDRESS $token_description_one
|
||||
>&2 eth-address-declarator-add -y $keystore_file -i $CIC_CHAIN_SPEC -p $ETH_PROVIDER -r $CIC_DECLARATOR_ADDRESS -w $debug $DEV_ETH_SARAFU_TOKEN_ADDRESS $token_description_two
|
||||
|
||||
|
||||
@@ -1,54 +1,35 @@
|
||||
image: docker:19.03.13
|
||||
image:
|
||||
name: gcr.io/kaniko-project/executor:debug
|
||||
entrypoint: [""]
|
||||
|
||||
variables:
|
||||
# docker host
|
||||
DOCKER_HOST: tcp://docker:2376
|
||||
# container, thanks to volume mount from config.toml
|
||||
DOCKER_TLS_CERTDIR: "/certs"
|
||||
# These are usually specified by the entrypoint, however the
|
||||
# Kubernetes executor doesn't run entrypoints
|
||||
# https://gitlab.com/gitlab-org/gitlab-runner/-/issues/4125
|
||||
DOCKER_TLS_VERIFY: 1
|
||||
DOCKER_CERT_PATH: "$DOCKER_TLS_CERTDIR/client"
|
||||
# We are building these from the apps dir to easily share the requirements file there.
|
||||
# It would be nicer to build from the app dir context. TODO figure out a nice way to do this in local DOCKER_TLS_VERIFY
|
||||
CONTEXT: apps/
|
||||
|
||||
services:
|
||||
- docker:19.03.13-dind
|
||||
|
||||
before_script:
|
||||
- docker info
|
||||
KANIKO_CACHE_ARGS: "--cache=true --cache-copy-layers=true --cache-ttl=24h"
|
||||
CONTEXT: $CI_PROJECT_DIR/apps/
|
||||
|
||||
.py_build_merge_request:
|
||||
stage: build
|
||||
before_script:
|
||||
- cd $CONTEXT
|
||||
variables:
|
||||
CI_DEBUG_TRACE: "true"
|
||||
IMAGE_TAG: $APP_NAME:$CI_COMMIT_SHORT_SHA
|
||||
- CI_DEBUG_TRACE: "true"
|
||||
script:
|
||||
- docker build -t $IMAGE_TAG -f $DOCKERFILE_PATH .
|
||||
- mkdir -p /kaniko/.docker
|
||||
- echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > "/kaniko/.docker/config.json"
|
||||
- /kaniko/executor --context $CONTEXT --dockerfile $DOCKERFILE_PATH $KANIKO_CACHE_ARGS --cache-repo $CI_REGISTRY_IMAGE --no-push
|
||||
rules:
|
||||
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
|
||||
when: always
|
||||
|
||||
.py_build_push:
|
||||
stage: build
|
||||
before_script:
|
||||
- cd $CONTEXT
|
||||
- echo "$CI_REGISTRY_PASSWORD" | docker login -u "$CI_REGISTRY_USER" $CI_REGISTRY --password-stdin
|
||||
variables:
|
||||
CI_DEBUG_TRACE: "true"
|
||||
IMAGE_TAG_BASE: $CI_REGISTRY_IMAGE/$APP_NAME:$CI_COMMIT_BRANCH-$CI_COMMIT_SHORT_SHA
|
||||
LATEST_TAG: $CI_REGISTRY_IMAGE/$APP_NAME:latest
|
||||
script:
|
||||
- export IMAGE_TAG="$IMAGE_TAG_BASE-$(date +%F.%H%M%S)"
|
||||
- docker build -t $IMAGE_TAG -f $DOCKERFILE_PATH .
|
||||
- docker push $IMAGE_TAG
|
||||
- docker tag $IMAGE_TAG $LATEST_TAG
|
||||
- docker push $LATEST_TAG
|
||||
rules:
|
||||
stage: build
|
||||
variables:
|
||||
IMAGE_TAG_BASE: $CI_REGISTRY_IMAGE/$APP_NAME:$CI_COMMIT_BRANCH-$CI_COMMIT_SHORT_SHA
|
||||
LATEST_TAG: $CI_REGISTRY_IMAGE/$APP_NAME:latest
|
||||
script:
|
||||
- export IMAGE_TAG="$IMAGE_TAG_BASE-$(date +%F.%H%M%S)"
|
||||
- mkdir -p /kaniko/.docker
|
||||
- echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > "/kaniko/.docker/config.json"
|
||||
# - /kaniko/executor --context $CONTEXT --dockerfile $DOCKERFILE_PATH $KANIKO_CACHE_ARGS --destination $IMAGE_TAG
|
||||
- /kaniko/executor --context $CONTEXT --dockerfile $DOCKERFILE_PATH $KANIKO_CACHE_ARGS --destination $IMAGE_TAG --destination $CI_REGISTRY_IMAGE/$APP_NAME:latest
|
||||
rules:
|
||||
- if: $CI_COMMIT_BRANCH == "master"
|
||||
when: always
|
||||
|
||||
|
||||
@@ -54,6 +54,7 @@ services:
|
||||
volumes:
|
||||
- ./scripts/initdb/create_db.sql:/docker-entrypoint-initdb.d/1-create_all_db.sql
|
||||
- ./apps/cic-meta/scripts/initdb/postgresql.sh:/docker-entrypoint-initdb.d/2-init-cic-meta.sh
|
||||
- ./apps/cic-cache/db/psycopg2/db.sql:/docker-entrypoint-initdb.d/3-init-cic-meta.sql
|
||||
- postgres-db:/var/lib/postgresql/data
|
||||
|
||||
redis:
|
||||
@@ -162,7 +163,43 @@ services:
|
||||
- -c
|
||||
- |
|
||||
if [[ -f /tmp/cic/config/.env ]]; then source /tmp/cic/config/.env; fi
|
||||
/usr/local/bin/cic-cache-tracker -vv
|
||||
/usr/local/bin/cic-cache-trackerd -vv
|
||||
volumes:
|
||||
- contract-config:/tmp/cic/config/:ro
|
||||
|
||||
cic-cache-tasker:
|
||||
build:
|
||||
context: apps
|
||||
dockerfile: cic-cache/docker/Dockerfile
|
||||
environment:
|
||||
CIC_REGISTRY_ADDRESS: $CIC_REGISTRY_ADDRESS # supplied at contract-config after contract provisioning
|
||||
ETH_PROVIDER: ${ETH_PROVIDER:-http://eth:8545}
|
||||
DATABASE_USER: ${DATABASE_USER:-grassroots}
|
||||
DATABASE_PASSWORD: ${DATABASE_PASSWORD:-tralala} # this is is set at initdb see: postgres/initdb/create_db.sql
|
||||
DATABASE_HOST: ${DATABASE_HOST:-postgres}
|
||||
DATABASE_PORT: ${DATABASE_PORT:-5432}
|
||||
DATABASE_NAME: ${DATABASE_NAME_CIC_CACHE:-cic_cache}
|
||||
DATABASE_ENGINE: ${DATABASE_ENGINE:-postgres}
|
||||
DATABASE_DRIVER: ${DATABASE_DRIVER:-psycopg2}
|
||||
DATABASE_DEBUG: 1
|
||||
ETH_ABI_DIR: ${ETH_ABI_DIR:-/usr/local/share/cic/solidity/abi}
|
||||
CIC_TRUST_ADDRESS: ${DEV_ETH_ACCOUNT_CONTRACT_DEPLOYER:-0xEb3907eCad74a0013c259D5874AE7f22DcBcC95C}
|
||||
CIC_CHAIN_SPEC: ${CIC_CHAIN_SPEC:-evm:bloxberg:8996}
|
||||
CELERY_BROKER_URL: redis://redis:6379
|
||||
CELERY_RESULT_URL: redis://redis:6379
|
||||
deploy:
|
||||
restart_policy:
|
||||
condition: on-failure
|
||||
depends_on:
|
||||
- redis
|
||||
- postgres
|
||||
- eth
|
||||
command:
|
||||
- /bin/bash
|
||||
- -c
|
||||
- |
|
||||
if [[ -f /tmp/cic/config/.env ]]; then source /tmp/cic/config/.env; fi
|
||||
/usr/local/bin/cic-cache-taskerd -vv
|
||||
volumes:
|
||||
- contract-config:/tmp/cic/config/:ro
|
||||
|
||||
@@ -192,7 +229,7 @@ services:
|
||||
- |
|
||||
if [[ -f /tmp/cic/config/.env ]]; then source /tmp/cic/config/.env; fi
|
||||
"/usr/local/bin/uwsgi" \
|
||||
--wsgi-file /usr/src/cic-cache/cic_cache/runnable/server.py \
|
||||
--wsgi-file /usr/src/cic-cache/cic_cache/runnable/serverd.py \
|
||||
--http :8000 \
|
||||
--pyargv -vv
|
||||
|
||||
@@ -238,7 +275,7 @@ services:
|
||||
- -c
|
||||
- |
|
||||
if [[ -f /tmp/cic/config/.env ]]; then source /tmp/cic/config/.env; fi
|
||||
./start_tasker.sh -q cic-eth
|
||||
./start_tasker.sh -q cic-eth -vv
|
||||
# command: [/bin/sh, "./start_tasker.sh", -q, cic-eth, -vv ]
|
||||
|
||||
cic-eth-tracker:
|
||||
|
||||
Reference in New Issue
Block a user