def on_balance_request(self, payload): """ We received a balance request from a relay or exit node. Respond with the latest block in our chain. """ if not self.bandwidth_wallet: self.logger.warn( "Bandwidth wallet is not available, not sending balance response!" ) return # Get the latest block latest_block = self.bandwidth_wallet.trustchain.persistence.get_latest( self.my_peer.public_key.key_to_bin(), block_type='tribler_bandwidth') if not latest_block: latest_block = TriblerBandwidthBlock() latest_block.public_key = EMPTY_PK # We hide the public key # We either send the response directly or relay the response to the last verified hop circuit = self.circuits[payload.circuit_id] if not circuit.hops: self.increase_bytes_sent( circuit, self.send_cell([circuit.peer.address], u"balance-response", BalanceResponsePayload.from_half_block( latest_block, circuit.circuit_id))) else: self.increase_bytes_sent( circuit, self.send_cell([circuit.peer.address], u"relay-balance-response", BalanceResponsePayload.from_half_block( latest_block, circuit.circuit_id)))
def on_balance_request(self, payload): """ We received a balance request from a relay or exit node. Respond with the latest block in our chain. """ if not self.bandwidth_wallet: self.logger.warn("Bandwidth wallet is not available, not sending balance response!") return # Get the latest block latest_block = self.bandwidth_wallet.trustchain.persistence.get_latest(self.my_peer.public_key.key_to_bin(), block_type='tribler_bandwidth') if not latest_block: latest_block = TriblerBandwidthBlock() latest_block.public_key = EMPTY_PK # We hide the public key # We either send the response directly or relay the response to the last verified hop circuit = self.circuits[payload.circuit_id] if not circuit.hops: self.increase_bytes_sent(circuit, self.send_cell([circuit.peer.address], u"balance-response", BalanceResponsePayload.from_half_block( latest_block, circuit.circuit_id))) else: self.increase_bytes_sent(circuit, self.send_cell([circuit.peer.address], u"relay-balance-response", BalanceResponsePayload.from_half_block( latest_block, circuit.circuit_id)))
def __init__(self, previous=None): super(TriblerBandwidthTestBlock, self).__init__() crypto = ECCrypto() other = crypto.generate_key(u"curve25519").pub().key_to_bin() transaction = { 'up': random.randint(201, 220), 'down': random.randint(221, 240), 'total_up': 0, 'total_down': 0, 'type': 'tribler_bandwidth' } if previous: self.key = previous.key transaction['total_up'] = previous.transaction[ 'total_up'] + transaction['up'] transaction['total_down'] = previous.transaction[ 'total_down'] + transaction['down'] TriblerBandwidthBlock.__init__( self, ('tribler_bandwidth', encode(transaction), previous.public_key, previous.sequence_number + 1, other, 0, previous.hash, 0, 0, 0)) else: transaction['total_up'] = random.randint(241, 260) transaction['total_down'] = random.randint(261, 280) self.key = crypto.generate_key(u"curve25519") TriblerBandwidthBlock.__init__( self, ('tribler_bandwidth', encode(transaction), self.key.pub().key_to_bin(), random.randint(50, 100), other, 0, sha256(str(random.randint(0, 100000))).digest(), 0, 0, 0)) self.sign(self.key)
def test_validate_not_sane_zeroes(self): db = MockDatabase() block1 = TriblerBandwidthBlock() # Act block1.transaction = {'up': 0, 'down': 0, 'total_up': 30, 'total_down': 40} result = block1.validate(db) self.assertEqual(result[0], ValidationResult.invalid) self.assertIn("Up and down are zero", result[1])
def test_validate_not_sane_zeroes(self): db = MockDatabase() block1 = TriblerBandwidthBlock() # Act block1.transaction = {'up': 0, 'down': 0, 'total_up': 30, 'total_down': 40} result = block1.validate(db) self.assertEqual(result[0], ValidationResult.invalid) self.assertIn("Up and down are zero", result[1])
def test_validate_not_sane_negatives(self): db = MockDatabase() block1 = TriblerBandwidthBlock() # Act block1.transaction = {'up': -10, 'down': -10, 'total_up': -20, 'total_down': -10} result = block1.validate(db) self.assertEqual(result[0], ValidationResult.invalid) self.assertIn("Up field is negative", result[1]) self.assertIn("Down field is negative", result[1]) self.assertIn("Total up field is negative", result[1]) self.assertIn("Total down field is negative", result[1])
def test_validate_not_sane_negatives(self): db = MockDatabase() block1 = TriblerBandwidthBlock() # Act block1.transaction = {'up': -10, 'down': -10, 'total_up': -20, 'total_down': -10} result = block1.validate(db) self.assertEqual(result[0], ValidationResult.invalid) self.assertIn("Up field is negative", result[1]) self.assertIn("Down field is negative", result[1]) self.assertIn("Total up field is negative", result[1]) self.assertIn("Total down field is negative", result[1])
def bootstrap_new_identity(self, amount): """ One-way payment channel. Create a new temporary identity, and transfer funds to the new identity. A different party can then take the result and do a transfer from the temporary identity to itself """ # Create new identity for the temporary identity crypto = ECCrypto() tmp_peer = Peer(crypto.generate_key(u"curve25519")) # Create the transaction specification transaction = {'up': 0, 'down': amount, 'type': 'tribler_bandwidth'} # Create the two half blocks that form the transaction local_half_block = TriblerBandwidthBlock.create( 'tribler_bandwidth', transaction, self.trustchain.persistence, self.trustchain.my_peer.public_key.key_to_bin(), link_pk=tmp_peer.public_key.key_to_bin()) local_half_block.sign(self.trustchain.my_peer.key) tmp_half_block = TriblerBandwidthBlock.create( 'tribler_bandwidth', transaction, self.trustchain.persistence, tmp_peer.public_key.key_to_bin(), link=local_half_block, link_pk=self.trustchain.my_peer.public_key.key_to_bin()) tmp_half_block.sign(tmp_peer.key) self.trustchain.persistence.add_block(local_half_block) self.trustchain.persistence.add_block(tmp_half_block) # Create the bootstrapped identity format block = { 'block_hash': tmp_half_block.hash.encode('base64'), 'sequence_number': tmp_half_block.sequence_number } result = { 'private_key': tmp_peer.key.key_to_bin().encode('base64'), 'transaction': { 'up': amount, 'down': 0 }, 'block': block } return result
def test_validate_existing_total_down(self): # Arrange db = MockDatabase() (block1, block2, block3, _) = TestBlocks.setup_validate() db.add_block(block1) db.add_block(block2) db.add_block(block3) # Act block2 = TriblerBandwidthBlock(block2.pack_db_insert()) block2.transaction["total_down"] += 10 block2.sign(db.get(block2.public_key, block2.sequence_number).key) result = block2.validate(db) # Assert self.assertEqual(result[0], ValidationResult.invalid) self.assertIn('Total down is higher than expected compared to the next block', result[1])
def do_payout(self, peer, circuit_id, amount, base_amount): """ Perform a payout to a specific peer. :param peer: The peer to perform the payout to, usually the next node in the circuit. :param circuit_id: The circuit id of the payout, used by the subsequent node. :param amount: The amount to put in the transaction, multiplier of base_amount. :param base_amount: The base amount for the payouts. """ self.logger.info("Sending payout of %d (base: %d) to %s (cid: %s)", amount, base_amount, peer, circuit_id) block = TriblerBandwidthBlock.create( 'tribler_bandwidth', { 'up': 0, 'down': amount }, self.bandwidth_wallet.trustchain.persistence, self.my_peer.public_key.key_to_bin(), link_pk=peer.public_key.key_to_bin()) block.sign(self.my_peer.key) self.bandwidth_wallet.trustchain.persistence.add_block(block) payload = PayoutPayload.from_half_block(block, circuit_id, base_amount).to_pack_list() packet = self._ez_pack(self._prefix, 23, [payload], False) self.send_packet([peer], u"payout", packet)
def on_payout_block(self, source_address, data): if not self.bandwidth_wallet: self.logger.warning( "Got payout while not having a TrustChain community running!") return payload = self._ez_unpack_noauth(PayoutPayload, data, global_time=False) peer = Peer(payload.public_key, source_address) def on_transaction_completed(blocks): # Send the next payout if blocks and payload.circuit_id in self.relay_from_to and block.transaction[ 'down'] > 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, block.transaction['down'] - payload.base_amount * 2, payload.base_amount) block = TriblerBandwidthBlock.from_payload(payload, self.serializer) self.bandwidth_wallet.trustchain.process_half_block(block, peer)\ .addCallbacks(on_transaction_completed, lambda _: None) # Check whether the block has been added to the database and has been verified if not self.bandwidth_wallet.trustchain.persistence.contains(block): self.logger.warning( "Not proceeding with payout - received payout block is not valid" ) return
def on_payout_block(self, source_address, data): if not self.bandwidth_wallet: self.logger.warning( "Got payout while not having a TrustChain community running!") return payload = self._ez_unpack_noauth(PayoutPayload, data, global_time=False) peer = Peer(payload.public_key, source_address) block = TriblerBandwidthBlock.from_payload(payload, self.serializer) self.bandwidth_wallet.trustchain.process_half_block(block, peer) # Send the next payout if payload.circuit_id in self.relay_from_to and block.transaction[ 'down'] > payload.base_amount: relay = self.relay_from_to[payload.circuit_id] circuit_peer = self.get_peer_from_address(relay.peer.address) if not circuit_peer: self.logger.warning( "%s Unable to find next peer %s for payout!", self.my_peer, relay.peer) return self.do_payout(circuit_peer, relay.circuit_id, block.transaction['down'] - payload.base_amount * 2, payload.base_amount)
def on_balance_response_cell(self, source_address, data, _): payload = self._ez_unpack_noauth(BalanceResponsePayload, data, global_time=False) block = TriblerBandwidthBlock.from_payload(payload, self.serializer) if not block.transaction: self.on_token_balance(payload.circuit_id, 0) else: self.on_token_balance(payload.circuit_id, block.transaction["total_up"] - block.transaction["total_down"])
def on_balance_response_cell(self, source_address, data, _): payload = self._ez_unpack_noauth(BalanceResponsePayload, data, global_time=False) block = TriblerBandwidthBlock.from_payload(payload, self.serializer) if not block.transaction: self.on_token_balance(payload.circuit_id, 0) else: self.on_token_balance(payload.circuit_id, block.transaction["total_up"] - block.transaction["total_down"])
def on_relay_balance_response_cell(self, source_address, data, _): payload = self._ez_unpack_noauth(BalanceResponsePayload, data, global_time=False) block = TriblerBandwidthBlock.from_payload(payload, self.serializer) # At this point, we don't have the circuit ID of the follow-up hop. We have to iterate over the items in the # request cache and find the link to the next hop. for cache in self.request_cache._identifiers.values(): if isinstance(cache, CreateRequestCache) and cache.from_circuit_id == payload.circuit_id: self.send_cell([cache.to_peer.address], u"balance-response", BalanceResponsePayload.from_half_block(block, cache.to_circuit_id))
def on_relay_balance_response_cell(self, source_address, data, _): payload = self._ez_unpack_noauth(BalanceResponsePayload, data, global_time=False) block = TriblerBandwidthBlock.from_payload(payload, self.serializer) # At this point, we don't have the circuit ID of the follow-up hop. We have to iterate over the items in the # request cache and find the link to the next hop. for cache in self.request_cache._identifiers.values(): if isinstance(cache, ExtendRequestCache) and cache.from_circuit_id == payload.circuit_id: self.send_cell([cache.to_peer.address], u"balance-response", BalanceResponsePayload.from_half_block(block, cache.to_circuit_id))
def test_validate_linked_down(self): db = MockDatabase() (block1, block2, _, _) = TestBlocks.setup_validate() db.add_block(block2) # Act db.add_block(TriblerBandwidthBlock.create('tribler_bandwidth', block1.transaction, db, block1.link_public_key, block1)) block1.transaction["down"] -= 5 result = block1.validate(db) self.assertEqual(result[0], ValidationResult.invalid) self.assertIn("Down/up mismatch on linked block", result[1])
def test_validate_linked_down(self): db = MockDatabase() (block1, block2, _, _) = TestBlocks.setup_validate() db.add_block(block2) # Act db.add_block(TriblerBandwidthBlock.create('tribler_bandwidth', block1.transaction, db, block1.link_public_key, block1)) block1.transaction["down"] -= 5 result = block1.validate(db) self.assertEqual(result[0], ValidationResult.invalid) self.assertIn("Down/up mismatch on linked block", result[1])
def bootstrap_new_identity(self, amount): """ One-way payment channel. Create a new temporary identity, and transfer funds to the new identity. A different party can then take the result and do a transfer from the temporary identity to itself """ # Create new identity for the temporary identity crypto = ECCrypto() tmp_peer = Peer(crypto.generate_key(u"curve25519")) # Create the transaction specification transaction = { 'up': 0, 'down': amount, 'type': 'tribler_bandwidth' } # Create the two half blocks that form the transaction local_half_block = TriblerBandwidthBlock.create('tribler_bandwidth', transaction, self.trustchain.persistence, self.trustchain.my_peer.public_key.key_to_bin(), link_pk=tmp_peer.public_key.key_to_bin()) local_half_block.sign(self.trustchain.my_peer.key) tmp_half_block = TriblerBandwidthBlock.create('tribler_bandwidth', transaction, self.trustchain.persistence, tmp_peer.public_key.key_to_bin(), link=local_half_block, link_pk=self.trustchain.my_peer.public_key.key_to_bin()) tmp_half_block.sign(tmp_peer.key) self.trustchain.persistence.add_block(local_half_block) self.trustchain.persistence.add_block(tmp_half_block) # Create the bootstrapped identity format block = {'block_hash': tmp_half_block.hash.encode('base64'), 'sequence_number': tmp_half_block.sequence_number} result = {'private_key': tmp_peer.key.key_to_bin().encode('base64'), 'transaction': {'up': amount, 'down': 0}, 'block': block} return result
def __init__(self, previous=None): super(TriblerBandwidthTestBlock, self).__init__() crypto = ECCrypto() other = crypto.generate_key(u"curve25519").pub().key_to_bin() transaction = {'up': random.randint(201, 220), 'down': random.randint(221, 240), 'total_up': 0, 'total_down': 0, 'type': 'tribler_bandwidth'} if previous: self.key = previous.key transaction['total_up'] = previous.transaction['total_up'] + transaction['up'] transaction['total_down'] = previous.transaction['total_down'] + transaction['down'] TriblerBandwidthBlock.__init__(self, ('tribler_bandwidth', encode(transaction), previous.public_key, previous.sequence_number + 1, other, 0, previous.hash, 0, 0, 0)) else: transaction['total_up'] = random.randint(241, 260) transaction['total_down'] = random.randint(261, 280) self.key = crypto.generate_key(u"curve25519") TriblerBandwidthBlock.__init__(self, ( 'tribler_bandwidth', encode(transaction), self.key.pub().key_to_bin(), random.randint(50, 100), other, 0, sha256(str(random.randint(0, 100000))).digest(), 0, 0, 0)) self.sign(self.key)
def test_validate_existing_total_down(self): # Arrange db = MockDatabase() (block1, block2, block3, _) = TestBlocks.setup_validate() db.add_block(block1) db.add_block(block2) db.add_block(block3) # Act block2 = TriblerBandwidthBlock(block2.pack_db_insert()) block2.transaction["total_down"] += 10 block2.sign(db.get(block2.public_key, block2.sequence_number).key) result = block2.validate(db) # Assert self.assertEqual(result[0], ValidationResult.invalid) self.assertIn('Total down is higher than expected compared to the next block', result[1])
def do_payout(self, peer, circuit_id, amount, base_amount): """ Perform a payout to a specific peer. :param peer: The peer to perform the payout to, usually the next node in the circuit. :param circuit_id: The circuit id of the payout, used by the subsequent node. :param amount: The amount to put in the transaction, multiplier of base_amount. :param base_amount: The base amount for the payouts. """ self.logger.info("Sending payout of %d (base: %d) to %s (cid: %s)", amount, base_amount, peer, circuit_id) block = TriblerBandwidthBlock.create( 'tribler_bandwidth', {'up': 0, 'down': amount}, self.bandwidth_wallet.trustchain.persistence, self.my_peer.public_key.key_to_bin(), link_pk=peer.public_key.key_to_bin()) block.sign(self.my_peer.key) self.bandwidth_wallet.trustchain.persistence.add_block(block) payload = PayoutPayload.from_half_block(block, circuit_id, base_amount).to_pack_list() packet = self._ez_pack(self._prefix, 23, [payload], False) self.send_packet([peer], packet)
def on_payout_block(self, source_address, data): if not self.bandwidth_wallet: self.logger.warning("Got payout while not having a TrustChain community running!") return payload = self._ez_unpack_noauth(PayoutPayload, data, global_time=False) peer = Peer(payload.public_key, source_address) def on_transaction_completed(blocks): # Send the next payout if blocks and payload.circuit_id in self.relay_from_to and block.transaction['down'] > 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, block.transaction['down'] - payload.base_amount * 2, payload.base_amount) block = TriblerBandwidthBlock.from_payload(payload, self.serializer) self.bandwidth_wallet.trustchain.process_half_block(block, peer)\ .addCallbacks(on_transaction_completed, lambda _: None) # Check whether the block has been added to the database and has been verified if not self.bandwidth_wallet.trustchain.persistence.contains(block): self.logger.warning("Not proceeding with payout - received payout block is not valid") return