def unit_test_gateway_state():
    """
    initialize the state machine with list of 1's
    claim two gateway addresses for deposit
    launch two supbrocesses to release the deposit addresses; on now another later
    check state, wait, check state again
    """
    print("\033c")
    print("\n\nunit test gateway deposit address state machine\n\n")
    initialize_addresses("xrp")
    print(json_ipc(f"xrp_gateway_state.txt"))
    print("\n\nlocking an xrp address\n")
    address_idx = lock_address("xrp")
    print("address index", address_idx)
    print(json_ipc(f"xrp_gateway_state.txt"))
    print("\n\nlocking another xrp address\n")
    address_idx = lock_address("xrp")
    print("address index", address_idx)
    print(json_ipc(f"xrp_gateway_state.txt"))
    print(
        "\n\nlaunching subprocess unlocking xrp address 0 immediately\n\nAND")
    print(
        "\nlaunching second subprocess unlocking xrp address 1 after 10 seconds\n"
    )
    time.sleep(0.1)
    unlock_address("xrp", 0, 0)
    time.sleep(0.1)
    unlock_address("xrp", 1, 10)
    print(json_ipc(f"xrp_gateway_state.txt"))
    print("\n\nprimary process waiting 10 seconds\n")
    time.sleep(11)
    print(json_ipc(f"xrp_gateway_state.txt"))
Beispiel #2
0
def block_maven(maven_id, start, stop):
    """
    BitShares public api consensus of get_block() returns all tx's on a given block
    :param int(maven_id): used for inter process communication channel identification
    :param int(start): beginning block number to get block transaction data
    :param int(stop): latest irreversible block number
    :return None: reports via text file inter process communication
    """
    rpc = wss_handshake("")
    # dictionary that will contain a list of transactions on each block
    blocks = {}
    for block_num in range(start, stop):
        blocks[block_num] = []
    json_ipc(doc=f"block_maven_{maven_id}.txt", text=json_dumps(blocks))
    for block_num in range(start, stop):
        while True:
            try:
                ret = wss_query(
                    rpc,
                    ["database", "get_block", [block_num]])["transactions"]
                if isinstance(ret, list):
                    blocks[block_num] = ret
                    break
                rpc = wss_handshake(rpc)
            except Exception:
                rpc = wss_handshake(rpc)
                continue
    json_ipc(doc=f"block_maven_{maven_id}.txt", text=json_dumps(blocks))
Beispiel #3
0
def block_num_maven(maven_id):
    """
    BTS public api maven opinion of last irreversible block number
    :param int(maven_id) used for inter process communication channel identification
    """
    rpc = ""
    rpc = wss_handshake(rpc)
    while True:
        if not randint(0, 100):
            rpc = wss_handshake(rpc)
        try:
            ret = wss_query(rpc,
                            ["database", "get_dynamic_global_properties", []])
            block_time = from_iso_date(ret["time"])
            if time.time() - block_time > 10:
                continue
            block_num = int(ret["last_irreversible_block_num"])
            latest = json_ipc(doc="block_number.txt")[0]
            if latest + 1000 < block_num < latest - 10:
                continue
            json_ipc(doc=f"block_num_maven_{maven_id}.txt",
                     text=json_dumps([
                         block_num,
                     ]))
        except Exception:
            rpc = wss_handshake(rpc)
        time.sleep(3)
def unlock_address_process(chain, idx, delay):
    """
    check the binary state of the gateway addresses
    reset the freed address state to 1
    """
    time.sleep(delay)
    doc = f"{chain}_gateway_state.txt"
    gateway_state = json_ipc(doc=doc)
    gateway_state[idx] = 1
    json_ipc(doc=doc, text=json_dumps(gateway_state))
def initialize_addresses(chain):
    """
    create the IPC file with a list of ones ie [1,1,1,...]
    the addresses are "all available" on startup.
    """
    initial_state = []
    doc = f"{chain}_gateway_state.txt"
    for _ in GATE[chain]:
        initial_state.append(1)
    json_ipc(doc=doc, text=json_dumps(initial_state))
Beispiel #6
0
def create_database():
    """
    initialize an empty text pipe IPC json_ipc
    :return None:
    """
    os.makedirs(PATH + "pipe", exist_ok=True)
    for maven_id in range(BLOCK_NUM_MAVENS):
        json_ipc(doc=f"block_num_maven_{maven_id}.txt", text=json_dumps([
            0,
        ]))
    json_ipc(doc=f"block_number.txt", text=json_dumps([
        0,
    ]))
def lock_address(chain):
    """
    check the binary state of the gateway addresses
    if an address is available, switch its state to zero and return its index
    else return None
    """
    doc = f"{chain}_gateway_state.txt"
    gateway_idx = None
    gateway_state = json_ipc(doc=doc)
    for idx, state in enumerate(gateway_state):
        if state:
            gateway_idx = idx
            gateway_state[idx] = 0
            break
    json_ipc(doc=doc, text=json_dumps(gateway_state))

    return gateway_idx
def main():
    """
    setting state of all inbound accounts to available
    subprocess auto send all inbound funds to outbound account
    subprocess deposit api server
    subprocess bitshares withdrawal listener
    """
    print("\033c\n")
    print(it("yellow", logo()))
    # initialize the the pipe folder of *txt files
    json_ipc(initialize=True)
    # set state machine to "all incoming accounts available"
    initialize_addresses("xrp")
    initialize_addresses("eos")
    # spawn 3 concurrent gateway subprocesses
    recycling()
    time.sleep(0.2)
    deposit_api_server()
    time.sleep(0.2)
    withdraw_listener_bitshares()
Beispiel #9
0
    def on_get(self, req, resp):
        """
        When there is a get request made to the deposit server api
        User GET request includes the client_id's BitShares account_name
        Select a gateway wallet from list currently available; remove it from the list
        the available address list will be stored in a json_ipc text pipe
        Server RESPONSE is deposit address and timeout
        After timeout or deposit return address to text pipe list
        """
        confirm_time = {
            "eos": 30,
            "xrp": 2,
        }
        # create a millesecond nonce to log this event
        nonce = milleseconds()
        # extract the incoming parameters to a dictionary
        data = dict(req.params)
        timestamp()
        line_number()
        print(it("red", "DEPOSIT SERVER RECEIVED A GET REQUEST"), "\n")
        call(["hostname", "-I"])
        print(data, "\n")
        client_id = data["client_id"]
        uia = data["uia_name"]
        # translate the incoming uia request to the appropriate network
        network = ""
        if uia == GATE["uia"]["xrp"]["asset_name"]:
            network = "xrp"
        elif uia == GATE["uia"]["eos"]["asset_name"]:
            network = "eos"
        print("network", network, "\n")
        if network in ["xrp", "eos"]:
            # lock an address until this transaction is complete
            gateway_idx = lock_address(network)
            print("gateway index", gateway_idx, "\n")
            if gateway_idx is not None:
                timestamp()
                line_number()
                deposit_address = GATE[network][gateway_idx]["public"]
                print("gateway address", deposit_address, "\n")
                # format a response json
                msg = json_dumps({
                    "response":
                    "success",
                    "server_time":
                    nonce,
                    "deposit_address":
                    deposit_address,
                    "gateway_timeout":
                    "30 MINUTES",
                    "msg":
                    (f"Welcome {client_id}, please deposit your gateway issued "
                     +
                     f"{network} asset, to the {uia} gateway 'deposit_address' "
                     +
                     "in this response.  Make ONE transfer to this address, " +
                     "within the gateway_timeout specified. Transactions on " +
                     f"this network take about {confirm_time[network]} " +
                     "minutes to confirm."),
                })
                print(
                    it("red",
                       f"STARTING {network} LISTENER TO ISSUE to {client_id}"),
                    "\n",
                )
                # dispatch the appropriate listener protocol
                listen = {"eos": listener_eosio, "xrp": listener_ripple}
                # in subprocess listen for payment from client_id to gateway[idx]
                # upon receipt issue asset, else timeout
                listener = Process(
                    target=listen[network],
                    args=(gateway_idx, None, "issue", client_id, nonce),
                )
                listener.start()
                print(f"{network}listener started", "\n")

            else:
                msg = json_dumps({
                    "response":
                    "error",
                    "server_time":
                    nonce,
                    "msg":
                    f"all {uia} gateway addresses are in use, " +
                    "please try again later",
                })

        else:
            msg = json_dumps({
                "response":
                "error",
                "server_time":
                nonce,
                "msg":
                f"{uia} is an invalid gateway UIA, please try again",
            })
        # log the response and build the response body with a data dictionary
        doc = str(nonce) + "_" + uia + "_" + client_id + ".txt"
        json_ipc(doc=doc, text=msg)
        time.sleep(
            5)  # allow some time for listener to start before offering address
        print(msg, "\n")
        resp.body = msg
        resp.status = HTTP_200
Beispiel #10
0
def listener_bitshares(selection=None):
    """
    primary listener event loop
    :param int(selection) or None: user choice for demonstration of listener
    :run forever:
    """
    # get node list from github repo for bitshares ui staging; write to file
    nodes = bitshares_nodes()
    options = raw_operations()
    json_ipc(doc="nodes.txt", text=json_dumps(nodes))
    # create a subfolder for the database; write to file
    create_database()
    # initialize block number
    last_block_num = curr_block_num = 0
    # bypass user input... gateway transfer ops
    act = print_op
    if selection is None:
        selection = 0
        act = withdraw
    # spawn subprocesses for gathering streaming consensus irreversible block number
    spawn_block_num_processes()
    # continually listen for last block["transaction"]["operations"]
    print(it("red", "\nINITIALIZING WITHDRAWAL LISTENER\n\n"))
    while True:
        try:
            # get the irreversible block number reported by each maven subprocess
            block_numbers = []
            for maven_id in range(BLOCK_NUM_MAVENS):
                block_num = json_ipc(doc=f"block_num_maven_{maven_id}.txt")[0]
                block_numbers.append(block_num)
            # the current block number is the statistical mode of the mavens
            # NOTE: may throw StatisticsError when no mode
            curr_block_num = mode(block_numbers)
            # print(curr_block_num)
            json_ipc(doc=f"block_number.txt",
                     text=json_dumps([
                         curr_block_num,
                     ]))
            # if the irreverisble block number has advanced
            if curr_block_num > last_block_num:
                print(
                    "\033[F",  # go back one line
                    it("blue", "BitShares Irreversible Block"),
                    it("yellow", curr_block_num),
                    time.ctime()[11:19],
                    it("blue", int(time.time())),
                )
                if last_block_num > 0:  # not on first iter
                    # spawn some new mavens to get prospective block data
                    start = last_block_num + 1
                    stop = curr_block_num + 1
                    spawn_block_processes(start, stop)
                    # inititialize blocks as a dict of empty transaction lists
                    blocks = {}
                    for block_num in range(start, stop):
                        blocks[block_num] = []
                    # get block transactions from each maven subprocesses
                    for maven_id in range(BLOCK_MAVENS):
                        # print(maven_id)
                        maven_blocks = json_ipc(
                            doc=f"block_maven_{maven_id}.txt")
                        # for each block that has past since last update
                        for block_num in range(start, stop):
                            # print(block_num)
                            # get the maven's version of that block from the dictionary
                            # NOTE: may throw KeyError, TODO: find out why?
                            maven_block = maven_blocks[str(block_num)]
                            # append that version to the list
                            # of maven opinions for that block number
                            blocks[block_num].append(json_dumps(maven_block))
                    # get the mode of the mavens for each block in the blocks dict
                    # NOTE: may throw StatisticsError when no mode
                    # for example half the nodes are on the next block number
                    blocks = {
                        k: json_loads(mode(v))
                        for k, v in blocks.items()
                    }
                    # triple nested:
                    # for each operation, in each transaction, on each block
                    for block_num, transactions in blocks.items():
                        for item, trx in enumerate(transactions):
                            for op in trx["operations"]:
                                # add the block and transaction numbers to the operation
                                op[1]["block"] = block_num
                                op[1]["trx"] = item + 1
                                op[1]["operation"] = (op[0], options[op[0]])
                                # spin off withdrawal act so listener can continue
                                process = Process(target=act, args=(op, ))
                                process.start()
                last_block_num = curr_block_num
            time.sleep(6)
        # statistics and key errors can be safely ignored, restart loop
        except (StatisticsError, KeyError):
            continue
        # in all other cases provide stack trace
        except Exception as error:
            print("\n\n", it("yellow", error), "\n\n")
            print(traceback.format_exc(), "\n")
            continue