def test_history(bandwidth_db): assert not bandwidth_db.get_history() tx1 = BandwidthTransactionData(1, bandwidth_db.my_pub_key, b"a", EMPTY_SIGNATURE, EMPTY_SIGNATURE, 3000) bandwidth_db.BandwidthTransaction.insert(tx1) history = bandwidth_db.get_history() assert len(history) == 1 assert history[0]["balance"] == -3000 tx2 = BandwidthTransactionData(1, b"a", bandwidth_db.my_pub_key, EMPTY_SIGNATURE, EMPTY_SIGNATURE, 4000) bandwidth_db.BandwidthTransaction.insert(tx2) history = bandwidth_db.get_history() assert len(history) == 2 assert history[1]["balance"] == 1000 # Test whether the history is pruned correctly bandwidth_db.MAX_HISTORY_ITEMS = 2 tx3 = BandwidthTransactionData(1, b"a", bandwidth_db.my_pub_key, EMPTY_SIGNATURE, EMPTY_SIGNATURE, 2000) bandwidth_db.BandwidthTransaction.insert(tx3) history = bandwidth_db.get_history() assert len(history) == 2
def test_add_bandwidth_transactions(trust_graph): """ Tests the maximum blocks/transactions that be be present in the graph. :return: """ my_pk = trust_graph.root_key for _ in range(trust_graph.max_nodes - 1): random_node_pk = unhexlify(get_random_node_public_key()) random_tx = BandwidthTransactionData(1, random_node_pk, my_pk, EMPTY_SIGNATURE, EMPTY_SIGNATURE, 3000) trust_graph.add_bandwidth_transaction(random_tx) assert trust_graph.number_of_nodes() == trust_graph.max_nodes # Already max number of nodes are added to the graph, adding more should raise an exception try: tx2 = BandwidthTransactionData(1, my_pk, b"a", EMPTY_SIGNATURE, EMPTY_SIGNATURE, 2000) trust_graph.add_bandwidth_transaction(tx2) except TrustGraphException as tge: exception_msg = getattr(tge, 'message', repr(tge)) assert f'Max node peers ({trust_graph.max_nodes}) reached in the graph' in exception_msg else: assert False, "Expected to fail but did not."
async def test_concurrent_transaction_out_of_order(self): """ Test creating multiple transactions, while the other party is offline and receives messages out of order. """ pk1 = self.nodes[0].my_peer.public_key.key_to_bin() pk2 = self.nodes[1].my_peer.public_key.key_to_bin() tx1 = BandwidthTransactionData(1, pk1, pk2, EMPTY_SIGNATURE, EMPTY_SIGNATURE, 1000) tx2 = BandwidthTransactionData(2, pk1, pk2, EMPTY_SIGNATURE, EMPTY_SIGNATURE, 2000) # Send them in reverse order cache = self.nodes[0].overlay.request_cache.add( BandwidthTransactionSignCache(self.nodes[0].overlay, tx1)) self.nodes[0].overlay.send_transaction(tx2, self.nodes[1].my_peer.address, cache.number) await self.deliver_messages() # This one should be ignored by node 1 cache = self.nodes[0].overlay.request_cache.add( BandwidthTransactionSignCache(self.nodes[0].overlay, tx1)) self.nodes[0].overlay.send_transaction(tx1, self.nodes[1].my_peer.address, cache.number) await self.deliver_messages() # Both parties should have the transaction with amount 2000 in their database assert self.nodes[0].overlay.database.get_total_taken(pk1) == 2000 assert self.nodes[1].overlay.database.get_total_taken(pk1) == 2000
def test_peers_helped_by(bandwidth_db): assert bandwidth_db.get_num_peers_helped_by(b"a") == 0 tx1 = BandwidthTransactionData(1, b"b", b"a", EMPTY_SIGNATURE, EMPTY_SIGNATURE, 3000) bandwidth_db.BandwidthTransaction.insert(tx1) assert bandwidth_db.get_num_peers_helped_by(b"a") == 1 tx2 = BandwidthTransactionData(2, b"c", b"a", EMPTY_SIGNATURE, EMPTY_SIGNATURE, 3000) bandwidth_db.BandwidthTransaction.insert(tx2) assert bandwidth_db.get_num_peers_helped_by(b"a") == 2 tx3 = BandwidthTransactionData(1, b"c", b"b", EMPTY_SIGNATURE, EMPTY_SIGNATURE, 3000) bandwidth_db.BandwidthTransaction.insert(tx3) assert bandwidth_db.get_num_peers_helped_by(b"a") == 2
def construct_signed_transaction(self, peer: Peer, amount: int) -> BandwidthTransactionData: """ Construct a new signed bandwidth transaction. :param peer: The counterparty of the transaction. :param amount: The amount of bytes to payout. :return A signed BandwidthTransaction. """ peer_pk = peer.public_key.key_to_bin() latest_tx = self.database.get_latest_transaction(self.my_pk, peer_pk) total_amount = latest_tx.amount + amount if latest_tx else amount next_seq_num = latest_tx.sequence_number + 1 if latest_tx else 1 tx = BandwidthTransactionData(next_seq_num, self.my_pk, peer_pk, EMPTY_SIGNATURE, EMPTY_SIGNATURE, total_amount) tx.sign(self.my_peer.key, as_a=True) return tx
def test_get_my_latest_transactions(bandwidth_db): assert not bandwidth_db.get_my_latest_transactions() tx1 = BandwidthTransactionData(1, b"a", bandwidth_db.my_pub_key, EMPTY_SIGNATURE, EMPTY_SIGNATURE, 3000) bandwidth_db.BandwidthTransaction.insert(tx1) tx2 = BandwidthTransactionData(1, bandwidth_db.my_pub_key, b"c", EMPTY_SIGNATURE, EMPTY_SIGNATURE, 3000) bandwidth_db.BandwidthTransaction.insert(tx2) tx3 = BandwidthTransactionData(1, b"c", b"d", EMPTY_SIGNATURE, EMPTY_SIGNATURE, 3000) bandwidth_db.BandwidthTransaction.insert(tx3) assert len(bandwidth_db.get_my_latest_transactions()) == 2 assert len(bandwidth_db.get_my_latest_transactions(limit=1)) == 1
async def test_ignore_unknown_transaction(self): """ Test whether we are ignoring a transaction that is not in our cache. """ pk1 = self.nodes[0].my_peer.public_key.key_to_bin() pk2 = self.nodes[1].my_peer.public_key.key_to_bin() tx = BandwidthTransactionData(1, pk1, pk2, EMPTY_SIGNATURE, EMPTY_SIGNATURE, 1000) tx.sign(self.nodes[0].my_peer.key, as_a=True) self.nodes[0].overlay.send_transaction(tx, self.nodes[1].my_peer.address, 1234) await self.deliver_messages() assert not self.nodes[0].overlay.database.get_latest_transaction( pk1, pk2)
async def test_totals(bandwidth_db): with db_session: tx1 = BandwidthTransactionData(1, b"a", b"b", EMPTY_SIGNATURE, EMPTY_SIGNATURE, 3000) bandwidth_db.BandwidthTransaction.insert(tx1) assert bandwidth_db.get_total_taken(b"a") == 3000 assert bandwidth_db.get_total_given(b"a") == 0 tx2 = BandwidthTransactionData(1, b"b", b"a", EMPTY_SIGNATURE, EMPTY_SIGNATURE, 4000) bandwidth_db.BandwidthTransaction.insert(tx2) assert bandwidth_db.get_total_taken(b"a") == 3000 assert bandwidth_db.get_total_given(b"a") == 4000 assert bandwidth_db.get_balance(b"a") == 1000 assert bandwidth_db.get_balance(b"b") == -1000
def test_store_large_transaction(bandwidth_db): large_tx = BandwidthTransactionData(1, b"a", b"b", EMPTY_SIGNATURE, EMPTY_SIGNATURE, 1024 * 1024 * 1024 * 3) bandwidth_db.BandwidthTransaction.insert(large_tx) latest_tx = bandwidth_db.get_latest_transaction(b"a", b"b") assert latest_tx
def test_get_latest_transaction(bandwidth_db): assert not bandwidth_db.get_latest_transaction(b"a", b"b") tx1 = BandwidthTransactionData(1, b"a", b"b", EMPTY_SIGNATURE, EMPTY_SIGNATURE, 3000) bandwidth_db.BandwidthTransaction.insert(tx1) tx2 = bandwidth_db.get_latest_transaction(b"a", b"b") assert tx1 == tx2 assert tx2.amount == 3000
def insert_node_transactions(root_key, session, node_public_key=None, count=1): for idx in range(count): counterparty = unhexlify(node_public_key if node_public_key else get_random_node_public_key()) amount = random.randint(10, 100) tx1 = BandwidthTransactionData(idx, root_key, counterparty, EMPTY_SIGNATURE, EMPTY_SIGNATURE, amount) session.bandwidth_community.database.BandwidthTransaction.insert(tx1)
def get_latest_transaction( self, public_key_a: bytes, public_key_b: bytes) -> BandwidthTransactionData: """ Return the latest transaction between two parties, or None if no such transaction exists. :param public_key_a: The public key of the party transferring the bandwidth. :param public_key_b: The public key of the party receiving the bandwidth. :return The latest transaction between the two specified parties, or None if no such transaction exists. """ db_obj = self.BandwidthTransaction.get(public_key_a=public_key_a, public_key_b=public_key_b) return BandwidthTransactionData.from_db(db_obj) if db_obj else None
def test_sign_transaction(): key1 = default_eccrypto.generate_key('curve25519') key2 = default_eccrypto.generate_key('curve25519') tx = BandwidthTransactionData(1, key1.pub().key_to_bin(), key2.pub().key_to_bin(), EMPTY_SIGNATURE, EMPTY_SIGNATURE, 3000) tx.sign(key1, as_a=True) assert tx.is_valid() assert tx.signature_a != EMPTY_SIGNATURE assert tx.signature_b == EMPTY_SIGNATURE tx.sign(key2, as_a=False) assert tx.is_valid() assert tx.signature_a != EMPTY_SIGNATURE assert tx.signature_b != EMPTY_SIGNATURE
def received_transaction(self, source_address: Address, data: bytes) -> None: """ Callback when we receive a transaction from another peer. :param source_address: The network address of the peer that has sent us the transaction. :param data: The serialized, raw data in the packet. """ payload = self._ez_unpack_noauth(BandwidthTransactionPayload, data, global_time=False) tx = BandwidthTransactionData.from_payload(payload) if not tx.is_valid(): self.logger.info("Transaction %s not valid, ignoring it", tx) return if payload.public_key_a == self.my_pk or payload.public_key_b == self.my_pk: # This transaction involves this peer. latest_tx = self.database.get_latest_transaction(tx.public_key_a, tx.public_key_b) if payload.public_key_b == self.my_peer.public_key.key_to_bin(): from_peer = Peer(payload.public_key_a, source_address) if latest_tx: # Check if the amount in the received transaction is higher than the amount of the latest one # in the database. if payload.amount > latest_tx.amount: # Sign it, store it, and send it back tx.sign(self.my_peer.key, as_a=False) self.database.BandwidthTransaction.insert(tx) self.send_transaction(tx, from_peer.address, payload.request_id) else: self.logger.info("Received older bandwidth transaction - sending back the latest one") self.send_transaction(latest_tx, from_peer.address, payload.request_id) else: # This transaction is the first one with party A. Sign it, store it, and send it back. tx.sign(self.my_peer.key, as_a=False) self.database.BandwidthTransaction.insert(tx) from_peer = Peer(payload.public_key_a, source_address) self.send_transaction(tx, from_peer.address, payload.request_id) elif payload.public_key_a == self.my_peer.public_key.key_to_bin(): # It seems that we initiated this transaction. Check if we are waiting for it. cache = self.request_cache.get("bandwidth-tx-sign", payload.request_id) if not cache: self.logger.info("Received bandwidth transaction %s without associated cache entry, ignoring it", tx) return if not latest_tx or (latest_tx and latest_tx.amount >= tx.amount): self.database.BandwidthTransaction.insert(tx) cache.future.set_result(tx) else: # This transaction involves two unknown peers. We can add it to our database. self.database.BandwidthTransaction.insert(tx)
def get_latest_transactions( self, public_key: bytes, limit: Optional[int] = 100) -> List[BandwidthTransactionData]: """ Return the latest transactions of a given public key, or an empty list if no transactions exist. :param public_key: The public key of the party transferring the bandwidth. :param limit: The number of transactions to return. (Default: 100) :return The latest transactions of the specified public key, or an empty list if no transactions exist. """ db_txs = select(tx for tx in self.BandwidthTransaction if public_key in (tx.public_key_a, tx.public_key_b))\ .limit(limit) return [BandwidthTransactionData.from_db(db_txn) for db_txn in db_txs]
def test_add_transaction(bandwidth_db): tx1 = BandwidthTransactionData(1, b"a", b"b", EMPTY_SIGNATURE, EMPTY_SIGNATURE, 3000) bandwidth_db.BandwidthTransaction.insert(tx1) assert bandwidth_db.has_transaction(tx1) tx2 = BandwidthTransactionData(2, b"a", b"b", EMPTY_SIGNATURE, EMPTY_SIGNATURE, 4000) bandwidth_db.BandwidthTransaction.insert(tx2) latest_tx = bandwidth_db.get_latest_transaction(b"a", b"b") assert latest_tx assert latest_tx.amount == 4000 # Test storing all transactions bandwidth_db.store_all_transactions = True tx3 = BandwidthTransactionData(3, b"a", b"b", EMPTY_SIGNATURE, EMPTY_SIGNATURE, 4000) bandwidth_db.BandwidthTransaction.insert(tx3) assert len(list(bandwidth_db.BandwidthTransaction.select())) == 2 assert bandwidth_db.has_transaction(tx2) assert bandwidth_db.has_transaction(tx3) # Test whether adding a transaction again does not result in an error bandwidth_db.BandwidthTransaction.insert(tx2)
def get_my_latest_transactions( self, limit: Optional[int] = None) -> List[BandwidthTransactionData]: """ Return all latest transactions involving you. :param limit: An optional integer, to limit the number of results returned. Pass None to get all results. :return A list containing all latest transactions involving you. """ results = [] db_txs = select(tx for tx in self.BandwidthTransaction if tx.public_key_a == self.my_pub_key or tx.public_key_b == self.my_pub_key)\ .limit(limit) for db_tx in db_txs: results.append(BandwidthTransactionData.from_db(db_tx)) return results
def test_get_latest_transactions(bandwidth_db): pub_key_a = b"a" pub_keys_rest = [b"b", b"c", b"d", b"e", b"f"] assert not bandwidth_db.get_latest_transactions(pub_key_a) for pub_key in pub_keys_rest: seq_number = random.randint(1, 100) amount = random.randint(1, 1000) tx = BandwidthTransactionData(seq_number, pub_key_a, pub_key, EMPTY_SIGNATURE, EMPTY_SIGNATURE, amount) bandwidth_db.BandwidthTransaction.insert(tx) txs = bandwidth_db.get_latest_transactions(pub_key_a) assert len(txs) == len(pub_keys_rest)
def on_payout(self, source_address: Address, data: bytes) -> None: """ We received a payout from another peer. :param source_address: The address of the peer that sent us this payout. :param data: The serialized, raw data. """ if not self.bandwidth_community: self.logger.warning( "Got payout while not having a bandwidth community running!") return payload = self._ez_unpack_noauth(BandwidthTransactionPayload, data, global_time=False) tx = BandwidthTransactionData.from_payload(payload) if not tx.is_valid(): self.logger.info( "Received invalid bandwidth transaction in tunnel community - ignoring it" ) return from_peer = Peer(payload.public_key_a, source_address) my_pk = self.my_peer.public_key.key_to_bin() latest_tx = self.bandwidth_community.database.get_latest_transaction( self.my_peer.public_key.key_to_bin(), from_peer.public_key.key_to_bin()) if payload.circuit_id != 0 and tx.public_key_b == my_pk and ( not latest_tx or latest_tx.amount < tx.amount): # Sign it and send it back tx.sign(self.my_peer.key, as_a=False) self.bandwidth_community.database.BandwidthTransaction.insert(tx) response_payload = BandwidthTransactionPayload.from_transaction( tx, 0, payload.base_amount) packet = self._ez_pack(self._prefix, 30, [response_payload], False) self.send_packet(from_peer, packet) elif payload.circuit_id == 0 and tx.public_key_a == my_pk: if not latest_tx or (latest_tx and latest_tx.amount >= tx.amount): self.bandwidth_community.database.BandwidthTransaction.insert( tx) # Send the next payout if payload.circuit_id in self.relay_from_to and tx.amount > payload.base_amount: relay = self.relay_from_to[payload.circuit_id] self._logger.info("Sending next payout to peer %s", relay.peer) self.do_payout(relay.peer, relay.circuit_id, payload.base_amount * 2, payload.base_amount)
def test_is_valid(): key1 = default_eccrypto.generate_key('curve25519') key2 = default_eccrypto.generate_key('curve25519') tx = BandwidthTransactionData(1, key1.pub().key_to_bin(), key2.pub().key_to_bin(), EMPTY_SIGNATURE, EMPTY_SIGNATURE, 3000) assert tx.is_valid() # No signatures have been computed so far tx.signature_a = b'a' * 32 assert not tx.is_valid() tx.signature_a = EMPTY_SIGNATURE tx.signature_b = b'a' * 32 assert not tx.is_valid() tx.signature_a = EMPTY_SIGNATURE tx.signature_b = EMPTY_SIGNATURE tx.sequence_number = -1 assert not tx.is_valid()
async def test_trustview_response(enable_api, mock_ipv8, session, mock_bootstrap): """ Test whether the trust graph response is correctly returned. Scenario: A graph with 3 nodes in each layers (layer 1: friends, layer 2: fofs, layer 3: fofofs). The current implementation of trust graph only considers two layers, therefore, number of nodes in the graph = 1 (root node) + 3 (friends) + 3 (fofs) = 7 number of transactions in the graphs = 3 (root node to friends) + 3 (friends) * 3 (fofs) = 12 """ root_key = session.bandwidth_community.my_pk friends = [ "4c69624e61434c504b3a2ee28ce24a2259b4e585b81106cdff4359fcf48e93336c11d133b01613f30b03b4db06df27" "80daac2cdf2ee60be611bf7367a9c1071ac50d65ca5858a50e9578", "4c69624e61434c504b3a5368c7b39a82063e29576df6d74fba3e0dba3af8e7a304b553b71f08ea6a0730e8cef767a4" "85dc6f390b6da5631f772941ea69ce2c098d802b7a28b500edf2b3", "4c69624e61434c504b3a0f3f6318e49ffeb0a160e7fcac5c1d3337ba409b45e1371ddca5e3b364ebdd1b73c775318a" "533a25335a5c36ae3695f1c3036b651893659fbf2e1f2bce66cf65", ] fofs = [ "4c69624e61434c504b3a2ee28ce24a2259b4e585b81106cdff4359fcf48e93336c11d133b01613f30b03b4db06df27" "80daac2cdf2ee60be611bf7367a9c1071ac50d65ca5858a50e9579", "4c69624e61434c504b3a5368c7b39a82063e29576df6d74fba3e0dba3af8e7a304b553b71f08ea6a0730e8cef767a4" "85dc6f390b6da5631f772941ea69ce2c098d802b7a28b500edf2b4", "4c69624e61434c504b3a0f3f6318e49ffeb0a160e7fcac5c1d3337ba409b45e1371ddca5e3b364ebdd1b73c775318a" "533a25335a5c36ae3695f1c3036b651893659fbf2e1f2bce66cf66", ] fofofs = [ "4c69624e61434c504b3a2ee28ce24a2259b4e585b81106cdff4359fcf48e93336c11d133b01613f30b03b4db06df27" "80daac2cdf2ee60be611bf7367a9c1071ac50d65ca5858a50e9580", "4c69624e61434c504b3a5368c7b39a82063e29576df6d74fba3e0dba3af8e7a304b553b71f08ea6a0730e8cef767a4" "85dc6f390b6da5631f772941ea69ce2c098d802b7a28b500edf2b5", "4c69624e61434c504b3a0f3f6318e49ffeb0a160e7fcac5c1d3337ba409b45e1371ddca5e3b364ebdd1b73c775318a" "533a25335a5c36ae3695f1c3036b651893659fbf2e1f2bce66cf67", ] def verify_response(response_json): expected_nodes = 1 + len(friends) + len(fofs) expected_txns = len(friends) + len(friends) * len(fofs) assert response_json['graph'] assert response_json['num_tx'] == expected_txns assert len(response_json['graph']['node']) == expected_nodes for pub_key in friends: tx1 = BandwidthTransactionData(1, root_key, unhexlify(pub_key), EMPTY_SIGNATURE, EMPTY_SIGNATURE, 3000) session.bandwidth_community.database.BandwidthTransaction.insert(tx1) for friend in friends: for fof in fofs: tx2 = BandwidthTransactionData(1, unhexlify(friend), unhexlify(fof), EMPTY_SIGNATURE, EMPTY_SIGNATURE, 3000) session.bandwidth_community.database.BandwidthTransaction.insert( tx2) for fof in fofs: for fofof in fofofs: tx3 = BandwidthTransactionData(1, unhexlify(fof), unhexlify(fofof), EMPTY_SIGNATURE, EMPTY_SIGNATURE, 3000) session.bandwidth_community.database.BandwidthTransaction.insert( tx3) response = await do_request(session, 'trustview', expected_code=200) verify_response(response)