Skip to content

Commit

Permalink
Added tests, fixed naming and fixed max amount
Browse files Browse the repository at this point in the history
  • Loading branch information
mitchellolsthoorn committed Jan 24, 2018
1 parent 0cd1c44 commit ca678e3
Show file tree
Hide file tree
Showing 8 changed files with 369 additions and 120 deletions.
67 changes: 33 additions & 34 deletions Tribler/Core/Modules/restapi/trustchain_endpoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ def __init__(self, session):
child_handler_dict = {
"statistics": TrustchainStatsEndpoint,
"blocks": TrustchainBlocksEndpoint,
"bootstrap": TrustchainBootstrapEndpoint}
"bootstrap": TrustchainBootstrapEndpoint
}

for path, child_cls in child_handler_dict.iteritems():
self.putChild(path, child_cls(session))
Expand Down Expand Up @@ -172,65 +173,63 @@ def render_GET(self, request):

class TrustchainBootstrapEndpoint(TrustchainBaseEndpoint):
"""
Bootstrap a new identity and transfer some reputation to the new key.
Bootstrap a new identity and transfer some bandwidth tokens to the new key.
"""

def render_GET(self, request):
"""
.. http:get:: /trustchain/bootstrap?up=int&down=int
.. http:get:: /trustchain/bootstrap?amount=int
A GET request to this endpoint generates a new identity and transfers some reputation to it.
A GET request to this endpoint generates a new identity and transfers bandwidth tokens to it.
The amount specifies how much tokens need to be emptied into the new identity
**Example request**:
.. sourcecode:: none
curl -X GET http://localhost:8085/trustchain/bootstrap?up=100000&down=40000
curl -X GET http://localhost:8085/trustchain/bootstrap?amount=1000
**Example response**:
.. sourcecode:: javascript
{
"private_key" : "NEW_KEY",
"public_key" : "NEW_PUBLIC_KEY" ,
"transactions" : [
{
"hash": "ab672fd6acc0",
"tx" : {
"up": 100000,
"down": 40000,
"total_up": 100000,
"total_down": 40000,
},
"sequence_number": 0,
"link_public_key": "PC_KEY",
"link_sequence_number": 1210,
"previous_hash": 00000000
}
]
"private_key": "TGliTmFDTFNLOmC4BR7otCpn+NzTBAFwKdSJdpT0KG9Zy5vPGX6s3rDXmNiDoGKyToLeYYB88vj9\nRj5NW
pbNf/ldcixYZ2YxQ7Q=\n",
"transaction": {
"down": 0,
"up": 1000
},
"block": {
"block_hash": "THJxNlKWMQG1Tio+Yz5CUCrnWahcyk6TDVfRLQf7w6M=\n",
"sequence_number": 1
}
}
"""

mc_community = self.get_trustchain_community()
if not mc_community:
request.setResponseCode(http.NOT_FOUND)
return json.dumps({"error": "trustchain community not found"})

statistics = mc_community.get_statistics()
up = statistics['total_up']
down = statistics['total_down']
if 'up' in request.args:
try:
up = int(request.args['up'][0])
except ValueError:
up = up
available_tokens = mc_community.get_bandwidth_tokens()

if 'down' in request.args:
if 'amount' in request.args:
try:
down = int(request.args['down'][0])
amount = int(request.args['amount'][0])
except ValueError:
down = down
request.setResponseCode(http.BAD_REQUEST)
return json.dumps({"error": "Provided token amount is not a number"})

if amount <= 0:
request.setResponseCode(http.BAD_REQUEST)
return json.dumps({"error": "Provided token amount is zero or negative"})
else:
amount = available_tokens

result = mc_community.bootstrap_new_identity(up, down)
if amount <= 0 or amount > available_tokens:
request.setResponseCode(http.BAD_REQUEST)
return json.dumps({"error": "Not enough bandwidth tokens available"})

result = mc_community.bootstrap_new_identity(amount)
return json.dumps(result)
62 changes: 62 additions & 0 deletions Tribler/Test/Community/Triblerchain/test_community.py
Original file line number Diff line number Diff line change
Expand Up @@ -386,3 +386,65 @@ def test_get_default_trust(self):
other_trust = blockingCallFromThread(reactor, other.community.get_trust, node.community.my_member)
self.assertEqual(node_trust, 1)
self.assertEqual(other_trust, 1)

def test_get_bandwidth_tokens_for_self(self):
"""
Test that the bandwidth tokens the own node has is the upload - the download total of all blocks.
"""
# Arrange
node, other = self.create_nodes(2)
transaction = {'up': 10, 'down': 5}
transaction2 = {'up': 5, 'down': 10}
TestTriblerChainCommunity.create_block(node, other, self._create_target(node, other), transaction)
TestTriblerChainCommunity.create_block(other, node, self._create_target(other, node), transaction2)

# Get statistics
node_trust = blockingCallFromThread(reactor, node.community.get_bandwidth_tokens, node.community.my_member)
other_trust = blockingCallFromThread(reactor, other.community.get_bandwidth_tokens, other.community.my_member)
self.assertEqual(node_trust, 5)
self.assertEqual(other_trust, -5)

def test_get_bandwidth_tokens(self):
"""
Test that the bandwidth tokens nodes have is the upload - the download total of all blocks.
"""
# Arrange
node, other = self.create_nodes(2)
transaction = {'up': 10, 'down': 5}
transaction2 = {'up': 5, 'down': 10}
TestTriblerChainCommunity.create_block(node, other, self._create_target(node, other), transaction)
TestTriblerChainCommunity.create_block(other, node, self._create_target(other, node), transaction2)

# Get statistics
node_trust = blockingCallFromThread(reactor, node.community.get_bandwidth_tokens, other.community.my_member)
other_trust = blockingCallFromThread(reactor, other.community.get_bandwidth_tokens, node.community.my_member)
self.assertEqual(node_trust, -5)
self.assertEqual(other_trust, 5)

def test_get_default_bandwidth_tokens(self):
"""
Test that the bandwidth token amount for nodes without blocks is 0.
"""
# Arrange
node, other = self.create_nodes(2)

# Get statistics
node_trust = blockingCallFromThread(reactor, node.community.get_bandwidth_tokens, other.community.my_member)
other_trust = blockingCallFromThread(reactor, other.community.get_bandwidth_tokens, node.community.my_member)
self.assertEqual(node_trust, 0)
self.assertEqual(other_trust, 0)

def test_bootstrapping(self):
"""
Test that bootstrapping process works.
"""
# Arrange
node, = self.create_nodes(1)

node_bootstrap = blockingCallFromThread(reactor, node.community.bootstrap_new_identity, 100)
self.assertNotEqual(node_bootstrap['private_key'],
node.community.my_member.private_key.key_to_bin().encode('base64'))
self.assertEqual(node_bootstrap['transaction']['up'], 100)
self.assertEqual(node_bootstrap['transaction']['down'], 0)
self.assertEqual(node_bootstrap['block']['sequence_number'], 1)
self.assertNotEqual(node_bootstrap['block']['block_hash'], "")
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class TestBlock(TrustChainBlock):
Also used in other test files for TrustChain.
"""

def __init__(self, transaction=None, previous=None):
def __init__(self, transaction=None, previous=None, key=None):
crypto = ECCrypto()
other = crypto.generate_key(u"curve25519").pub().key_to_bin()

Expand All @@ -27,7 +27,11 @@ def __init__(self, transaction=None, previous=None):
TrustChainBlock.__init__(self, (encode(transaction), previous.public_key, previous.sequence_number + 1,
other, 0, previous.hash, 0, 0))
else:
self.key = crypto.generate_key(u"curve25519")
if key:
self.key = key
else:
self.key = crypto.generate_key(u"curve25519")

TrustChainBlock.__init__(self, (
encode(transaction), self.key.pub().key_to_bin(), random.randint(50, 100), other, 0,
sha256(str(random.randint(0, 100000))).digest(), 0, 0))
Expand Down
92 changes: 92 additions & 0 deletions Tribler/Test/Core/Modules/RestApi/test_trustchain_endpoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,3 +166,95 @@ def test_get_blocks_unlimited(self):
self.should_check_equality = False
return self.do_request('trustchain/blocks/%s' % TestBlock().public_key.encode("HEX"),
expected_code=200)

@deferred(timeout=10)
def test_get_bootstrap_identity_no_community(self):
"""
Testing whether the API returns error 404 if no trustchain community is loaded when bootstrapping a new identity
"""
self.dispersy.get_communities = lambda: []
return self.do_request('trustchain/bootstrap', expected_code=404)

@deferred(timeout=10)
def test_get_bootstrap_identity_all_tokens(self):
"""
Testing whether the API return all available credit when no argument is supplied
"""
transaction = {'up': 100, 'down': 0, 'total_up': 100, 'total_down': 0}
transaction2 = {'up': 100, 'down': 0}

def verify_response(response):
response_json = json.loads(response)
self.assertEqual(response_json['transaction'], transaction2)

test_block = TestBlock(transaction=transaction, key=self.tc_community.my_member.private_key)
self.tc_community.persistence.add_block(test_block)

self.should_check_equality = False
return self.do_request('trustchain/bootstrap', expected_code=200).addCallback(verify_response)

@deferred(timeout=10)
def test_get_bootstrap_identity_partial_tokens(self):
"""
Testing whether the API return partial available credit when argument is supplied
"""
transaction = {'up': 100, 'down': 0, 'total_up': 100, 'total_down': 0}
transaction2 = {'up': 50, 'down': 0}

def verify_response(response):
response_json = json.loads(response)
self.assertEqual(response_json['transaction'], transaction2)

test_block = TestBlock(transaction=transaction, key=self.tc_community.my_member.private_key)
self.tc_community.persistence.add_block(test_block)

self.should_check_equality = False
return self.do_request('trustchain/bootstrap?amount=50', expected_code=200).addCallback(verify_response)

@deferred(timeout=10)
def test_get_bootstrap_identity_not_enough_tokens(self):
"""
Testing whether the API returns error 400 if bandwidth is to low when bootstrapping a new identity
"""
transaction = {'up': 100, 'down': 0, 'total_up': 100, 'total_down': 0}
test_block = TestBlock(transaction=transaction, key=self.tc_community.my_member.private_key)
self.tc_community.persistence.add_block(test_block)

self.should_check_equality = False
return self.do_request('trustchain/bootstrap?amount=200', expected_code=400)

@deferred(timeout=10)
def test_get_bootstrap_identity_not_enough_tokens_2(self):
"""
Testing whether the API returns error 400 if bandwidth is to low when bootstrapping a new identity
"""
transaction = {'up': 0, 'down': 100, 'total_up': 0, 'total_down': 100}
test_block = TestBlock(transaction=transaction, key=self.tc_community.my_member.private_key)
self.tc_community.persistence.add_block(test_block)

self.should_check_equality = False
return self.do_request('trustchain/bootstrap?amount=10', expected_code=400)

@deferred(timeout=10)
def test_get_bootstrap_identity_zero_amount(self):
"""
Testing whether the API returns error 400 if amount is zero when bootstrapping a new identity
"""
self.should_check_equality = False
return self.do_request('trustchain/bootstrap?amount=0', expected_code=400)

@deferred(timeout=10)
def test_get_bootstrap_identity_negative_amount(self):
"""
Testing whether the API returns error 400 if amount is negative when bootstrapping a new identity
"""
self.should_check_equality = False
return self.do_request('trustchain/bootstrap?amount=-1', expected_code=400)

@deferred(timeout=10)
def test_get_bootstrap_identity_string(self):
"""
Testing whether the API returns error 400 if amount is string when bootstrapping a new identity
"""
self.should_check_equality = False
return self.do_request('trustchain/bootstrap?amount=aaa', expected_code=400)
40 changes: 27 additions & 13 deletions Tribler/community/triblerchain/community.py
Original file line number Diff line number Diff line change
Expand Up @@ -215,10 +215,29 @@ def get_trust(self, member):
# We need a minimum of 1 trust to have a chance to be selected in the categorical distribution.
return 1

def bootstrap_new_identity(self, up, down):
"""
def get_bandwidth_tokens(self, member=None):
"""
Get the bandwidth tokens for another member.
Currently this is just the difference in the amount of MBs exchanged with them.
:param member: the member we interacted with
:type member: dispersy.member.Member
:return: the amount of bandwidth tokens for this member
:rtype: int
"""
if member is None:
member = self.my_member

block = self.persistence.get_latest(member.public_key)
if block:
return block.transaction['total_up'] - block.transaction['total_down']

return 0

def bootstrap_new_identity(self, amount):
"""
One-way payment channel.
Create a new temporary identity , and transfer funds to the new identity.
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
"""

Expand All @@ -227,15 +246,15 @@ def bootstrap_new_identity(self, up, down):

# Create the transaction specification
transaction = {
'up': up, 'down': down
'up': 0, 'down': amount
}

# Create the two half blocks that form the transaction
local_half_block = TriblerChainBlock.create(transaction, self.persistence, self.my_member.public_key,
link_pk=tmp_member.public_key)
local_half_block.sign(self.my_member.private_key)
tmp_half_block = TriblerChainBlock.create(transaction, DBShim(), tmp_member.public_key, link=local_half_block,
link_pk=self.my_member.public_key)
tmp_half_block = TriblerChainBlock.create(transaction, self.persistence, tmp_member.public_key,
link=local_half_block, link_pk=self.my_member.public_key)
tmp_half_block.sign(tmp_member.private_key)

self.persistence.add_block(local_half_block)
Expand All @@ -245,8 +264,8 @@ def bootstrap_new_identity(self, up, down):
block = {'block_hash': tmp_half_block.hash.encode('base64'),
'sequence_number': tmp_half_block.sequence_number}

result = {'transaction': transaction, 'block': block,
'private_key': tmp_member.private_key.key_to_bin().encode('base64')}
result = {'private_key': tmp_member.private_key.key_to_bin().encode('base64'),
'transaction': {'up': amount, 'down': 0}, 'block': block}
return result


Expand All @@ -266,8 +285,3 @@ def on_introduction_response(self, messages):

def start_walking(self):
self.register_task("take step", LoopingCall(self.take_step)).start(self.CrawlerDelay, now=False)


class DBShim:
def get_latest(self, x):
return {}
Loading

0 comments on commit ca678e3

Please sign in to comment.