Exemplo n.º 1
0
def send_receipts(l5_block: "l5_block_model.L5BlockModel") -> None:
    receipt_path = "/v1/receipt"
    get_claim_path = "/v1/claim"
    chain_id_set = set()
    _log.info(f"l5 block to loop {l5_block.__dict__}")
    _log.info(f"Sending receipts to {len(l5_block.l4_blocks)} lower nodes")
    for l4_block in l5_block.l4_blocks:
        try:
            block_dictionary = json.loads(l4_block)
            chain_id = block_dictionary["l1_dc_id"]
            block = block_dictionary["l1_block_id"]
            full_claim_path = f"{get_claim_path}/{block}"
            # Get the claim data for billing
            claim_url = f"{matchmaking.get_dragonchain_address(chain_id)}{full_claim_path}"
            headers, _ = authorization.generate_authenticated_request("GET", chain_id, full_claim_path)
            _log.info(f"getting claim for {block} from {chain_id}")
            try:
                _log.info(f"----> {claim_url}")
                r = requests.get(claim_url, headers=headers, timeout=30)
                _log.info(f"<---- {r.status_code} {r.text}")
            except Exception:
                _log.exception("Failed to get claim!")
            if r.status_code != 200:
                _log.error(f"Claim check failed! Rejecting block {block} from {chain_id}")
                continue
            else:
                claim = r.json()
                # Add this L5's proof to the block
                _log.info(f"Claim received from l1 {claim}")
                _log.info(f"data points blockid {l5_block.block_id}  signature {l5_block.proof}")
                block_data = {}
                block_data["blockId"] = l5_block.block_id
                block_data["signature"] = l5_block.proof
                claim["validations"]["l5"][l5_block.dc_id] = block_data
                chain_id_set.add(chain_id)
                _log.info(f"Sending filled claim {claim}")
                try:
                    claim_check_id = f"{chain_id}-{block}"
                    matchmaking.resolve_claim_check(claim_check_id)
                except Exception:
                    _log.exception("Failure to finalize claim in matchmaking. Sending reciepts to lower level nodes.")
        except Exception as e:
            _log.exception(f"[BROADCAST] Error while trying to broadcast down for l4 block {l4_block}\n{e}\n!Will ignore this broadcast!")

    payload = l5_block.export_as_at_rest()
    for chain_id in chain_id_set:
        try:
            headers, data = authorization.generate_authenticated_request("POST", chain_id, receipt_path, payload)
            url = f"{matchmaking.get_dragonchain_address(chain_id)}{receipt_path}"
            _log.info(f"----> {url}")
            r = requests.post(url, data=data, headers=headers, timeout=30)
            _log.info(f"<---- {r.status_code} {r.text}")
            if r.status_code != 200:
                # TODO failed to enqueue block to specific l1, consider another call to matchmaking, etc
                _log.info(f"[BROADCAST] WARNING: failed to transmit to {chain_id} with error {r.text}")
            else:
                _log.info("[BROADCAST] Sucessful receipt sent down to L1")
        except Exception:
            _log.error(f"[BROADCAST] ERROR: Couldn't broadcast receipt down to {chain_id}! Ignoring")
Exemplo n.º 2
0
def make_broadcast_futures(session: aiohttp.ClientSession, block_id: str, level: int, chain_ids: set) -> Optional[Set[asyncio.Task]]:
    """Initiate broadcasts for a block id to certain higher level nodes
    Args:
        session: aiohttp session to use for making http requests
        block_id: the block id to broadcast
        level: higher level of the chain_ids to broadcast to
        chain_ids: set of (level) chains to broadcast to
    Returns:
        Set of asyncio futures for the http requests initialized (None if it was not possible to get broadcast dto)
    """
    path = "/v1/enqueue"
    broadcasts = set()
    try:
        broadcast_dto = block_dao.get_broadcast_dto(level, block_id)
    except exceptions.NotEnoughVerifications as e:
        _log.warning(f"[BROADCAST PROCESSOR] {str(e)}")
        _log.info(f"[BROADCAST PROCESSOR] Will attempt to broadcast block {block_id} next run")
        broadcast_functions.increment_storage_error_sync(block_id, level)
        return None
    _log.debug(f"[BROADCAST PROCESSOR] Sending broadcast(s) for {block_id} level {level}:\n{broadcast_dto}")
    for chain in chain_ids:
        try:
            headers, data = authorization.generate_authenticated_request("POST", chain, path, broadcast_dto)
            if level != 5:
                headers["deadline"] = str(BROADCAST_RECEIPT_WAIT_TIME)
            else:
                headers["deadline"] = str(get_l5_wait_time(chain))
            url = f"{matchmaking.get_dragonchain_address(chain)}{path}"
            _log.info(f"[BROADCAST PROCESSOR] Firing transaction for {chain} (level {level}) at {url}")
            broadcasts.add(asyncio.create_task(session.post(url=url, data=data, headers=headers, timeout=HTTP_REQUEST_TIMEOUT)))
        except Exception:
            _log.exception(f"[BROADCAST PROCESSOR] Exception trying to broadcast to {chain}")
    return broadcasts
Exemplo n.º 3
0
    def test_generated_authenticated_request_with_verifier(
            self, mock_get_auth_key, mock_date, mock_is_replay, mock_get_id):
        """
        This is more of psuedo integration test, ensuring that
        the generate_authenticated_request 'POST', will generate things are properly
        validated by verify_request_authorization

        If this test ever fails, it means that interchain communication will be broken
        even if all other tests pass
        """
        full_path = "/path"
        dcid = "test_dcid"
        timestamp = "2018-11-14T09:05:25.128176Z"
        json_content = {"thing": "test"}
        headers, content = authorization.generate_authenticated_request(
            "POST", dcid, full_path, json_content, "SHA256")
        auth_str = headers["Authorization"]
        # Test with SHA256 HMAC Auth
        authorization.verify_request_authorization(auth_str, "POST", full_path,
                                                   dcid, timestamp,
                                                   "application/json", content,
                                                   False, "api_keys", "create",
                                                   "create_api_key")
        headers, content = authorization.generate_authenticated_request(
            "POST", dcid, full_path, json_content, "BLAKE2b512")
        auth_str = headers["Authorization"]
        # Test with BLAKE2b512 HMAC Auth
        authorization.verify_request_authorization(auth_str, "POST", full_path,
                                                   dcid, timestamp,
                                                   "application/json", content,
                                                   False, "api_keys", "create",
                                                   "create_api_key")
        headers, content = authorization.generate_authenticated_request(
            "POST", dcid, full_path, json_content, "SHA3-256")
        auth_str = headers["Authorization"]
        # Test with SHA3-256 HMAC Auth
        authorization.verify_request_authorization(auth_str, "POST", full_path,
                                                   dcid, timestamp,
                                                   "application/json", content,
                                                   False, "api_keys", "create",
                                                   "create_api_key")
Exemplo n.º 4
0
def make_matchmaking_request(
    http_verb: str, path: str, json_content: dict = None, retry: bool = True, authenticated: bool = True
) -> requests.Response:
    """Make an authenticated request to matchmaking and return the response
    Args:
        http_verb: GET, POST, etc
        path: path of the request
        json_content: OPTIONAL if the request needs a post body, include it as a dictionary
        retry: boolean whether or not to recursively retry on recoverable errors (i.e. no auth, missing registration)
            Note: This should not be provided manually, and is only for recursive calls within the function it
        authenticated: boolean whether or not this matchmaking endpoint is authenticated
    Returns:
        Requests response object
    Raises:
        exceptions.MatchmakingError when unexpected matchmaking error occurs
        exceptions.InsufficientFunds when matchmaking responds with payment required
        exceptions.NotFound when matchmaking responds with a 404
    """
    if json_content is None:
        json_content = {}
    http_verb = http_verb.upper()
    _log.info(f"[MATCHMAKING] Performing {http_verb} request to {path} with data: {json_content}")
    headers, data = None, None
    if authenticated:
        headers, data = authorization.generate_authenticated_request(http_verb, "matchmaking", path, json_content)
    else:
        data = json.dumps(json_content, separators=(",", ":")).encode("utf-8") if json_content else b""
        headers = {"Content-Type": "application/json"} if json_content else {}

    response = requests.request(method=http_verb, url=f"{MATCHMAKING_ADDRESS}{path}", headers=headers, data=data, timeout=REQUEST_TIMEOUT)

    if response.status_code < 200 or response.status_code >= 300:
        if retry and response.status_code == 401 and authenticated:
            _log.warning("[MATCHMAKING] received 401 from matchmaking. Registering new key with matchmaking and trying again")
            authorization.register_new_key_with_matchmaking()
            return make_matchmaking_request(http_verb=http_verb, path=path, json_content=json_content, retry=False, authenticated=authenticated)
        elif retry and response.status_code == 403 and authenticated:
            _log.warning("[MATCHMAKING] received 403 from matchmaking. Registration is probably expired. Re-registering and trying again")
            register()
            return make_matchmaking_request(http_verb=http_verb, path=path, json_content=json_content, retry=False, authenticated=authenticated)
        elif response.status_code == 402:
            raise exceptions.InsufficientFunds("received insufficient funds (402) from matchmaking")
        elif response.status_code == 404:
            raise exceptions.NotFound("Not found (404) from matchmaking")
        raise exceptions.MatchmakingError(
            f"Received unexpected response code {response.status_code} from matchmaking with response:\n{response.text}"
        )

    return response
 def test_gen_interchain_request_dcid(self, mock_get_auth_key, date_mock,
                                      mock_register, mock_dcid):
     dcid = "adcid"
     full_path = "/path"
     json_content = {"thing": "test"}
     json_str = json.dumps(json_content,
                           separators=(",", ":")).encode("utf-8")
     expected_headers = {
         "Content-Type":
         "application/json",
         "timestamp":
         "timestampZ",
         "dragonchain":
         dcid,
         "Authorization":
         "DC1-HMAC-SHA256 test_dcid:1oJseWBqbZokioWGWjb2jq1iq493MkgUyc3FkQND5XM=",
     }
     # Test valid SHA256
     headers, content = authorization.generate_authenticated_request(
         "POST", dcid, full_path, json_content, "SHA256")
     self.assertEqual(content, json_str)
     self.assertDictEqual(headers, expected_headers)
     # Test valid BLAKE2b512
     headers, content = authorization.generate_authenticated_request(
         "POST", dcid, full_path, json_content, "BLAKE2b512")
     expected_headers[
         "Authorization"] = "DC1-HMAC-BLAKE2b512 test_dcid:JJiXbVuTjJ03/hNW8fZipw5DUiktO2lJSyml824eWS++mmilth7/BABgDYPvprAa99PHzFzYPA41iL45bI4p1w=="
     self.assertEqual(content, json_str)
     self.assertDictEqual(headers, expected_headers)
     # Test valid SHA3-256
     headers, content = authorization.generate_authenticated_request(
         "POST", dcid, full_path, json_content, "SHA3-256")
     expected_headers[
         "Authorization"] = "DC1-HMAC-SHA3-256 test_dcid:ANsT9nToNzhWbxtoank/oLMDZoish5tFVuhAMzF/obo="
     self.assertEqual(content, json_str)
     self.assertDictEqual(headers, expected_headers)
Exemplo n.º 6
0
def send_receipt(block: "model.BlockModel") -> None:
    try:
        dcid = block.get_associated_l1_dcid()
        full_path = "/v1/receipt"
        url = f"{matchmaking.get_dragonchain_address(dcid)}{full_path}"
        headers, data = authorization.generate_authenticated_request("POST", dcid, full_path, block.export_as_at_rest())
        _log.info(f"----> {url}")
        r = requests.post(url, data=data, headers=headers, timeout=30)
        _log.info(f"<---- {r.status_code} {r.text}")
        if r.status_code != 200:
            _log.info(f"[BROADCAST] WARNING: failed to transmit to {dcid} with error {r.text}")
        else:
            _log.info("[BROADCAST] Sucessful receipt sent down to L1")
    except Exception:
        _log.error(f"[BROADCAST] ERROR: Couldn't broadcast receipt down to {dcid}! Ignoring")
 def test_gen_interchain_request_matchmaking(self, date_mock, mock_register,
                                             mock_get, mock_dcid):
     full_path = "/path"
     json_content = {"thing": "test"}
     json_str = json.dumps(json_content,
                           separators=(",", ":")).encode("utf-8")
     expected_headers = {
         "Content-Type":
         "application/json",
         "timestamp":
         "timestampZ",
         "Authorization":
         "DC1-HMAC-SHA256 test_dcid:ab+hEQC0NNJB7mHwpqsfQqEcOyolNOmDEQe9gvUZTYI=",
     }
     headers, content = authorization.generate_authenticated_request(
         "POST", "matchmaking", full_path, json_content, "SHA256")
     self.assertEqual(content, json_str)
     self.assertDictEqual(headers, expected_headers)