Beispiel #1
0
def restart_nonce(
    logger: Logger,
    dbsession: Session,
    network: str,
    ethereum_node_url: str,
    ethereum_private_key: str,
    ethereum_gas_limit: str,
    ethereum_gas_price: str,
):
    check_good_private_key(ethereum_private_key)

    web3 = create_web3(ethereum_node_url)

    service = EthereumStoredTXService(network, dbsession, web3,
                                      ethereum_private_key, ethereum_gas_price,
                                      ethereum_gas_limit, BroadcastAccount,
                                      PreparedTransaction)

    service.ensure_accounts_in_sync()

    account = service.get_or_create_broadcast_account()
    txs = service.get_last_transactions(limit=1)
    if txs.count() > 0:
        raise HistoryDeleteNeeded(
            "Cannot reset nonce as the database contains txs for {}. Delete database to restart."
            .format(service.address))

    # read nonce from the network and record to the database
    tx_count = web3.eth.getTransactionCount(service.address)
    account.current_nonce = tx_count

    logger.info("Address %s, nonce is now set to %d", service.address,
                account.current_nonce)
    dbsession.flush()
Beispiel #2
0
def broadcast(
    logger: Logger,
    dbsession: Session,
    network: str,
    ethereum_node_url: Union[str, Web3],
    ethereum_private_key: str,
    ethereum_gas_limit: Optional[str],
    ethereum_gas_price: Optional[str],
    commit=True,
):
    """Issue out a new Ethereum token."""

    check_good_private_key(ethereum_private_key)

    web3 = create_web3(ethereum_node_url)

    service = EthereumStoredTXService(network, dbsession, web3,
                                      ethereum_private_key, ethereum_gas_price,
                                      ethereum_gas_limit, BroadcastAccount,
                                      PreparedTransaction)

    service.ensure_accounts_in_sync()

    pending_broadcasts = service.get_pending_broadcasts()

    logger.info("Pending %d transactions for broadcasting in network %s",
                pending_broadcasts.count(), network)

    if pending_broadcasts.count() == 0:
        logger.info(
            "No new transactions to broadcast. Use sto tx-update command to see tx status."
        )
        return []

    account = Account.privateKeyToAccount(ethereum_private_key)
    balance = web3.eth.getBalance(account.address)

    logger.info("Our address %s has ETH balance of %f for operations",
                account.address, from_wei(balance, "ether"))

    txs = list(pending_broadcasts)
    # https://stackoverflow.com/questions/41985993/tqdm-show-progress-for-a-generator-i-know-the-length-of
    for tx in tqdm(txs, total=pending_broadcasts.count()):
        try:
            service.broadcast(tx)
            # logger.info("Broadcasted %s", tx.txid)
        except Exception as e:
            logger.exception(e)
            logger.error("Failed to broadcast transaction %s: %s", tx.txid,
                         tx.human_readable_description)
            raise e

        if commit:
            dbsession.commit()  # Try to minimise file system sync issues

    return txs
Beispiel #3
0
def distribute_single(logger: Logger, dbsession: Session, network: str,
                      ethereum_node_url: Union[str, Web3],
                      ethereum_abi_file: Optional[str],
                      ethereum_private_key: Optional[str],
                      ethereum_gas_limit: Optional[int],
                      ethereum_gas_price: Optional[int], token_address: str,
                      ext_id: str, email: str, name: str, to_address: str,
                      amount: Decimal) -> bool:
    """Send out a single transfer.

    :return: True if a new tx for broadcasting was created
    """

    d = DistributionEntry(ext_id, email, name, to_address, amount)

    check_good_private_key(ethereum_private_key)

    abi = get_abi(ethereum_abi_file)

    web3 = create_web3(ethereum_node_url)

    service = EthereumStoredTXService(network, dbsession, web3,
                                      ethereum_private_key, ethereum_gas_price,
                                      ethereum_gas_limit, BroadcastAccount,
                                      PreparedTransaction)

    logger.info(
        "Starting creating distribution transactions for %s token from nonce %s",
        token_address, service.get_next_nonce())

    total = d.amount * 10**18

    available = service.get_raw_token_balance(token_address, abi)
    if total > available:
        raise NotEnoughTokens(
            "Not enough tokens for distribution. Account {} has {} raw token balance, needed {}"
            .format(service.get_or_create_broadcast_account().address,
                    available, total))

    if not service.is_distributed(d.external_id, token_address):
        # Going to tx queue
        raw_amount = int(d.amount * 10**18)
        note = "Distributing tokens, raw amount: {}".format(raw_amount)
        service.distribute_tokens(d.external_id, d.address, raw_amount,
                                  token_address, abi, note)
        logger.info("New broadcast has been created")
        return True
    else:
        logger.error("Already distributed")
        return False
Beispiel #4
0
def distribute_tokens(logger: Logger, dbsession: Session, network: str,
                      ethereum_node_url: Union[str, Web3],
                      ethereum_abi_file: Optional[str],
                      ethereum_private_key: Optional[str],
                      ethereum_gas_limit: Optional[int],
                      ethereum_gas_price: Optional[int], token_address: str,
                      dists: List[DistributionEntry]) -> Tuple[int, int]:
    """Sends tokens to their first owners in primary markets."""

    check_good_private_key(ethereum_private_key)

    abi = get_abi(ethereum_abi_file)

    web3 = create_web3(ethereum_node_url)

    service = EthereumStoredTXService(network, dbsession, web3,
                                      ethereum_private_key, ethereum_gas_price,
                                      ethereum_gas_limit, BroadcastAccount,
                                      PreparedTransaction)

    logger.info(
        "Starting creating distribution transactions for %s token from nonce %s",
        token_address, service.get_next_nonce())

    total = sum([dist.amount * 10**18 for dist in dists])

    available = service.get_raw_token_balance(token_address, abi)
    if total > available:
        raise NotEnoughTokens(
            "Not enough tokens for distribution. Account {} has {} raw token balance, needed {}"
            .format(service.get_or_create_broadcast_account().address,
                    available, total))

    new_distributes = old_distributes = 0

    for d in tqdm(dists):
        if not service.is_distributed(d.external_id, token_address):
            # Going to tx queue
            raw_amount = int(d.amount * 10**18)
            note = "Distributing tokens, raw amount: {}".format(raw_amount)
            service.distribute_tokens(d.external_id, d.address, raw_amount,
                                      token_address, abi, note)
            new_distributes += 1
        else:
            # CSV reimports
            old_distributes += 1

    logger.info("Prepared transactions for broadcasting for network %s",
                network)
    return new_distributes, old_distributes
Beispiel #5
0
def update_status(
    logger: Logger,
    dbsession: Session,
    network: str,
    ethereum_node_url: Union[str, Web3],
    ethereum_private_key: str,
    ethereum_gas_limit: str,
    ethereum_gas_price: str,
    commit=True,
):
    """Issue out a new Ethereum token."""

    check_good_private_key(ethereum_private_key)

    web3 = create_web3(ethereum_node_url)

    service = EthereumStoredTXService(network, dbsession, web3,
                                      ethereum_private_key, ethereum_gas_price,
                                      ethereum_gas_limit, BroadcastAccount,
                                      PreparedTransaction)

    unfinished_txs = service.get_unmined_txs()

    logger.info(
        "Updating status for %d unfinished transactions for broadcasting in network %s",
        unfinished_txs.count(), network)

    if unfinished_txs.count() == 0:
        logger.info(
            "No transactions to update. Use sto tx-last command to show the status of the last transactions."
        )
        return []

    unfinished_txs = list(unfinished_txs)

    # https://stackoverflow.com/questions/41985993/tqdm-show-progress-for-a-generator-i-know-the-length-of
    for tx in tqdm(unfinished_txs):
        service.update_status(tx)
        if commit:
            dbsession.commit()  # Try to minimise file system sync issues

    return unfinished_txs
Beispiel #6
0
def next_nonce(
    logger: Logger,
    dbsession: Session,
    network: str,
    ethereum_node_url: str,
    ethereum_private_key: str,
    ethereum_gas_limit: str,
    ethereum_gas_price: str,
):
    check_good_private_key(ethereum_private_key)

    web3 = create_web3(ethereum_node_url)

    service = EthereumStoredTXService(network, dbsession, web3,
                                      ethereum_private_key, ethereum_gas_price,
                                      ethereum_gas_limit, BroadcastAccount,
                                      PreparedTransaction)
    account = service.get_or_create_broadcast_account()
    ft = pretty_date(account.created_at)
    logger.info("Address %s, created at %s, nonce is now set to %d",
                service.address, ft, account.current_nonce)
Beispiel #7
0
def get_last_transactions(logger: Logger,
              dbsession: Session,
              network: str,
              limit: int,
              ethereum_node_url: str,
              ethereum_private_key: str,
              ethereum_gas_limit: str,
              ethereum_gas_price: str,
):
    """Issue out a new Ethereum token."""

    check_good_private_key(ethereum_private_key)

    web3 = create_web3(ethereum_node_url)

    service = EthereumStoredTXService(network, dbsession, web3, ethereum_private_key, ethereum_gas_price, ethereum_gas_limit, BroadcastAccount, PreparedTransaction)

    last_txs = service.get_last_transactions(limit)
    if last_txs.count() == 0:
        logger.info("No transactions yet")
        return []

    return list(last_txs)
Beispiel #8
0
def deploy_token_contracts(
        logger: Logger, dbsession: Session, network: str,
        ethereum_node_url: Union[str, Web3], ethereum_abi_file: Optional[str],
        ethereum_private_key: Optional[str], ethereum_gas_limit: Optional[int],
        ethereum_gas_price: Optional[int], name: str, symbol: str, url: str,
        amount: int, transfer_restriction: str):
    """Issue out a new Ethereum token."""

    assert type(amount) == int
    decimals = 18  # Everything else is bad idea

    check_good_private_key(ethereum_private_key)

    abi = get_abi(ethereum_abi_file)

    web3 = create_web3(ethereum_node_url)

    # We do not have anything else implemented yet
    assert transfer_restriction == "unrestricted"

    service = EthereumStoredTXService(network, dbsession, web3,
                                      ethereum_private_key, ethereum_gas_price,
                                      ethereum_gas_limit, BroadcastAccount,
                                      PreparedTransaction)

    # Deploy security token
    note = "Deploying token contract for {}".format(name)
    deploy_tx1 = service.deploy_contract("SecurityToken",
                                         abi,
                                         note,
                                         constructor_args={
                                             "_name": name,
                                             "_symbol": symbol,
                                             "_url": url
                                         })  # See SecurityToken.sol

    # Deploy transfer agent
    note = "Deploying unrestricted transfer policy for {}".format(name)
    deploy_tx2 = service.deploy_contract("UnrestrictedTransferAgent", abi,
                                         note)

    # Set transfer agent
    note = "Making transfer restriction policy for {} effective".format(name)
    contract_address = deploy_tx1.contract_address
    update_tx1 = service.interact_with_contract(
        "SecurityToken", abi, contract_address, note, "setTransactionVerifier",
        {"newVerifier": deploy_tx2.contract_address})

    # Issue out initial shares
    note = "Creating {} initial shares for {}".format(amount, name)
    contract_address = deploy_tx1.contract_address
    amount_18 = int(amount * 10**decimals)
    update_tx2 = service.interact_with_contract("SecurityToken", abi,
                                                contract_address, note,
                                                "issueTokens",
                                                {"value": amount_18})

    logger.info("Prepared transactions for broadcasting for network %s",
                network)
    logger.info("STO token contract address will be %s%s%s",
                colorama.Fore.LIGHTGREEN_EX, deploy_tx1.contract_address,
                colorama.Fore.RESET)
    return [deploy_tx1, deploy_tx2, update_tx1, update_tx2]
Beispiel #9
0
def diagnose(logger: Logger,
             node_url: str,
             private_key_hex: str,
             check_timestamps=True) -> Optional[Exception]:
    """Run Ethereum connection and account diagnostics.

    Check that the user has properly configured Ethereum node and private key.

    Never fails. Exceptions are written to the logger output and returned.
    """

    try:
        check_good_node_url(node_url)

        logger.info("Attempting to connect to Ethereum node %s", node_url)
        web3 = create_web3(node_url)

        logger.info("Connected to Ethereum node software %s",
                    web3.version.node)

        block_num = web3.eth.blockNumber

        d = datetime.datetime.utcnow()
        unix_time = calendar.timegm(d.utctimetuple())

        block_info = web3.eth.getBlock(block_num)
        last_time = block_info["timestamp"]

        if check_timestamps:
            if last_time == 0:
                raise NodeNotSynced(
                    "Looks like your node has not yet been synced.")

            ago = unix_time - last_time

            logger.info(
                "Current Ethereum node block number: %d, last block %d seconds ago - compare this to data on https://etherscan.io",
                block_num, ago)

            if ago < 0:
                raise NodeNotSynced(
                    "Last block in the future? Do we have a clock with a wrong timezone somewhere?"
                )

            if ago > 1800:
                raise NodeNotSynced(
                    "Looks like your node has not received a block for half an hour. It is most likely unsynced at the moment."
                )

        check_good_private_key(private_key_hex)

        logger.info("Using private key %s...", private_key_hex[0:3])
        account = Account.privateKeyToAccount(to_bytes(hexstr=private_key_hex))

        balance = web3.eth.getBalance(account.address)
        logger.info("Address %s has ETH balance of %f", account.address,
                    from_wei(balance, "ether"))

        if balance == 0:
            raise NeedMoney(
                "Your Ethereum account {} needs to have ETH in order to use this tool"
                .format(account.address))

    except Exception as e:

        logger.error("Diagnostics failure")
        logger.exception(e)
        return e