def pop_waiting_messages( self, token_network_address: TokenNetworkAddress, channel_id: ChannelID) -> Iterator[DeferableMessage]: """Return all waiting messages for the given channel and delete them from the db""" # Return messages with self._cursor() as cursor: for row in cursor.execute( """ SELECT message FROM waiting_message WHERE token_network_address = ? AND channel_id = ? """, [ to_checksum_address(token_network_address), hex256(channel_id) ], ): yield JSONSerializer.deserialize(row["message"]) with self._cursor() as cursor: # Delete returned messages cursor.execute( "DELETE FROM waiting_message WHERE token_network_address = ? AND channel_id = ?", [ to_checksum_address(token_network_address), hex256(channel_id) ], )
def upsert_channel(self, channel: Channel) -> None: values = [ to_checksum_address(channel.token_network_address), hex256(channel.identifier), to_checksum_address(channel.participant1), to_checksum_address(channel.participant2), hex256(channel.settle_timeout), channel.state, hex256(channel.closing_block) if channel.closing_block else None, channel.closing_participant, encode_hex(channel.closing_tx_hash) if channel.closing_tx_hash else None, encode_hex(channel.claim_tx_hash) if channel.claim_tx_hash else None, ] if channel.update_status: values += [ to_checksum_address( channel.update_status.update_sender_address), hex256(channel.update_status.nonce), ] else: values += [None, None] upsert_sql = "INSERT OR REPLACE INTO channel VALUES ({})".format( ", ".join("?" * len(values))) self.conn.execute(upsert_sql, values)
def get_ious( self, sender: Address = None, expiration_block: BlockNumber = None, claimed: bool = None, expires_before: BlockNumber = None, amount_at_least: TokenAmount = None, ) -> Iterator[IOU]: query = """ SELECT *, (SELECT chain_id FROM blockchain) AS chain_id FROM iou WHERE 1=1 """ args: list = [] if sender is not None: query += " AND sender = ?" args.append(to_checksum_address(sender)) if expiration_block is not None: query += " AND expiration_block = ?" args.append(hex256(expiration_block)) if claimed is not None: query += " AND claimed = ?" args.append(claimed) if expires_before is not None: query += " AND expiration_block < ?" args.append(hex256(expires_before)) if amount_at_least is not None: query += " AND amount >= ?" args.append(hex256(amount_at_least)) for row in self.conn.execute(query, args): iou_dict = dict(zip(row.keys(), row)) iou_dict["receiver"] = to_checksum_address(self.pfs_address) yield IOU.Schema().load(iou_dict)
def upsert_capacity_update(self, message: PFSCapacityUpdate) -> None: capacity_update_dict = dict( updating_participant=to_checksum_address(message.updating_participant), token_network_address=to_checksum_address( message.canonical_identifier.token_network_address ), channel_id=hex256(message.canonical_identifier.channel_identifier), updating_capacity=hex256(message.updating_capacity), other_capacity=hex256(message.other_capacity), ) self.upsert("capacity_update", capacity_update_dict)
def upsert_scheduled_event(self, event: ScheduledEvent) -> None: contained_event: SubEvent = cast(SubEvent, event.event) values = [ hex256(event.trigger_block_number), EVENT_TYPE_ID_MAP[type(contained_event)], to_checksum_address(contained_event.token_network_address), hex256(contained_event.channel_identifier), contained_event.non_closing_participant, ] upsert_sql = "INSERT OR REPLACE INTO scheduled_events VALUES ({})".format( ", ".join("?" * len(values))) self.conn.execute(upsert_sql, values)
def get_channel(self, token_network_address: TokenNetworkAddress, channel_id: ChannelID) -> Optional[Channel]: row = self.conn.execute( """ SELECT * FROM channel WHERE identifier = ? AND token_network_address = ? """, [hex256(channel_id), to_checksum_address(token_network_address)], ).fetchone() if row is None: return None kwargs = { key: val for key, val in zip(row.keys(), row) if not key.startswith("update_status") } kwargs["token_network_address"] = decode_hex( kwargs["token_network_address"]) kwargs["participant1"] = decode_hex(kwargs["participant1"]) kwargs["participant2"] = decode_hex(kwargs["participant2"]) if kwargs["closing_tx_hash"] is not None: kwargs["closing_tx_hash"] = decode_hex(kwargs["closing_tx_hash"]) if kwargs["claim_tx_hash"] is not None: kwargs["claim_tx_hash"] = decode_hex(kwargs["claim_tx_hash"]) return Channel( update_status=OnChainUpdateStatus( update_sender_address=decode_hex(row["update_status_sender"]), nonce=row["update_status_nonce"], ) if row["update_status_nonce"] is not None else None, **kwargs, )
def get_scheduled_events( self, max_trigger_timestamp: Timestamp) -> List[ScheduledEvent]: """Return all scheduled events before max_trigger_timestamp The events with the earliest trigger_timestamp are returned first.""" rows = self.conn.execute( """ SELECT * FROM scheduled_events WHERE trigger_timestamp <= ? ORDER BY trigger_timestamp """, [hex256(max_trigger_timestamp)], ).fetchall() def create_scheduled_event(row: sqlite3.Row) -> ScheduledEvent: event_type = EVENT_ID_TYPE_MAP[row["event_type"]] sub_event = event_type( TokenNetworkAddress( to_canonical_address(row["token_network_address"])), row["channel_identifier"], row["non_closing_participant"], ) return ScheduledEvent(trigger_timestamp=row["trigger_timestamp"], event=sub_event) return [create_scheduled_event(row) for row in rows]
def upsert_iou(self, iou: IOU) -> None: iou_dict = IOU.Schema(exclude=["receiver", "chain_id"]).dump(iou) iou_dict["one_to_n_address"] = to_checksum_address( iou_dict["one_to_n_address"]) for key in ("amount", "expiration_block"): iou_dict[key] = hex256(iou_dict[key]) self.upsert("iou", iou_dict)
def upsert_channel_view(self, channel_view: ChannelView) -> None: cv_dict = ChannelView.Schema(strict=True, exclude=["state"]).dump(channel_view)[0] for key in ( "channel_id", "settle_timeout", "capacity", "reveal_timeout", "deposit", "update_nonce", "absolute_fee", ): cv_dict[key] = hex256(cv_dict[key]) self.conn.execute( """ INSERT OR REPLACE INTO channel_view ( token_network_address, channel_id, participant1, participant2, settle_timeout, capacity, reveal_timeout, deposit, update_nonce, absolute_fee, relative_fee ) VALUES ( :token_network_address, :channel_id, :participant1, :participant2, :settle_timeout, :capacity, :reveal_timeout, :deposit, :update_nonce, :absolute_fee, :relative_fee ) """, cv_dict, )
def upsert_monitor_request(self, request: MonitorRequest) -> None: values = [ hex256(request.channel_identifier), to_checksum_address(request.token_network_address), request.balance_hash, hex256(request.nonce), request.additional_hash, to_hex(request.closing_signature), to_hex(request.non_closing_signature), hex256(request.reward_amount), to_hex(request.reward_proof_signature), to_checksum_address(request.non_closing_signer), ] upsert_sql = "INSERT OR REPLACE INTO monitor_request VALUES ({})".format( ", ".join("?" * len(values))) self.conn.execute(upsert_sql, values)
def upsert_monitor_request(self, request: MonitorRequest) -> None: self.upsert( "monitor_request", dict( channel_identifier=hex256(request.channel_identifier), token_network_address=to_checksum_address( request.token_network_address), balance_hash=request.balance_hash, nonce=hex256(request.nonce), additional_hash=request.additional_hash, closing_signature=to_hex(request.closing_signature), non_closing_signature=to_hex(request.non_closing_signature), reward_amount=hex256(request.reward_amount), reward_proof_signature=to_hex(request.reward_proof_signature), non_closing_signer=to_checksum_address( request.non_closing_signer), ), )
def remove_scheduled_event(self, event: ScheduledEvent) -> None: contained_event: SubEvent = cast(SubEvent, event.event) values = [ hex256(event.trigger_block_number), to_checksum_address(contained_event.token_network_address), hex256(contained_event.channel_identifier), contained_event.non_closing_participant, ] self.conn.execute( """ DELETE FROM scheduled_events WHERE trigger_block_number = ? AND token_network_address = ? AND channel_identifier = ? AND non_closing_participant =? """, values, )
def insert_waiting_message(self, message: DeferableMessage) -> None: self.insert( "waiting_message", dict( token_network_address=to_checksum_address( message.canonical_identifier.token_network_address ), channel_id=hex256(message.canonical_identifier.channel_identifier), message=JSONSerializer.serialize(message), ), )
def get_ious( self, sender: Optional[Address] = None, claimable_until: Optional[Timestamp] = None, claimed: Optional[bool] = None, claimable_until_after: Optional[Timestamp] = None, claimable_until_before: Optional[Timestamp] = None, amount_at_least: Optional[TokenAmount] = None, ) -> Iterator[IOU]: query = """ SELECT *, (SELECT chain_id FROM blockchain) AS chain_id FROM iou WHERE 1=1 """ args: list = [] if sender is not None: query += " AND sender = ?" args.append(to_checksum_address(sender)) if claimable_until is not None: query += " AND claimable_until = ?" args.append(hex256(claimable_until)) if claimed is not None: query += " AND claimed = ?" args.append(claimed) if claimable_until_before is not None: query += " AND claimable_until < ?" args.append(hex256(claimable_until_before)) if claimable_until_after is not None: query += " AND claimable_until > ?" args.append(hex256(claimable_until_after)) if amount_at_least is not None: query += " AND amount >= ?" args.append(hex256(amount_at_least)) with self._cursor() as cursor: for row in cursor.execute(query, args): iou_dict = dict(zip(row.keys(), row)) iou_dict["receiver"] = to_checksum_address(self.pfs_address) yield IOU.Schema().load(iou_dict)
def upsert_iou(self, iou: IOU) -> None: iou_dict = IOU.Schema(strict=True).dump(iou)[0] for key in ("amount", "expiration_block"): iou_dict[key] = hex256(iou_dict[key]) self.conn.execute( """ INSERT OR REPLACE INTO iou ( sender, amount, expiration_block, signature, claimed ) VALUES ( :sender, :amount, :expiration_block, :signature, :claimed ) """, iou_dict, )
def prepare_feedback( self, token: FeedbackToken, route: List[Address], estimated_fee: FeeAmount ) -> None: hexed_route = [to_checksum_address(e) for e in route] token_dict = dict( token_id=token.uuid.hex, creation_time=token.creation_time, token_network_address=to_checksum_address(token.token_network_address), route=json.dumps(hexed_route), estimated_fee=hex256(estimated_fee), source_address=hexed_route[0], target_address=hexed_route[-1], ) self.insert("feedback", token_dict)
def test_purge_old_monitor_requests( ms_database: Database, build_request_monitoring, request_collector, monitoring_service: MonitoringService, ): # We'll test the purge on MRs for three different channels req_mons = [ build_request_monitoring(channel_id=1), build_request_monitoring(channel_id=2), build_request_monitoring(channel_id=3), ] for req_mon in req_mons: request_collector.on_monitor_request(req_mon) # Channel 1 exists in the db token_network_address = req_mons[0].balance_proof.token_network_address ms_database.conn.execute( "INSERT INTO token_network VALUES (?, ?)", [ to_checksum_address(token_network_address), DEFAULT_TOKEN_NETWORK_SETTLE_TIMEOUT ], ) ms_database.upsert_channel( Channel( identifier=ChannelID(1), token_network_address=token_network_address, participant1=Address(b"1" * 20), participant2=Address(b"2" * 20), )) # The request for channel 2 is recent (default), but the one for channel 3 # has been added 16 minutes ago. saved_at = (datetime.utcnow() - timedelta(minutes=16)).timestamp() ms_database.conn.execute( """ UPDATE monitor_request SET saved_at = ? WHERE channel_identifier = ? """, [saved_at, hex256(3)], ) monitoring_service._purge_old_monitor_requests() # pylint: disable=protected-access remaining_mrs = ms_database.conn.execute(""" SELECT channel_identifier, waiting_for_channel FROM monitor_request ORDER BY channel_identifier """).fetchall() assert [tuple(mr) for mr in remaining_mrs] == [(1, False), (2, True)]
def channel_close_age(self, token_network_address: TokenNetworkAddress, channel_id: ChannelID) -> Optional[int]: """How many blocks ago was the given channel closed?""" row = self.conn.execute( """ SELECT blockchain.latest_committed_block, channel.closing_block FROM channel, blockchain WHERE channel.identifier = ? AND channel.token_network_address = ? """, [hex256(channel_id), to_checksum_address(token_network_address)], ).fetchone() if not row or row["closing_block"] is None: return None return row["latest_committed_block"] - row["closing_block"]
def upsert_channel(self, channel: Channel) -> None: channel_dict = Channel.Schema().dump(channel) for key in ( "channel_id", "settle_timeout", "capacity1", "reveal_timeout1", "update_nonce1", "capacity2", "reveal_timeout2", "update_nonce2", ): channel_dict[key] = hex256(int(channel_dict[key])) channel_dict["fee_schedule1"] = json.dumps(channel_dict["fee_schedule1"]) channel_dict["fee_schedule2"] = json.dumps(channel_dict["fee_schedule2"]) self.upsert("channel", channel_dict)
def upsert_channel_view(self, channel_view: ChannelView) -> None: cv_dict = ChannelView.Schema().dump(channel_view) for key in ( "channel_id", "settle_timeout", "capacity", "reveal_timeout", "deposit", "update_nonce", ): cv_dict[key] = hex256(cv_dict[key]) cv_dict["fee_schedule_sender"] = json.dumps( cv_dict["fee_schedule_sender"]) cv_dict["fee_schedule_receiver"] = json.dumps( cv_dict["fee_schedule_receiver"]) self.upsert("channel_view", cv_dict)
def delete_channel( self, token_network_address: TokenNetworkAddress, channel_id: ChannelID ) -> bool: """Tries to delete a channel from the database Args: token_network_address: The address of the token network of the channel channel_id: The id of the channel Returns: `True` if the channel was deleted, `False` if it did not exist """ cursor = self.conn.execute( "DELETE FROM channel WHERE token_network_address = ? AND channel_id = ?", [to_checksum_address(token_network_address), hex256(channel_id)], ) assert cursor.rowcount <= 1, "Did delete more than one channel" return cursor.rowcount == 1
def get_monitor_request( self, token_network_address: TokenNetworkAddress, channel_id: ChannelID, non_closing_signer: Address, ) -> Optional[MonitorRequest]: row = self.conn.execute( """ SELECT monitor_request.*, blockchain.chain_id, blockchain.monitor_contract_address AS msc_address FROM monitor_request, blockchain WHERE channel_identifier = ? AND token_network_address = ? AND non_closing_signer = ? """, [ hex256(channel_id), to_checksum_address(token_network_address), to_checksum_address(non_closing_signer), ], ).fetchone() if row is None: return None kwargs = { key: val for key, val in zip(row.keys(), row) if key not in ("saved_at", "waiting_for_channel") } kwargs["token_network_address"] = to_canonical_address( kwargs["token_network_address"]) kwargs["msc_address"] = to_canonical_address(kwargs["msc_address"]) kwargs["closing_signature"] = decode_hex(kwargs["closing_signature"]) kwargs["non_closing_participant"] = to_canonical_address( kwargs.pop("non_closing_signer")) kwargs["non_closing_signature"] = decode_hex( kwargs["non_closing_signature"]) kwargs["reward_proof_signature"] = decode_hex( kwargs["reward_proof_signature"]) return MonitorRequest(**kwargs)
def get_capacity_updates( self, updating_participant: Address, token_network_address: TokenNetworkAddress, channel_id: int, ) -> Tuple[TokenAmount, TokenAmount]: capacity_list = self.conn.execute( """ SELECT updating_capacity, other_capacity FROM capacity_update WHERE updating_participant=? AND token_network_address=? AND channel_id=? """, [ to_checksum_address(updating_participant), to_checksum_address(token_network_address), hex256(channel_id), ], ) try: return next(capacity_list) except StopIteration: return TokenAmount(0), TokenAmount(0)
def get_scheduled_events( self, max_trigger_block: BlockNumber) -> List[ScheduledEvent]: rows = self.conn.execute( """ SELECT * FROM scheduled_events WHERE trigger_block_number <= ? """, [hex256(max_trigger_block)], ).fetchall() def create_scheduled_event(row: sqlite3.Row) -> ScheduledEvent: event_type = EVENT_ID_TYPE_MAP[row["event_type"]] sub_event = event_type( decode_hex(row["token_network_address"]), row["channel_identifier"], row["non_closing_participant"], ) return ScheduledEvent( trigger_block_number=row["trigger_block_number"], event=sub_event) return [create_scheduled_event(row) for row in rows]
def get_monitor_request( self, token_network_address: TokenNetworkAddress, channel_id: ChannelID, non_closing_signer: Address, ) -> Optional[MonitorRequest]: row = self.conn.execute( """ SELECT *, (SELECT chain_id FROM blockchain) As chain_id FROM monitor_request WHERE channel_identifier = ? AND token_network_address = ? AND non_closing_signer = ? """, [ hex256(channel_id), to_checksum_address(token_network_address), to_checksum_address(non_closing_signer), ], ).fetchone() if row is None: return None kwargs = { key: val for key, val in zip(row.keys(), row) if key != "non_closing_signer" } kwargs["token_network_address"] = to_canonical_address( kwargs["token_network_address"]) kwargs["closing_signature"] = decode_hex(kwargs["closing_signature"]) kwargs["non_closing_signature"] = decode_hex( kwargs["non_closing_signature"]) kwargs["reward_proof_signature"] = decode_hex( kwargs["reward_proof_signature"]) return MonitorRequest(**kwargs)
def test_hex256(): assert hex256(123) == "+0x000000000000000000000000000000000000000000000000000000000000007b" assert hex256(-123) == "-0x000000000000000000000000000000000000000000000000000000000000007b" assert hex256(0) == "+0x0000000000000000000000000000000000000000000000000000000000000000"