Skip to content

Commit

Permalink
Merge pull request #3 from TokenMarketNet/refactor-tr-107
Browse files Browse the repository at this point in the history
Refactor tr 107
  • Loading branch information
miohtama committed Jan 28, 2019
2 parents 1e7ed79 + 400f4fe commit 85e4a90
Show file tree
Hide file tree
Showing 7 changed files with 332 additions and 33 deletions.
51 changes: 27 additions & 24 deletions sto/cli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -344,30 +344,8 @@ def broadcast(config: BoardCommmadConfiguration):
Send all management account transactions to Ethereum network.
After a while, transactions are picked up by miners and included in the blockchain.
"""

assert is_ethereum_network(config.network)

logger = config.logger

from sto.ethereum.broadcast import broadcast

dbsession = config.dbsession

txs = broadcast(logger,
dbsession,
config.network,
ethereum_node_url=config.ethereum_node_url,
ethereum_private_key=config.ethereum_private_key,
ethereum_gas_limit=config.ethereum_gas_limit,
ethereum_gas_price=config.ethereum_gas_price)

if txs:
from sto.ethereum.txservice import EthereumStoredTXService
EthereumStoredTXService.print_transactions(txs)
logger.info("Run %ssto tx-update%s to monitor your transaction propagation status", colorama.Fore.LIGHTCYAN_EX, colorama.Fore.RESET)

# Write database
dbsession.commit()
from sto.ethereum.utils import broadcast as _broadcast
_broadcast(config)


@cli.command(name="tx-update")
Expand Down Expand Up @@ -601,6 +579,31 @@ def version(config: BoardCommmadConfiguration):
print(config.version)


@cli.command(name="kyc-deploy")
@click.pass_obj
def kyc_deploy(config: BoardCommmadConfiguration):
"""
Deploys Kyc contract to desired ethereum network
network, ethereum-abi-file, ethereum-private-key, ethereum-node-url are required args
"""
from sto.ethereum.utils import deploy_contract
deploy_contract(config, contract_name='BasicKYC')


@cli.command(name="kyc-manage")
@click.option('--whitelist-address', required=True, help="address to whitelist", type=str)
@click.pass_obj
def kyc_manage(config: BoardCommmadConfiguration, whitelist_address):
"""
Whitelist a address in KYC smart contract
network, ethereum-abi-file, ethereum-private-key, ethereum-node-url are required args
"""
from sto.ethereum.utils import whitelist_kyc_address
whitelist_kyc_address(
config=config,
address=whitelist_address
)


def main():
# https://github.com/pallets/click/issues/204#issuecomment-270012917
Expand Down
2 changes: 1 addition & 1 deletion sto/ethereum/contracts-flattened.json

Large diffs are not rendered by default.

159 changes: 158 additions & 1 deletion sto/ethereum/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

from typing import Optional

import colorama
import rlp
from eth_abi import encode_abi
from web3 import Web3, HTTPProvider
Expand All @@ -13,6 +14,9 @@
from eth_utils import keccak, to_checksum_address, to_bytes, is_hex_address, is_checksum_address
from web3.utils.contracts import encode_abi

from sto.cli.main import is_ethereum_network


class NoNodeConfigured(Exception):
pass

Expand All @@ -21,7 +25,6 @@ class NeedPrivateKey(Exception):
pass



def check_good_node_url(node_url: str):
if not node_url:
raise NoNodeConfigured("You need to give --ethereum-node-url command line option or set it up in a config file")
Expand Down Expand Up @@ -52,6 +55,9 @@ def create_web3(url: str) -> Web3:
return Web3(HTTPProvider(url))


def integer_hash(number: int):
return int(keccak(number).hex(), 16)


def mk_contract_address(sender: str, nonce: int) -> str:
"""Create a contract address using eth-utils.
Expand Down Expand Up @@ -163,5 +169,156 @@ def getLogs(self,
yield get_event_data(abi, entry)


def priv_key_to_address(private_key):
from eth_account import Account
acc = Account.privateKeyToAccount(private_key)
return acc.address


def deploy_contract(config, contract_name, constructor_args=()):
tx = get_contract_deployed_tx(config.dbsession, contract_name)
if tx:
config.logger.error(
'contract already deployed at address: {}'.format(tx.contract_address)
)
return

from sto.ethereum.txservice import EthereumStoredTXService
from sto.models.implementation import BroadcastAccount, PreparedTransaction

assert is_ethereum_network(config.network)

check_good_private_key(config.ethereum_private_key)

abi = get_abi(config.ethereum_abi_file)

web3 = create_web3(config.ethereum_node_url)

service = EthereumStoredTXService(
config.network,
config.dbsession,
web3,
config.ethereum_private_key,
config.ethereum_gas_price,
config.ethereum_gas_limit,
BroadcastAccount,
PreparedTransaction
)
note = "Deploying contract {}".format(contract_name)
service.deploy_contract(
contract_name=contract_name,
abi=abi,
note=note,
constructor_args=constructor_args
)
# Write database
dbsession = config.dbsession
dbsession.commit()
# deploy on ethereum network
broadcast(config)


def broadcast(config):
# extracted this out as a separate method so that
# this code can be re used elsewhere
assert is_ethereum_network(config.network)

logger = config.logger

from sto.ethereum.broadcast import broadcast

dbsession = config.dbsession

txs = broadcast(logger,
dbsession,
config.network,
ethereum_node_url=config.ethereum_node_url,
ethereum_private_key=config.ethereum_private_key,
ethereum_gas_limit=config.ethereum_gas_limit,
ethereum_gas_price=config.ethereum_gas_price)

if txs:
from sto.ethereum.txservice import EthereumStoredTXService
EthereumStoredTXService.print_transactions(txs)
logger.info("Run %ssto tx-update%s to monitor your transaction propagation status", colorama.Fore.LIGHTCYAN_EX,
colorama.Fore.RESET)

# Write database
dbsession.commit()


def deploy_contract_on_eth_network(
web3,
abi,
bytecode,
bytecode_runtime,
private_key,
ethereum_gas_limit,
ethereum_gas_price,
constructor_args
):
from web3.middleware.signing import construct_sign_and_send_raw_middleware
# the following code helps deploying using infura
web3.middleware_stack.add(construct_sign_and_send_raw_middleware(private_key))

contract = web3.eth.contract(
abi=abi,
bytecode=bytecode,
bytecode_runtime=bytecode_runtime
)
tx_kwargs = {
'from': priv_key_to_address(private_key)
}
if ethereum_gas_limit:
tx_kwargs['gas'] = ethereum_gas_limit
if ethereum_gas_price:
tx_kwargs['gasPrice'] = ethereum_gas_price

tx_hash = contract.constructor(*constructor_args).transact(tx_kwargs)
receipt = web3.eth.waitForTransactionReceipt(tx_hash)
assert receipt['status'] == 1, "failed to deploy contract"
return receipt['contractAddress']


def get_contract_deployed_tx(dbsession, contract_name):
from sto.models.implementation import PreparedTransaction
return dbsession.query(PreparedTransaction).filter(
PreparedTransaction.filter_by_contract_name(contract_name)
).first()


def whitelist_kyc_address(config, address):
from sto.ethereum.txservice import EthereumStoredTXService
from sto.models.implementation import BroadcastAccount, PreparedTransaction

tx = get_contract_deployed_tx(config.dbsession, 'BasicKYC')
if not tx:
raise Exception(
'BasicKyc contract is not deployed. '
'invoke command kyc_deploy to deploy the smart contract'
)

web3 = create_web3(config.ethereum_node_url)

service = EthereumStoredTXService(
config.network,
config.dbsession,
web3,
config.ethereum_private_key,
config.ethereum_gas_price,
config.ethereum_gas_limit,
BroadcastAccount,
PreparedTransaction
)
abi = get_abi(config.ethereum_abi_file)

service.interact_with_contract(
contract_name='BasicKYC',
abi=abi,
address=tx.contract_address,
note='whitelisting address {0}'.format(address),
func_name='whitelistUser',
args={'who': address, 'status': True}
)
broadcast(config)

5 changes: 5 additions & 0 deletions sto/models/broadcastaccount.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,3 +179,8 @@ def is_token_contract_deployment(self) -> bool:
"""
return self.contract_name in ("SecurityToken",)

@classmethod
def filter_by_contract_name(cls, contract_name):
return sa.cast(
cls.other_data['abi']['name'], sa.String
) == '"{0}"'.format(contract_name)
97 changes: 94 additions & 3 deletions tests/cli/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,47 @@
import os

import pytest

from sto.distribution import read_csv
from sto.ethereum.broadcast import broadcast
from sto.ethereum.distribution import distribute_tokens
from sto.ethereum.issuance import deploy_token_contracts, contract_status
from sto.ethereum.status import update_status
from sto.models.broadcastaccount import _PreparedTransaction
from sto.cli.main import cli
from sto.ethereum.utils import get_abi


@pytest.fixture
def security_token(web3, private_key_hex):
from sto.ethereum.utils import deploy_contract_on_eth_network, get_abi
args = ["SecurityToken", "SEC", "http://tokenmarket.net/"] # Owner set
abi = get_abi(None)['SecurityToken']

return deploy_contract_on_eth_network(
web3,
abi['abi'],
abi['bytecode'],
abi['bytecode_runtime'],
private_key_hex,
None,
None,
constructor_args=args
)


@pytest.fixture
def kyc_contract(click_runner, dbsession, db_path, private_key_hex):
result = click_runner.invoke(
cli,
[
'--database-file', db_path,
'--ethereum-private-key', private_key_hex,
'kyc-deploy'
]
)
assert result.exit_code == 0
tx = get_contract_deployed_tx(dbsession, 'BasicKYC')
return tx.contract_address


def test_issuance(logger, dbsession, web3, private_key_hex):
Expand Down Expand Up @@ -164,9 +199,65 @@ def test_distribute(logger, dbsession, web3, private_key_hex, sample_csv_file):
assert old_distributes == 2


def test_kyc_deploy(
dbsession,
private_key_hex,
db_path,
monkeypatch_create_web3,
monkeypatch_get_contract_deployed_tx,
get_contract_deployed_tx,
web3,
click_runner
):
result = click_runner.invoke(
cli,
[
'--database-file', db_path,
'--ethereum-private-key', private_key_hex,
'kyc-deploy'
]
)
assert result.exit_code == 0
tx = get_contract_deployed_tx(dbsession, 'BasicKYC')
assert tx.contract_name == 'BasicKYC'
assert tx.contract_address is not None
assert web3.eth.getCode(tx.contract_address) not in ['0x', None]


def test_kyc_manage(
dbsession,
private_key_hex,
web3,
db_path,
monkeypatch_create_web3,
monkeypatch_get_contract_deployed_tx,
get_contract_deployed_tx,
click_runner
):
result = click_runner.invoke(
cli,
[
'--database-file', db_path,
'--ethereum-private-key', private_key_hex,
'kyc-deploy'
]
)
assert result.exit_code == 0
tx = get_contract_deployed_tx(dbsession, 'BasicKYC')
abi = get_abi(None)
kyc_contract = web3.eth.contract(address=tx.contract_address, abi=abi['BasicKYC']['abi'])

eth_address = web3.eth.account.create().address



result = click_runner.invoke(
cli,
[
'--database-file', db_path,
'--ethereum-private-key', private_key_hex,
'--ethereum-gas-limit', 80000,
'kyc-manage',
'--whitelist-address', eth_address
]
)
assert result.exit_code == 0
assert kyc_contract.functions.isWhitelisted(eth_address).call() == True

0 comments on commit 85e4a90

Please sign in to comment.