From 6d99427bc7bdbda6cdff517a3b4c61ddd8aa60c8 Mon Sep 17 00:00:00 2001 From: diminator Date: Thu, 21 Apr 2016 18:59:48 +0200 Subject: [PATCH 1/9] hashlocked conditions + tests + docs --- bigchaindb/util.py | 33 +- docs/source/python-server-api-examples.md | 146 ++++++ tests/db/test_bigchain_api.py | 130 ++++++ .../doc/run_doc_python_server_api_examples.py | 429 ++++++++++-------- 4 files changed, 540 insertions(+), 198 deletions(-) diff --git a/bigchaindb/util.py b/bigchaindb/util.py index e17874ac..8b03444d 100644 --- a/bigchaindb/util.py +++ b/bigchaindb/util.py @@ -202,15 +202,20 @@ def create_tx(current_owners, new_owners, inputs, operation, payload=None): }, } """ - current_owners = current_owners if isinstance(current_owners, list) else [current_owners] - new_owners = new_owners if isinstance(new_owners, list) else [new_owners] - inputs = inputs if isinstance(inputs, list) else [inputs] + # validate arguments (owners and inputs should be lists or None) - # validate arguments (owners and inputs should be lists) + # The None case appears on fulfilling a hashlock + if current_owners is None: + current_owners = [] if not isinstance(current_owners, list): current_owners = [current_owners] + + # The None case appears on assigning a hashlock + if new_owners is None: + new_owners = [] if not isinstance(new_owners, list): new_owners = [new_owners] + if not isinstance(inputs, list): inputs = [inputs] @@ -250,20 +255,24 @@ def create_tx(current_owners, new_owners, inputs, operation, payload=None): # handle outputs conditions = [] for fulfillment in fulfillments: + condition = None if len(new_owners) > 1: condition = cc.ThresholdSha256Fulfillment(threshold=len(new_owners)) for new_owner in new_owners: condition.add_subfulfillment(cc.Ed25519Fulfillment(public_key=new_owner)) elif len(new_owners) == 1: condition = cc.Ed25519Fulfillment(public_key=new_owners[0]) - conditions.append({ - 'new_owners': new_owners, - 'condition': { - 'details': json.loads(condition.serialize_json()), - 'uri': condition.condition.serialize_uri() - }, - 'cid': fulfillment['fid'] - }) + + # The None case appears on assigning a hashlock + if condition: + conditions.append({ + 'new_owners': new_owners, + 'condition': { + 'details': json.loads(condition.serialize_json()), + 'uri': condition.condition.serialize_uri() + }, + 'cid': fulfillment['fid'] + }) tx = { 'fulfillments': fulfillments, diff --git a/docs/source/python-server-api-examples.md b/docs/source/python-server-api-examples.md index 15ee08f7..4bbfe6d1 100644 --- a/docs/source/python-server-api-examples.md +++ b/docs/source/python-server-api-examples.md @@ -746,3 +746,149 @@ threshold_tx_transfer "version":1 } ``` + + +### Hash-locked Conditions + +By creating a hash of a difficult-to-guess 256-bit random or pseudo-random integer it is possible to create a condition which the creator can trivially fulfill by publishing the random value. However, for anyone else, the condition is cryptographically hard to fulfill, because they would have to find a preimage for the given condition hash. + +One possible usecase might be to redeem a digital voucher when given a secret (voucher code). + +```python +# Create a hash-locked asset without any new_owners +hashlock_tx = b.create_transaction(b.me, None, None, 'CREATE') + +# Define a secret that will be hashed - fulfillments need to guess the secret +secret = b'much secret! wow!' +first_tx_condition = cc.PreimageSha256Fulfillment(preimage=secret) + +# The conditions list is empty, so we need to append a new condition +hashlock_tx['transaction']['conditions'].append({ + 'condition': { + 'details': json.loads(first_tx_condition.serialize_json()), + 'uri': first_tx_condition.condition.serialize_uri() + }, + 'cid': 0, + 'new_owners': None +}) + +# Conditions have been updated, so hash needs updating +hashlock_tx['id'] = util.get_hash_data(hashlock_tx) + +# The asset needs to be signed by the current_owner +hashlock_tx_signed = b.sign_transaction(hashlock_tx, b.me_private) + +# Some validations +assert b.validate_transaction(hashlock_tx_signed) == hashlock_tx_signed +assert b.is_valid_transaction(hashlock_tx_signed) == hashlock_tx_signed + +b.write_transaction(hashlock_tx_signed) +hashlock_tx_signed +``` + +```python +{ + "assignee":"FmLm6MxCABc8TsiZKdeYaZKo5yZWMM6Vty7Q1B6EgcP2", + "id":"604c520244b7ff63604527baf269e0cbfb887122f503703120fd347d6b99a237", + "transaction":{ + "conditions":[ + { + "cid":0, + "condition":{ + "details":{ + "bitmask":3, + "preimage":"much secret! wow!", + "type":"fulfillment", + "type_id":0 + }, + "uri":"cc:0:3:nsW2IiYgk9EUtsg4uBe3pBnOgRoAEX2IIsPgjqZz47U:17" + }, + "new_owners":None + } + ], + "data":None, + "fulfillments":[ + { + "current_owners":[ + "FmLm6MxCABc8TsiZKdeYaZKo5yZWMM6Vty7Q1B6EgcP2" + ], + "fid":0, + "fulfillment":"cf:4:21-D-LfNhIQhvY5914ArFTUGpgPKc7EVC1ZtJqqOTHGx1p9FuRr9tRfkbdqtX2MZWh7sRVUmMnwp7I1-xZbCnCkeADf69IwDHbZvNS6aTr1CpekREsV9ZG8m_wjlZiUN", + "input":None + } + ], + "operation":"CREATE", + "timestamp":"1461250387.910102" + }, + "version":1 +} +``` + +In order to redeem the asset, one needs to create a fulfillment the correct secret as a preimage: + +```python +hashlockuser_priv, hashlockuser_pub = crypto.generate_key_pair() + +# create hashlock fulfillment tx +hashlock_fulfill_tx = b.create_transaction(None, hashlockuser_pub, {'txid': hashlock_tx['id'], 'cid': 0}, 'TRANSFER') + +# provide a wrong secret +hashlock_fulfill_tx_fulfillment = cc.PreimageSha256Fulfillment(preimage=b'') +hashlock_fulfill_tx['transaction']['fulfillments'][0]['fulfillment'] = \ + hashlock_fulfill_tx_fulfillment.serialize_uri() + +assert b.is_valid_transaction(hashlock_fulfill_tx) == False + +# provide the right secret +hashlock_fulfill_tx_fulfillment = cc.PreimageSha256Fulfillment(preimage=secret) +hashlock_fulfill_tx['transaction']['fulfillments'][0]['fulfillment'] = \ + hashlock_fulfill_tx_fulfillment.serialize_uri() + +assert b.validate_transaction(hashlock_fulfill_tx) == hashlock_fulfill_tx +assert b.is_valid_transaction(hashlock_fulfill_tx) == hashlock_fulfill_tx + +b.write_transaction(hashlock_fulfill_tx) +hashlock_fulfill_tx +``` + +```python +{ + "assignee":"FmLm6MxCABc8TsiZKdeYaZKo5yZWMM6Vty7Q1B6EgcP2", + "id":"fe6871bf3ca62eb61c52c5555cec2e07af51df817723f0cb76e5cf6248f449d2", + "transaction":{ + "conditions":[ + { + "cid":0, + "condition":{ + "details":{ + "bitmask":32, + "public_key":"EiqCKxnBCmmNb83qyGch48tULK9RLaEt4xFA43UVCVDb", + "signature":None, + "type":"fulfillment", + "type_id":4 + }, + "uri":"cc:4:20:y9884Md2YI_wdnGSTJGhwvFaNsKLe8sqwimqk-2JLSI:96" + }, + "new_owners":[ + "EiqCKxnBCmmNb83qyGch48tULK9RLaEt4xFA43UVCVDb" + ] + } + ], + "data":None, + "fulfillments":[ + { + "current_owners":[], + "fid":0, + "fulfillment":"cf:0:bXVjaCBzZWNyZXQhIHdvdyE", + "input":{ + "cid":0, + "txid":"604c520244b7ff63604527baf269e0cbfb887122f503703120fd347d6b99a237" + } + } + ], + "operation":"TRANSFER", + "timestamp":"1461250397.944510" + }, + "version":1 +} +``` \ No newline at end of file diff --git a/tests/db/test_bigchain_api.py b/tests/db/test_bigchain_api.py index 71ad97a2..07479944 100644 --- a/tests/db/test_bigchain_api.py +++ b/tests/db/test_bigchain_api.py @@ -1544,6 +1544,136 @@ class TestCryptoconditions(object): assert b.verify_signature(tx_transfer_signed) is True + def test_create_asset_with_hashlock_condition(self, b): + hashlock_tx = b.create_transaction(b.me, None, None, 'CREATE') + + secret = b'much secret! wow!' + first_tx_condition = cc.PreimageSha256Fulfillment(preimage=secret) + + hashlock_tx['transaction']['conditions'].append({ + 'condition': { + 'details': json.loads(first_tx_condition.serialize_json()), + 'uri': first_tx_condition.condition.serialize_uri() + }, + 'cid': 0, + 'new_owners': None + }) + # conditions have been updated, so hash needs updating + hashlock_tx['id'] = util.get_hash_data(hashlock_tx) + + hashlock_tx_signed = b.sign_transaction(hashlock_tx, b.me_private) + + assert b.validate_transaction(hashlock_tx_signed) == hashlock_tx_signed + assert b.is_valid_transaction(hashlock_tx_signed) == hashlock_tx_signed + + b.write_transaction(hashlock_tx_signed) + + # create and write block to bigchain + block = b.create_block([hashlock_tx_signed]) + b.write_block(block, durability='hard') + + @pytest.mark.usefixtures('inputs') + def test_transfer_asset_with_hashlock_condition(self, b, user_vk, user_sk): + first_input_tx = b.get_owned_ids(user_vk).pop() + + hashlock_tx = b.create_transaction(user_vk, None, first_input_tx, 'TRANSFER') + + secret = b'much secret! wow!' + first_tx_condition = cc.PreimageSha256Fulfillment(preimage=secret) + + hashlock_tx['transaction']['conditions'].append({ + 'condition': { + 'details': json.loads(first_tx_condition.serialize_json()), + 'uri': first_tx_condition.condition.serialize_uri() + }, + 'cid': 0, + 'new_owners': None + }) + # conditions have been updated, so hash needs updating + hashlock_tx['id'] = util.get_hash_data(hashlock_tx) + + hashlock_tx_signed = b.sign_transaction(hashlock_tx, user_sk) + + assert b.validate_transaction(hashlock_tx_signed) == hashlock_tx_signed + assert b.is_valid_transaction(hashlock_tx_signed) == hashlock_tx_signed + assert len(b.get_owned_ids(user_vk)) == 1 + + b.write_transaction(hashlock_tx_signed) + + # create and write block to bigchain + block = b.create_block([hashlock_tx_signed]) + b.write_block(block, durability='hard') + + assert len(b.get_owned_ids(user_vk)) == 0 + + def test_create_and_fulfill_asset_with_hashlock_condition(self, b, user_vk): + hashlock_tx = b.create_transaction(b.me, None, None, 'CREATE') + + secret = b'much secret! wow!' + first_tx_condition = cc.PreimageSha256Fulfillment(preimage=secret) + + hashlock_tx['transaction']['conditions'].append({ + 'condition': { + 'details': json.loads(first_tx_condition.serialize_json()), + 'uri': first_tx_condition.condition.serialize_uri() + }, + 'cid': 0, + 'new_owners': None + }) + # conditions have been updated, so hash needs updating + hashlock_tx['id'] = util.get_hash_data(hashlock_tx) + + hashlock_tx_signed = b.sign_transaction(hashlock_tx, b.me_private) + + assert b.validate_transaction(hashlock_tx_signed) == hashlock_tx_signed + assert b.is_valid_transaction(hashlock_tx_signed) == hashlock_tx_signed + + b.write_transaction(hashlock_tx_signed) + + # create and write block to bigchain + block = b.create_block([hashlock_tx_signed]) + b.write_block(block, durability='hard') + + assert len(b.get_owned_ids(b.me)) == 0 + + # create hashlock fulfillment tx + hashlock_fulfill_tx = b.create_transaction(None, user_vk, {'txid': hashlock_tx['id'], 'cid': 0}, 'TRANSFER') + + hashlock_fulfill_tx_fulfillment = cc.PreimageSha256Fulfillment(preimage=b'') + hashlock_fulfill_tx['transaction']['fulfillments'][0]['fulfillment'] = \ + hashlock_fulfill_tx_fulfillment.serialize_uri() + + with pytest.raises(exceptions.InvalidSignature): + b.validate_transaction(hashlock_fulfill_tx) + assert b.is_valid_transaction(hashlock_fulfill_tx) == False + + hashlock_fulfill_tx_fulfillment = cc.PreimageSha256Fulfillment(preimage=secret) + hashlock_fulfill_tx['transaction']['fulfillments'][0]['fulfillment'] = \ + hashlock_fulfill_tx_fulfillment.serialize_uri() + + assert b.validate_transaction(hashlock_fulfill_tx) == hashlock_fulfill_tx + assert b.is_valid_transaction(hashlock_fulfill_tx) == hashlock_fulfill_tx + + b.write_transaction(hashlock_fulfill_tx) + + # create and write block to bigchain + block = b.create_block([hashlock_fulfill_tx]) + b.write_block(block, durability='hard') + + assert len(b.get_owned_ids(b.me)) == 0 + assert len(b.get_owned_ids(user_vk)) == 1 + + # try doublespending + user2_sk, user2_vk = crypto.generate_key_pair() + hashlock_doublespend_tx = b.create_transaction(None, user2_vk, {'txid': hashlock_tx['id'], 'cid': 0}, 'TRANSFER') + + hashlock_doublespend_tx_fulfillment = cc.PreimageSha256Fulfillment(preimage=secret) + hashlock_doublespend_tx['transaction']['fulfillments'][0]['fulfillment'] = \ + hashlock_doublespend_tx_fulfillment.serialize_uri() + + with pytest.raises(exceptions.DoubleSpend): + b.validate_transaction(hashlock_doublespend_tx) + def test_get_subcondition_from_vk(self, b, user_sk, user_vk): user2_sk, user2_vk = crypto.generate_key_pair() user3_sk, user3_vk = crypto.generate_key_pair() diff --git a/tests/doc/run_doc_python_server_api_examples.py b/tests/doc/run_doc_python_server_api_examples.py index 3500d6ab..355cc957 100644 --- a/tests/doc/run_doc_python_server_api_examples.py +++ b/tests/doc/run_doc_python_server_api_examples.py @@ -29,208 +29,265 @@ tx_signed = b.sign_transaction(tx, b.me_private) # included in a block, and written to the bigchain b.write_transaction(tx_signed) -sleep(10) +# sleep(10) +# +# """ +# Read the Creation Transaction from the DB +# """ +# +# tx_retrieved = b.get_transaction(tx_signed['id']) +# +# print(json.dumps(tx_retrieved, sort_keys=True, indent=4, separators=(',', ':'))) +# +# print(testuser1_pub) +# print(b.me) +# +# print(tx_retrieved['id']) +# +# """ +# Transfer the Digital Asset +# """ +# +# # create a second testuser +# testuser2_priv, testuser2_pub = crypto.generate_key_pair() +# +# # retrieve the transaction with condition id +# tx_retrieved_id = b.get_owned_ids(testuser1_pub).pop() +# print(json.dumps(tx_retrieved_id, sort_keys=True, indent=4, separators=(',', ':'))) +# +# # create a transfer transaction +# tx_transfer = b.create_transaction(testuser1_pub, testuser2_pub, tx_retrieved_id, 'TRANSFER') +# +# # sign the transaction +# tx_transfer_signed = b.sign_transaction(tx_transfer, testuser1_priv) +# +# # write the transaction +# b.write_transaction(tx_transfer_signed) +# +# sleep(10) +# +# # check if the transaction is already in the bigchain +# tx_transfer_retrieved = b.get_transaction(tx_transfer_signed['id']) +# print(json.dumps(tx_transfer_retrieved, sort_keys=True, indent=4, separators=(',', ':'))) +# +# """ +# Double Spends +# """ +# +# # create another transfer transaction with the same input +# tx_transfer2 = b.create_transaction(testuser1_pub, testuser2_pub, tx_retrieved_id, 'TRANSFER') +# +# # sign the transaction +# tx_transfer_signed2 = b.sign_transaction(tx_transfer2, testuser1_priv) +# +# # check if the transaction is valid +# try: +# b.validate_transaction(tx_transfer_signed2) +# except exceptions.DoubleSpend as e: +# print(e) +# +# """ +# Multiple Owners +# """ +# +# # Create a new asset and assign it to multiple owners +# tx_multisig = b.create_transaction(b.me, [testuser1_pub, testuser2_pub], None, 'CREATE') +# +# # Have the federation sign the transaction +# tx_multisig_signed = b.sign_transaction(tx_multisig, b.me_private) +# b.write_transaction(tx_multisig_signed) +# +# # wait a few seconds for the asset to appear on the blockchain +# sleep(10) +# +# # retrieve the transaction +# tx_multisig_retrieved = b.get_transaction(tx_multisig_signed['id']) +# +# print(json.dumps(tx_multisig_retrieved, sort_keys=True, indent=4, separators=(',', ':'))) +# +# testuser3_priv, testuser3_pub = crypto.generate_key_pair() +# +# tx_multisig_retrieved_id = b.get_owned_ids(testuser2_pub).pop() +# tx_multisig_transfer = b.create_transaction([testuser1_pub, testuser2_pub], testuser3_pub, tx_multisig_retrieved_id, 'TRANSFER') +# tx_multisig_transfer_signed = b.sign_transaction(tx_multisig_transfer, [testuser1_priv, testuser2_priv]) +# +# b.write_transaction(tx_multisig_transfer_signed) +# +# # wait a few seconds for the asset to appear on the blockchain +# sleep(10) +# +# # retrieve the transaction +# tx_multisig_retrieved = b.get_transaction(tx_multisig_transfer_signed['id']) +# +# print(json.dumps(tx_multisig_transfer_signed, sort_keys=True, indent=4, separators=(',', ':'))) +# +# """ +# Multiple Inputs and Outputs +# """ +# for i in range(3): +# tx_mimo_asset = b.create_transaction(b.me, testuser1_pub, None, 'CREATE') +# tx_mimo_asset_signed = b.sign_transaction(tx_mimo_asset, b.me_private) +# b.write_transaction(tx_mimo_asset_signed) +# +# sleep(10) +# +# # get inputs +# owned_mimo_inputs = b.get_owned_ids(testuser1_pub) +# print(len(owned_mimo_inputs)) +# +# # create a transaction +# tx_mimo = b.create_transaction(testuser1_pub, testuser2_pub, owned_mimo_inputs, 'TRANSFER') +# tx_mimo_signed = b.sign_transaction(tx_mimo, testuser1_priv) +# +# # write the transaction +# b.write_transaction(tx_mimo_signed) +# +# print(json.dumps(tx_mimo_signed, sort_keys=True, indent=4, separators=(',', ':'))) +# +# """ +# Threshold Conditions +# """ +# +# # create some new testusers +# thresholduser1_priv, thresholduser1_pub = crypto.generate_key_pair() +# thresholduser2_priv, thresholduser2_pub = crypto.generate_key_pair() +# thresholduser3_priv, thresholduser3_pub = crypto.generate_key_pair() +# +# # retrieve the last transaction of testuser2 +# tx_retrieved_id = b.get_owned_ids(testuser2_pub).pop() +# +# # create a base template for a 1-input/3-output transaction +# threshold_tx = b.create_transaction(testuser2_pub, [thresholduser1_pub, thresholduser2_pub, thresholduser3_pub], +# tx_retrieved_id, 'TRANSFER') +# +# # create a 2-out-of-3 Threshold Cryptocondition +# threshold_condition = cc.ThresholdSha256Fulfillment(threshold=2) +# threshold_condition.add_subfulfillment(cc.Ed25519Fulfillment(public_key=thresholduser1_pub)) +# threshold_condition.add_subfulfillment(cc.Ed25519Fulfillment(public_key=thresholduser2_pub)) +# threshold_condition.add_subfulfillment(cc.Ed25519Fulfillment(public_key=thresholduser3_pub)) +# +# # update the condition in the newly created transaction +# threshold_tx['transaction']['conditions'][0]['condition'] = { +# 'details': json.loads(threshold_condition.serialize_json()), +# 'uri': threshold_condition.condition.serialize_uri() +# } +# +# # conditions have been updated, so hash needs updating +# threshold_tx['id'] = util.get_hash_data(threshold_tx) +# +# # sign the transaction +# threshold_tx_signed = b.sign_transaction(threshold_tx, testuser2_priv) +# +# # write the transaction +# b.write_transaction(threshold_tx_signed) +# +# sleep(10) +# +# # check if the transaction is already in the bigchain +# tx_threshold_retrieved = b.get_transaction(threshold_tx_signed['id']) +# print(json.dumps(tx_threshold_retrieved, sort_keys=True, indent=4, separators=(',', ':'))) +# +# thresholduser4_priv, thresholduser4_pub = crypto.generate_key_pair() +# +# # retrieve the last transaction of thresholduser1_pub +# tx_retrieved_id = b.get_owned_ids(thresholduser1_pub).pop() +# +# # create a base template for a 2-input/1-output transaction +# threshold_tx_transfer = b.create_transaction([thresholduser1_pub, thresholduser2_pub, thresholduser3_pub], +# thresholduser4_pub, tx_retrieved_id, 'TRANSFER') +# +# # parse the threshold cryptocondition +# threshold_fulfillment = cc.Fulfillment.from_json(threshold_tx['transaction']['conditions'][0]['condition']['details']) +# +# subfulfillment1 = threshold_fulfillment.get_subcondition_from_vk(thresholduser1_pub)[0] +# subfulfillment2 = threshold_fulfillment.get_subcondition_from_vk(thresholduser2_pub)[0] +# subfulfillment3 = threshold_fulfillment.get_subcondition_from_vk(thresholduser3_pub)[0] +# +# +# # get the fulfillment message to sign +# threshold_tx_fulfillment_message = util.get_fulfillment_message(threshold_tx_transfer, +# threshold_tx_transfer['transaction']['fulfillments'][0], +# serialized=True) +# +# # clear the subconditions of the threshold fulfillment, they will be added again after signing +# threshold_fulfillment.subconditions = [] +# +# # sign and add the subconditions until threshold of 2 is reached +# subfulfillment1.sign(threshold_tx_fulfillment_message, crypto.SigningKey(thresholduser1_priv)) +# threshold_fulfillment.add_subfulfillment(subfulfillment1) +# subfulfillment2.sign(threshold_tx_fulfillment_message, crypto.SigningKey(thresholduser2_priv)) +# threshold_fulfillment.add_subfulfillment(subfulfillment2) +# +# # Add remaining (unfulfilled) fulfillment as a condition +# threshold_fulfillment.add_subcondition(subfulfillment3.condition) +# +# assert threshold_fulfillment.validate(threshold_tx_fulfillment_message) == True +# +# threshold_tx_transfer['transaction']['fulfillments'][0]['fulfillment'] = threshold_fulfillment.serialize_uri() +# +# assert b.verify_signature(threshold_tx_transfer) == True +# +# assert b.validate_transaction(threshold_tx_transfer) == threshold_tx_transfer +# +# b.write_transaction(threshold_tx_transfer) +# +# print(json.dumps(threshold_tx_transfer, sort_keys=True, indent=4, separators=(',', ':'))) """ -Read the Creation Transaction from the DB +Hashlocked Conditions """ -tx_retrieved = b.get_transaction(tx_signed['id']) +# Create a hash-locked asset without any new_owners +hashlock_tx = b.create_transaction(b.me, None, None, 'CREATE') -print(json.dumps(tx_retrieved, sort_keys=True, indent=4, separators=(',', ':'))) +# Define a secret that will be hashed - fulfillments need to guess the secret +secret = b'much secret! wow!' +first_tx_condition = cc.PreimageSha256Fulfillment(preimage=secret) -print(testuser1_pub) -print(b.me) +# The conditions list is empty, so we need to append a new condition +hashlock_tx['transaction']['conditions'].append({ + 'condition': { + 'details': json.loads(first_tx_condition.serialize_json()), + 'uri': first_tx_condition.condition.serialize_uri() + }, + 'cid': 0, + 'new_owners': None +}) -print(tx_retrieved['id']) +# Conditions have been updated, so hash needs updating +hashlock_tx['id'] = util.get_hash_data(hashlock_tx) -""" -Transfer the Digital Asset -""" +# The asset needs to be signed by the current_owner +hashlock_tx_signed = b.sign_transaction(hashlock_tx, b.me_private) -# create a second testuser -testuser2_priv, testuser2_pub = crypto.generate_key_pair() +# Some validations +assert b.validate_transaction(hashlock_tx_signed) == hashlock_tx_signed +assert b.is_valid_transaction(hashlock_tx_signed) == hashlock_tx_signed -# retrieve the transaction with condition id -tx_retrieved_id = b.get_owned_ids(testuser1_pub).pop() -print(json.dumps(tx_retrieved_id, sort_keys=True, indent=4, separators=(',', ':'))) - -# create a transfer transaction -tx_transfer = b.create_transaction(testuser1_pub, testuser2_pub, tx_retrieved_id, 'TRANSFER') - -# sign the transaction -tx_transfer_signed = b.sign_transaction(tx_transfer, testuser1_priv) - -# write the transaction -b.write_transaction(tx_transfer_signed) +b.write_transaction(hashlock_tx_signed) +print(json.dumps(hashlock_tx_signed, sort_keys=True, indent=4, separators=(',', ':'))) sleep(10) -# check if the transaction is already in the bigchain -tx_transfer_retrieved = b.get_transaction(tx_transfer_signed['id']) -print(json.dumps(tx_transfer_retrieved, sort_keys=True, indent=4, separators=(',', ':'))) +hashlockuser_priv, hashlockuser_pub = crypto.generate_key_pair() -""" -Double Spends -""" +# create hashlock fulfillment tx +hashlock_fulfill_tx = b.create_transaction(None, hashlockuser_priv, {'txid': hashlock_tx['id'], 'cid': 0}, 'TRANSFER') -# create another transfer transaction with the same input -tx_transfer2 = b.create_transaction(testuser1_pub, testuser2_pub, tx_retrieved_id, 'TRANSFER') +# try a wrong secret +hashlock_fulfill_tx_fulfillment = cc.PreimageSha256Fulfillment(preimage=b'') +hashlock_fulfill_tx['transaction']['fulfillments'][0]['fulfillment'] = \ + hashlock_fulfill_tx_fulfillment.serialize_uri() -# sign the transaction -tx_transfer_signed2 = b.sign_transaction(tx_transfer2, testuser1_priv) +assert b.is_valid_transaction(hashlock_fulfill_tx) == False -# check if the transaction is valid -try: - b.validate_transaction(tx_transfer_signed2) -except exceptions.DoubleSpend as e: - print(e) - -""" -Multiple Owners -""" - -# Create a new asset and assign it to multiple owners -tx_multisig = b.create_transaction(b.me, [testuser1_pub, testuser2_pub], None, 'CREATE') - -# Have the federation sign the transaction -tx_multisig_signed = b.sign_transaction(tx_multisig, b.me_private) -b.write_transaction(tx_multisig_signed) - -# wait a few seconds for the asset to appear on the blockchain -sleep(10) - -# retrieve the transaction -tx_multisig_retrieved = b.get_transaction(tx_multisig_signed['id']) - -print(json.dumps(tx_multisig_retrieved, sort_keys=True, indent=4, separators=(',', ':'))) - -testuser3_priv, testuser3_pub = crypto.generate_key_pair() - -tx_multisig_retrieved_id = b.get_owned_ids(testuser2_pub).pop() -tx_multisig_transfer = b.create_transaction([testuser1_pub, testuser2_pub], testuser3_pub, tx_multisig_retrieved_id, 'TRANSFER') -tx_multisig_transfer_signed = b.sign_transaction(tx_multisig_transfer, [testuser1_priv, testuser2_priv]) - -b.write_transaction(tx_multisig_transfer_signed) - -# wait a few seconds for the asset to appear on the blockchain -sleep(10) - -# retrieve the transaction -tx_multisig_retrieved = b.get_transaction(tx_multisig_transfer_signed['id']) - -print(json.dumps(tx_multisig_transfer_signed, sort_keys=True, indent=4, separators=(',', ':'))) - -""" -Multiple Inputs and Outputs -""" -for i in range(3): - tx_mimo_asset = b.create_transaction(b.me, testuser1_pub, None, 'CREATE') - tx_mimo_asset_signed = b.sign_transaction(tx_mimo_asset, b.me_private) - b.write_transaction(tx_mimo_asset_signed) - -sleep(10) - -# get inputs -owned_mimo_inputs = b.get_owned_ids(testuser1_pub) -print(len(owned_mimo_inputs)) - -# create a transaction -tx_mimo = b.create_transaction(testuser1_pub, testuser2_pub, owned_mimo_inputs, 'TRANSFER') -tx_mimo_signed = b.sign_transaction(tx_mimo, testuser1_priv) - -# write the transaction -b.write_transaction(tx_mimo_signed) - -print(json.dumps(tx_mimo_signed, sort_keys=True, indent=4, separators=(',', ':'))) - -""" -Threshold Conditions -""" - -# create some new testusers -thresholduser1_priv, thresholduser1_pub = crypto.generate_key_pair() -thresholduser2_priv, thresholduser2_pub = crypto.generate_key_pair() -thresholduser3_priv, thresholduser3_pub = crypto.generate_key_pair() - -# retrieve the last transaction of testuser2 -tx_retrieved_id = b.get_owned_ids(testuser2_pub).pop() - -# create a base template for a 1-input/3-output transaction -threshold_tx = b.create_transaction(testuser2_pub, [thresholduser1_pub, thresholduser2_pub, thresholduser3_pub], - tx_retrieved_id, 'TRANSFER') - -# create a 2-out-of-3 Threshold Cryptocondition -threshold_condition = cc.ThresholdSha256Fulfillment(threshold=2) -threshold_condition.add_subfulfillment(cc.Ed25519Fulfillment(public_key=thresholduser1_pub)) -threshold_condition.add_subfulfillment(cc.Ed25519Fulfillment(public_key=thresholduser2_pub)) -threshold_condition.add_subfulfillment(cc.Ed25519Fulfillment(public_key=thresholduser3_pub)) - -# update the condition in the newly created transaction -threshold_tx['transaction']['conditions'][0]['condition'] = { - 'details': json.loads(threshold_condition.serialize_json()), - 'uri': threshold_condition.condition.serialize_uri() -} - -# conditions have been updated, so hash needs updating -threshold_tx['id'] = util.get_hash_data(threshold_tx) - -# sign the transaction -threshold_tx_signed = b.sign_transaction(threshold_tx, testuser2_priv) - -# write the transaction -b.write_transaction(threshold_tx_signed) - -sleep(10) - -# check if the transaction is already in the bigchain -tx_threshold_retrieved = b.get_transaction(threshold_tx_signed['id']) -print(json.dumps(tx_threshold_retrieved, sort_keys=True, indent=4, separators=(',', ':'))) - -thresholduser4_priv, thresholduser4_pub = crypto.generate_key_pair() - -# retrieve the last transaction of thresholduser1_pub -tx_retrieved_id = b.get_owned_ids(thresholduser1_pub).pop() - -# create a base template for a 2-input/1-output transaction -threshold_tx_transfer = b.create_transaction([thresholduser1_pub, thresholduser2_pub, thresholduser3_pub], - thresholduser4_pub, tx_retrieved_id, 'TRANSFER') - -# parse the threshold cryptocondition -threshold_fulfillment = cc.Fulfillment.from_json(threshold_tx['transaction']['conditions'][0]['condition']['details']) - -subfulfillment1 = threshold_fulfillment.get_subcondition_from_vk(thresholduser1_pub)[0] -subfulfillment2 = threshold_fulfillment.get_subcondition_from_vk(thresholduser2_pub)[0] -subfulfillment3 = threshold_fulfillment.get_subcondition_from_vk(thresholduser3_pub)[0] - - -# get the fulfillment message to sign -threshold_tx_fulfillment_message = util.get_fulfillment_message(threshold_tx_transfer, - threshold_tx_transfer['transaction']['fulfillments'][0], - serialized=True) - -# clear the subconditions of the threshold fulfillment, they will be added again after signing -threshold_fulfillment.subconditions = [] - -# sign and add the subconditions until threshold of 2 is reached -subfulfillment1.sign(threshold_tx_fulfillment_message, crypto.SigningKey(thresholduser1_priv)) -threshold_fulfillment.add_subfulfillment(subfulfillment1) -subfulfillment2.sign(threshold_tx_fulfillment_message, crypto.SigningKey(thresholduser2_priv)) -threshold_fulfillment.add_subfulfillment(subfulfillment2) - -# Add remaining (unfulfilled) fulfillment as a condition -threshold_fulfillment.add_subcondition(subfulfillment3.condition) - -assert threshold_fulfillment.validate(threshold_tx_fulfillment_message) == True - -threshold_tx_transfer['transaction']['fulfillments'][0]['fulfillment'] = threshold_fulfillment.serialize_uri() - -assert b.verify_signature(threshold_tx_transfer) == True - -assert b.validate_transaction(threshold_tx_transfer) == threshold_tx_transfer - -b.write_transaction(threshold_tx_transfer) - -print(json.dumps(threshold_tx_transfer, sort_keys=True, indent=4, separators=(',', ':'))) +# provide the right secret +hashlock_fulfill_tx_fulfillment = cc.PreimageSha256Fulfillment(preimage=secret) +hashlock_fulfill_tx['transaction']['fulfillments'][0]['fulfillment'] = \ + hashlock_fulfill_tx_fulfillment.serialize_uri() +assert b.validate_transaction(hashlock_fulfill_tx) == hashlock_fulfill_tx +assert b.is_valid_transaction(hashlock_fulfill_tx) == hashlock_fulfill_tx +b.write_transaction(hashlock_fulfill_tx) +print(json.dumps(hashlock_fulfill_tx, sort_keys=True, indent=4, separators=(',', ':'))) From e92627e4474be8f53e1f4a38c2019e07d3bbd85c Mon Sep 17 00:00:00 2001 From: diminator Date: Thu, 21 Apr 2016 19:02:07 +0200 Subject: [PATCH 2/9] uncommented example code --- .../doc/run_doc_python_server_api_examples.py | 406 +++++++++--------- 1 file changed, 203 insertions(+), 203 deletions(-) diff --git a/tests/doc/run_doc_python_server_api_examples.py b/tests/doc/run_doc_python_server_api_examples.py index 355cc957..17e9cf87 100644 --- a/tests/doc/run_doc_python_server_api_examples.py +++ b/tests/doc/run_doc_python_server_api_examples.py @@ -29,209 +29,209 @@ tx_signed = b.sign_transaction(tx, b.me_private) # included in a block, and written to the bigchain b.write_transaction(tx_signed) -# sleep(10) -# -# """ -# Read the Creation Transaction from the DB -# """ -# -# tx_retrieved = b.get_transaction(tx_signed['id']) -# -# print(json.dumps(tx_retrieved, sort_keys=True, indent=4, separators=(',', ':'))) -# -# print(testuser1_pub) -# print(b.me) -# -# print(tx_retrieved['id']) -# -# """ -# Transfer the Digital Asset -# """ -# -# # create a second testuser -# testuser2_priv, testuser2_pub = crypto.generate_key_pair() -# -# # retrieve the transaction with condition id -# tx_retrieved_id = b.get_owned_ids(testuser1_pub).pop() -# print(json.dumps(tx_retrieved_id, sort_keys=True, indent=4, separators=(',', ':'))) -# -# # create a transfer transaction -# tx_transfer = b.create_transaction(testuser1_pub, testuser2_pub, tx_retrieved_id, 'TRANSFER') -# -# # sign the transaction -# tx_transfer_signed = b.sign_transaction(tx_transfer, testuser1_priv) -# -# # write the transaction -# b.write_transaction(tx_transfer_signed) -# -# sleep(10) -# -# # check if the transaction is already in the bigchain -# tx_transfer_retrieved = b.get_transaction(tx_transfer_signed['id']) -# print(json.dumps(tx_transfer_retrieved, sort_keys=True, indent=4, separators=(',', ':'))) -# -# """ -# Double Spends -# """ -# -# # create another transfer transaction with the same input -# tx_transfer2 = b.create_transaction(testuser1_pub, testuser2_pub, tx_retrieved_id, 'TRANSFER') -# -# # sign the transaction -# tx_transfer_signed2 = b.sign_transaction(tx_transfer2, testuser1_priv) -# -# # check if the transaction is valid -# try: -# b.validate_transaction(tx_transfer_signed2) -# except exceptions.DoubleSpend as e: -# print(e) -# -# """ -# Multiple Owners -# """ -# -# # Create a new asset and assign it to multiple owners -# tx_multisig = b.create_transaction(b.me, [testuser1_pub, testuser2_pub], None, 'CREATE') -# -# # Have the federation sign the transaction -# tx_multisig_signed = b.sign_transaction(tx_multisig, b.me_private) -# b.write_transaction(tx_multisig_signed) -# -# # wait a few seconds for the asset to appear on the blockchain -# sleep(10) -# -# # retrieve the transaction -# tx_multisig_retrieved = b.get_transaction(tx_multisig_signed['id']) -# -# print(json.dumps(tx_multisig_retrieved, sort_keys=True, indent=4, separators=(',', ':'))) -# -# testuser3_priv, testuser3_pub = crypto.generate_key_pair() -# -# tx_multisig_retrieved_id = b.get_owned_ids(testuser2_pub).pop() -# tx_multisig_transfer = b.create_transaction([testuser1_pub, testuser2_pub], testuser3_pub, tx_multisig_retrieved_id, 'TRANSFER') -# tx_multisig_transfer_signed = b.sign_transaction(tx_multisig_transfer, [testuser1_priv, testuser2_priv]) -# -# b.write_transaction(tx_multisig_transfer_signed) -# -# # wait a few seconds for the asset to appear on the blockchain -# sleep(10) -# -# # retrieve the transaction -# tx_multisig_retrieved = b.get_transaction(tx_multisig_transfer_signed['id']) -# -# print(json.dumps(tx_multisig_transfer_signed, sort_keys=True, indent=4, separators=(',', ':'))) -# -# """ -# Multiple Inputs and Outputs -# """ -# for i in range(3): -# tx_mimo_asset = b.create_transaction(b.me, testuser1_pub, None, 'CREATE') -# tx_mimo_asset_signed = b.sign_transaction(tx_mimo_asset, b.me_private) -# b.write_transaction(tx_mimo_asset_signed) -# -# sleep(10) -# -# # get inputs -# owned_mimo_inputs = b.get_owned_ids(testuser1_pub) -# print(len(owned_mimo_inputs)) -# -# # create a transaction -# tx_mimo = b.create_transaction(testuser1_pub, testuser2_pub, owned_mimo_inputs, 'TRANSFER') -# tx_mimo_signed = b.sign_transaction(tx_mimo, testuser1_priv) -# -# # write the transaction -# b.write_transaction(tx_mimo_signed) -# -# print(json.dumps(tx_mimo_signed, sort_keys=True, indent=4, separators=(',', ':'))) -# -# """ -# Threshold Conditions -# """ -# -# # create some new testusers -# thresholduser1_priv, thresholduser1_pub = crypto.generate_key_pair() -# thresholduser2_priv, thresholduser2_pub = crypto.generate_key_pair() -# thresholduser3_priv, thresholduser3_pub = crypto.generate_key_pair() -# -# # retrieve the last transaction of testuser2 -# tx_retrieved_id = b.get_owned_ids(testuser2_pub).pop() -# -# # create a base template for a 1-input/3-output transaction -# threshold_tx = b.create_transaction(testuser2_pub, [thresholduser1_pub, thresholduser2_pub, thresholduser3_pub], -# tx_retrieved_id, 'TRANSFER') -# -# # create a 2-out-of-3 Threshold Cryptocondition -# threshold_condition = cc.ThresholdSha256Fulfillment(threshold=2) -# threshold_condition.add_subfulfillment(cc.Ed25519Fulfillment(public_key=thresholduser1_pub)) -# threshold_condition.add_subfulfillment(cc.Ed25519Fulfillment(public_key=thresholduser2_pub)) -# threshold_condition.add_subfulfillment(cc.Ed25519Fulfillment(public_key=thresholduser3_pub)) -# -# # update the condition in the newly created transaction -# threshold_tx['transaction']['conditions'][0]['condition'] = { -# 'details': json.loads(threshold_condition.serialize_json()), -# 'uri': threshold_condition.condition.serialize_uri() -# } -# -# # conditions have been updated, so hash needs updating -# threshold_tx['id'] = util.get_hash_data(threshold_tx) -# -# # sign the transaction -# threshold_tx_signed = b.sign_transaction(threshold_tx, testuser2_priv) -# -# # write the transaction -# b.write_transaction(threshold_tx_signed) -# -# sleep(10) -# -# # check if the transaction is already in the bigchain -# tx_threshold_retrieved = b.get_transaction(threshold_tx_signed['id']) -# print(json.dumps(tx_threshold_retrieved, sort_keys=True, indent=4, separators=(',', ':'))) -# -# thresholduser4_priv, thresholduser4_pub = crypto.generate_key_pair() -# -# # retrieve the last transaction of thresholduser1_pub -# tx_retrieved_id = b.get_owned_ids(thresholduser1_pub).pop() -# -# # create a base template for a 2-input/1-output transaction -# threshold_tx_transfer = b.create_transaction([thresholduser1_pub, thresholduser2_pub, thresholduser3_pub], -# thresholduser4_pub, tx_retrieved_id, 'TRANSFER') -# -# # parse the threshold cryptocondition -# threshold_fulfillment = cc.Fulfillment.from_json(threshold_tx['transaction']['conditions'][0]['condition']['details']) -# -# subfulfillment1 = threshold_fulfillment.get_subcondition_from_vk(thresholduser1_pub)[0] -# subfulfillment2 = threshold_fulfillment.get_subcondition_from_vk(thresholduser2_pub)[0] -# subfulfillment3 = threshold_fulfillment.get_subcondition_from_vk(thresholduser3_pub)[0] -# -# -# # get the fulfillment message to sign -# threshold_tx_fulfillment_message = util.get_fulfillment_message(threshold_tx_transfer, -# threshold_tx_transfer['transaction']['fulfillments'][0], -# serialized=True) -# -# # clear the subconditions of the threshold fulfillment, they will be added again after signing -# threshold_fulfillment.subconditions = [] -# -# # sign and add the subconditions until threshold of 2 is reached -# subfulfillment1.sign(threshold_tx_fulfillment_message, crypto.SigningKey(thresholduser1_priv)) -# threshold_fulfillment.add_subfulfillment(subfulfillment1) -# subfulfillment2.sign(threshold_tx_fulfillment_message, crypto.SigningKey(thresholduser2_priv)) -# threshold_fulfillment.add_subfulfillment(subfulfillment2) -# -# # Add remaining (unfulfilled) fulfillment as a condition -# threshold_fulfillment.add_subcondition(subfulfillment3.condition) -# -# assert threshold_fulfillment.validate(threshold_tx_fulfillment_message) == True -# -# threshold_tx_transfer['transaction']['fulfillments'][0]['fulfillment'] = threshold_fulfillment.serialize_uri() -# -# assert b.verify_signature(threshold_tx_transfer) == True -# -# assert b.validate_transaction(threshold_tx_transfer) == threshold_tx_transfer -# -# b.write_transaction(threshold_tx_transfer) -# -# print(json.dumps(threshold_tx_transfer, sort_keys=True, indent=4, separators=(',', ':'))) +sleep(10) + +""" +Read the Creation Transaction from the DB +""" + +tx_retrieved = b.get_transaction(tx_signed['id']) + +print(json.dumps(tx_retrieved, sort_keys=True, indent=4, separators=(',', ':'))) + +print(testuser1_pub) +print(b.me) + +print(tx_retrieved['id']) + +""" +Transfer the Digital Asset +""" + +# create a second testuser +testuser2_priv, testuser2_pub = crypto.generate_key_pair() + +# retrieve the transaction with condition id +tx_retrieved_id = b.get_owned_ids(testuser1_pub).pop() +print(json.dumps(tx_retrieved_id, sort_keys=True, indent=4, separators=(',', ':'))) + +# create a transfer transaction +tx_transfer = b.create_transaction(testuser1_pub, testuser2_pub, tx_retrieved_id, 'TRANSFER') + +# sign the transaction +tx_transfer_signed = b.sign_transaction(tx_transfer, testuser1_priv) + +# write the transaction +b.write_transaction(tx_transfer_signed) + +sleep(10) + +# check if the transaction is already in the bigchain +tx_transfer_retrieved = b.get_transaction(tx_transfer_signed['id']) +print(json.dumps(tx_transfer_retrieved, sort_keys=True, indent=4, separators=(',', ':'))) + +""" +Double Spends +""" + +# create another transfer transaction with the same input +tx_transfer2 = b.create_transaction(testuser1_pub, testuser2_pub, tx_retrieved_id, 'TRANSFER') + +# sign the transaction +tx_transfer_signed2 = b.sign_transaction(tx_transfer2, testuser1_priv) + +# check if the transaction is valid +try: + b.validate_transaction(tx_transfer_signed2) +except exceptions.DoubleSpend as e: + print(e) + +""" +Multiple Owners +""" + +# Create a new asset and assign it to multiple owners +tx_multisig = b.create_transaction(b.me, [testuser1_pub, testuser2_pub], None, 'CREATE') + +# Have the federation sign the transaction +tx_multisig_signed = b.sign_transaction(tx_multisig, b.me_private) +b.write_transaction(tx_multisig_signed) + +# wait a few seconds for the asset to appear on the blockchain +sleep(10) + +# retrieve the transaction +tx_multisig_retrieved = b.get_transaction(tx_multisig_signed['id']) + +print(json.dumps(tx_multisig_retrieved, sort_keys=True, indent=4, separators=(',', ':'))) + +testuser3_priv, testuser3_pub = crypto.generate_key_pair() + +tx_multisig_retrieved_id = b.get_owned_ids(testuser2_pub).pop() +tx_multisig_transfer = b.create_transaction([testuser1_pub, testuser2_pub], testuser3_pub, tx_multisig_retrieved_id, 'TRANSFER') +tx_multisig_transfer_signed = b.sign_transaction(tx_multisig_transfer, [testuser1_priv, testuser2_priv]) + +b.write_transaction(tx_multisig_transfer_signed) + +# wait a few seconds for the asset to appear on the blockchain +sleep(10) + +# retrieve the transaction +tx_multisig_retrieved = b.get_transaction(tx_multisig_transfer_signed['id']) + +print(json.dumps(tx_multisig_transfer_signed, sort_keys=True, indent=4, separators=(',', ':'))) + +""" +Multiple Inputs and Outputs +""" +for i in range(3): + tx_mimo_asset = b.create_transaction(b.me, testuser1_pub, None, 'CREATE') + tx_mimo_asset_signed = b.sign_transaction(tx_mimo_asset, b.me_private) + b.write_transaction(tx_mimo_asset_signed) + +sleep(10) + +# get inputs +owned_mimo_inputs = b.get_owned_ids(testuser1_pub) +print(len(owned_mimo_inputs)) + +# create a transaction +tx_mimo = b.create_transaction(testuser1_pub, testuser2_pub, owned_mimo_inputs, 'TRANSFER') +tx_mimo_signed = b.sign_transaction(tx_mimo, testuser1_priv) + +# write the transaction +b.write_transaction(tx_mimo_signed) + +print(json.dumps(tx_mimo_signed, sort_keys=True, indent=4, separators=(',', ':'))) + +""" +Threshold Conditions +""" + +# create some new testusers +thresholduser1_priv, thresholduser1_pub = crypto.generate_key_pair() +thresholduser2_priv, thresholduser2_pub = crypto.generate_key_pair() +thresholduser3_priv, thresholduser3_pub = crypto.generate_key_pair() + +# retrieve the last transaction of testuser2 +tx_retrieved_id = b.get_owned_ids(testuser2_pub).pop() + +# create a base template for a 1-input/3-output transaction +threshold_tx = b.create_transaction(testuser2_pub, [thresholduser1_pub, thresholduser2_pub, thresholduser3_pub], + tx_retrieved_id, 'TRANSFER') + +# create a 2-out-of-3 Threshold Cryptocondition +threshold_condition = cc.ThresholdSha256Fulfillment(threshold=2) +threshold_condition.add_subfulfillment(cc.Ed25519Fulfillment(public_key=thresholduser1_pub)) +threshold_condition.add_subfulfillment(cc.Ed25519Fulfillment(public_key=thresholduser2_pub)) +threshold_condition.add_subfulfillment(cc.Ed25519Fulfillment(public_key=thresholduser3_pub)) + +# update the condition in the newly created transaction +threshold_tx['transaction']['conditions'][0]['condition'] = { + 'details': json.loads(threshold_condition.serialize_json()), + 'uri': threshold_condition.condition.serialize_uri() +} + +# conditions have been updated, so hash needs updating +threshold_tx['id'] = util.get_hash_data(threshold_tx) + +# sign the transaction +threshold_tx_signed = b.sign_transaction(threshold_tx, testuser2_priv) + +# write the transaction +b.write_transaction(threshold_tx_signed) + +sleep(10) + +# check if the transaction is already in the bigchain +tx_threshold_retrieved = b.get_transaction(threshold_tx_signed['id']) +print(json.dumps(tx_threshold_retrieved, sort_keys=True, indent=4, separators=(',', ':'))) + +thresholduser4_priv, thresholduser4_pub = crypto.generate_key_pair() + +# retrieve the last transaction of thresholduser1_pub +tx_retrieved_id = b.get_owned_ids(thresholduser1_pub).pop() + +# create a base template for a 2-input/1-output transaction +threshold_tx_transfer = b.create_transaction([thresholduser1_pub, thresholduser2_pub, thresholduser3_pub], + thresholduser4_pub, tx_retrieved_id, 'TRANSFER') + +# parse the threshold cryptocondition +threshold_fulfillment = cc.Fulfillment.from_json(threshold_tx['transaction']['conditions'][0]['condition']['details']) + +subfulfillment1 = threshold_fulfillment.get_subcondition_from_vk(thresholduser1_pub)[0] +subfulfillment2 = threshold_fulfillment.get_subcondition_from_vk(thresholduser2_pub)[0] +subfulfillment3 = threshold_fulfillment.get_subcondition_from_vk(thresholduser3_pub)[0] + + +# get the fulfillment message to sign +threshold_tx_fulfillment_message = util.get_fulfillment_message(threshold_tx_transfer, + threshold_tx_transfer['transaction']['fulfillments'][0], + serialized=True) + +# clear the subconditions of the threshold fulfillment, they will be added again after signing +threshold_fulfillment.subconditions = [] + +# sign and add the subconditions until threshold of 2 is reached +subfulfillment1.sign(threshold_tx_fulfillment_message, crypto.SigningKey(thresholduser1_priv)) +threshold_fulfillment.add_subfulfillment(subfulfillment1) +subfulfillment2.sign(threshold_tx_fulfillment_message, crypto.SigningKey(thresholduser2_priv)) +threshold_fulfillment.add_subfulfillment(subfulfillment2) + +# Add remaining (unfulfilled) fulfillment as a condition +threshold_fulfillment.add_subcondition(subfulfillment3.condition) + +assert threshold_fulfillment.validate(threshold_tx_fulfillment_message) == True + +threshold_tx_transfer['transaction']['fulfillments'][0]['fulfillment'] = threshold_fulfillment.serialize_uri() + +assert b.verify_signature(threshold_tx_transfer) == True + +assert b.validate_transaction(threshold_tx_transfer) == threshold_tx_transfer + +b.write_transaction(threshold_tx_transfer) + +print(json.dumps(threshold_tx_transfer, sort_keys=True, indent=4, separators=(',', ':'))) """ Hashlocked Conditions From 1813c2687967330744205463ae0b60080273064c Mon Sep 17 00:00:00 2001 From: Rodolphe Marques Date: Thu, 28 Apr 2016 13:54:50 +0200 Subject: [PATCH 3/9] validate_block now checks if a federation node created the block. It checks to see if node_pubkey is a federation node and if the signature is correct. Created tests --- bigchaindb/consensus.py | 10 +++++++++- tests/db/test_bigchain_api.py | 31 +++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/bigchaindb/consensus.py b/bigchaindb/consensus.py index 277dad3e..26d89623 100644 --- a/bigchaindb/consensus.py +++ b/bigchaindb/consensus.py @@ -167,7 +167,6 @@ class BaseConsensusRules(AbstractConsensusRules): return transaction - # TODO: Unsure if a bigchain parameter is really necessary here? @staticmethod def validate_block(bigchain, block): """Validate a block. @@ -189,6 +188,15 @@ class BaseConsensusRules(AbstractConsensusRules): if calculated_hash != block['id']: raise exceptions.InvalidHash() + # Check if the block was created by a federation node + if block['block']['node_pubkey'] not in (bigchain.federation_nodes + [bigchain.me]): + raise exceptions.OperationError('Only federation nodes can create blocks') + + # Check if block signature is valid + verifying_key = crypto.VerifyingKey(block['block']['node_pubkey']) + if not verifying_key.verify(util.serialize(block['block']), block['signature']): + raise exceptions.InvalidSignature('Invalid block signature') + return block @staticmethod diff --git a/tests/db/test_bigchain_api.py b/tests/db/test_bigchain_api.py index 23e73580..340f5c2d 100644 --- a/tests/db/test_bigchain_api.py +++ b/tests/db/test_bigchain_api.py @@ -407,6 +407,37 @@ class TestBlockValidation(object): assert block == b.validate_block(block) assert b.is_valid_block(block) + def test_invalid_signature(self, b): + # create a valid block + block = b.create_block([]) + + # replace the block signature with an invalid one + block['signature'] = crypto.SigningKey(b.me_private).sign(b'wrongdata') + + # check that validate_block raises an InvalidSignature exception + with pytest.raises(exceptions.InvalidSignature): + b.validate_block(block) + + def test_invalid_node_pubkey(self, b): + # blocks can only be created by a federation node + # create a valid block + block = b.create_block([]) + + # create some temp keys + tmp_sk, tmp_vk = crypto.generate_key_pair() + + # change the block node_pubkey + block['block']['node_pubkey'] = tmp_vk + + # just to make sure lets re-hash the block and create a valid signature + # from a non federation node + block['id'] = crypto.hash_data(util.serialize(block['block'])) + block['signature'] = crypto.SigningKey(tmp_sk).sign(util.serialize(block['block'])) + + # check that validate_block raises an OperationError + with pytest.raises(exceptions.OperationError): + b.validate_block(block) + class TestBigchainVoter(object): From 783c9a5834eeb5b08d4733f6e4edd508608e90e6 Mon Sep 17 00:00:00 2001 From: troymc Date: Fri, 29 Apr 2016 15:01:36 +0200 Subject: [PATCH 4/9] Python Style Guide: How to Format Long import Statements --- PYTHON_STYLE_GUIDE.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/PYTHON_STYLE_GUIDE.md b/PYTHON_STYLE_GUIDE.md index 61863860..ca94c197 100644 --- a/PYTHON_STYLE_GUIDE.md +++ b/PYTHON_STYLE_GUIDE.md @@ -43,6 +43,16 @@ my_string = 'This is a very long string, so long that it will not fit into just It seems the preference is for slashes, but using parentheses is okay too. (There are good arguments either way. Arguing about it seems like a waste of time.) +### How to Format Long import Statements + +If you need to `import` lots of names from a module or package, and they won't all fit in one line (without making the line too long), then use parentheses to spread the names across multiple lines, like so: +```python +from Tkinter import (Tk, Frame, Button, Entry, Canvas, Text, + LEFT, DISABLED, NORMAL, RIDGE, END) +``` + +For the rationale, see [PEP 328](https://www.python.org/dev/peps/pep-0328/#rationale-for-parentheses). + ### Using the % operator or `format()` to Format Strings Given the choice: From db06970b1a864daede1ee3c450c94ce576477612 Mon Sep 17 00:00:00 2001 From: diminator Date: Tue, 3 May 2016 11:18:57 +0200 Subject: [PATCH 5/9] PR comments --- bigchaindb/util.py | 10 ++++++++-- docs/source/python-server-api-examples.md | 7 ------- tests/doc/run_doc_python_server_api_examples.py | 1 - 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/bigchaindb/util.py b/bigchaindb/util.py index f173d4e5..9f0e59c3 100644 --- a/bigchaindb/util.py +++ b/bigchaindb/util.py @@ -256,15 +256,21 @@ def create_tx(current_owners, new_owners, inputs, operation, payload=None): # handle outputs conditions = [] for fulfillment in fulfillments: - condition = None + + # threshold condition if len(new_owners) > 1: condition = cc.ThresholdSha256Fulfillment(threshold=len(new_owners)) for new_owner in new_owners: condition.add_subfulfillment(cc.Ed25519Fulfillment(public_key=new_owner)) + + # simple signature condition elif len(new_owners) == 1: condition = cc.Ed25519Fulfillment(public_key=new_owners[0]) - # The None case appears on assigning a hashlock + # to be added later (hashlock conditions) + else: + condition = None + if condition: conditions.append({ 'new_owners': new_owners, diff --git a/docs/source/python-server-api-examples.md b/docs/source/python-server-api-examples.md index 4bbfe6d1..c191fe74 100644 --- a/docs/source/python-server-api-examples.md +++ b/docs/source/python-server-api-examples.md @@ -765,7 +765,6 @@ first_tx_condition = cc.PreimageSha256Fulfillment(preimage=secret) # The conditions list is empty, so we need to append a new condition hashlock_tx['transaction']['conditions'].append({ 'condition': { - 'details': json.loads(first_tx_condition.serialize_json()), 'uri': first_tx_condition.condition.serialize_uri() }, 'cid': 0, @@ -795,12 +794,6 @@ hashlock_tx_signed { "cid":0, "condition":{ - "details":{ - "bitmask":3, - "preimage":"much secret! wow!", - "type":"fulfillment", - "type_id":0 - }, "uri":"cc:0:3:nsW2IiYgk9EUtsg4uBe3pBnOgRoAEX2IIsPgjqZz47U:17" }, "new_owners":None diff --git a/tests/doc/run_doc_python_server_api_examples.py b/tests/doc/run_doc_python_server_api_examples.py index 17e9cf87..56a6aa31 100644 --- a/tests/doc/run_doc_python_server_api_examples.py +++ b/tests/doc/run_doc_python_server_api_examples.py @@ -247,7 +247,6 @@ first_tx_condition = cc.PreimageSha256Fulfillment(preimage=secret) # The conditions list is empty, so we need to append a new condition hashlock_tx['transaction']['conditions'].append({ 'condition': { - 'details': json.loads(first_tx_condition.serialize_json()), 'uri': first_tx_condition.condition.serialize_uri() }, 'cid': 0, From bf46d7d497e5f74e049be8b33d5733aa8b073247 Mon Sep 17 00:00:00 2001 From: Dimitri De Jonghe Date: Tue, 3 May 2016 11:28:35 +0200 Subject: [PATCH 6/9] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5c38f639..7865103d 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![PyPI](https://img.shields.io/pypi/v/bigchaindb.svg)](https://pypi.python.org/pypi/BigchainDB) [![Travis branch](https://img.shields.io/travis/bigchaindb/bigchaindb/master.svg)](https://travis-ci.org/bigchaindb/bigchaindb) [![Codecov branch](https://img.shields.io/codecov/c/github/bigchaindb/bigchaindb/master.svg)](https://codecov.io/github/bigchaindb/bigchaindb?branch=master) -[![Documentation Status](https://readthedocs.org/projects/bigchaindb/badge/?version=stable)](https://bigchaindb.readthedocs.org/en/stable/) +[![Documentation Status](https://readthedocs.org/projects/bigchaindb/badge/?version=latest)](https://bigchaindb.readthedocs.org/en/latest/) # BigchainDB From 2ac3e360f7b7cea6726d7904651c1baca4b31ec8 Mon Sep 17 00:00:00 2001 From: Rodolphe Marques Date: Tue, 3 May 2016 11:36:22 +0200 Subject: [PATCH 7/9] Bump version number 0.2.0 -> 0.3.0 Updated changelog --- CHANGELOG.md | 16 ++++++++++++++++ bigchaindb/version.py | 4 ++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e4f44483..8d9aabd3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,22 @@ For reference, the possible headings are: * **External Contributors** to list contributors outside of ascribe GmbH. +## [0.3.0] - 2016-05-03 +Tag name: v0.3.0 += commit: +committed: + +### Added +- Crypto-conditions specs according to the Interledger protocol: [Pull Request #174](https://github.com/bigchaindb/bigchaindb/pull/174) +- Added support for anonymous hashlocked conditions and fulfillments: [Pull Request #211](https://github.com/bigchaindb/bigchaindb/pull/211) + +### Changed +- Several improvements to the aws deployment scripts: [Pull Request #227](https://github.com/bigchaindb/bigchaindb/pull/227) + +### Fixed +- Bug related to block validation: [Pull Request #233](https://github.com/bigchaindb/bigchaindb/pull/233) + + ## [0.2.0] - 2016-04-26 Tag name: v0.2.0 = commit: 0c4a2b380aabdcf50fa2d7fb351c290aaedc3db7 diff --git a/bigchaindb/version.py b/bigchaindb/version.py index 3af9f911..793d1adc 100644 --- a/bigchaindb/version.py +++ b/bigchaindb/version.py @@ -1,2 +1,2 @@ -__version__ = '0.2.0' -__short_version__ = '0.2' \ No newline at end of file +__version__ = '0.3.0' +__short_version__ = '0.3' \ No newline at end of file From a37d2ff19dfc830010f1459d48f3078db041c061 Mon Sep 17 00:00:00 2001 From: Rodolphe Marques Date: Tue, 3 May 2016 11:46:33 +0200 Subject: [PATCH 8/9] updated changelog --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d9aabd3..f886d3dd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,13 @@ committed: ### Fixed - Bug related to block validation: [Pull Request #233](https://github.com/bigchaindb/bigchaindb/pull/233) +### Notes +This release completely refactored the structure of the transactions and broke compatibility with older versions +of BigchainDB. The refactor of the transactions was made in order to add support for multiple inputs/outputs and +the crypto-conditions specs from the Interledger protocol. + +We also updated the rethinkdb python drivers so you need to upgrade to rethinkdb v2.3+ + ## [0.2.0] - 2016-04-26 Tag name: v0.2.0 From a97c54e82be954a1411e5bfe0f09a9c631309f1e Mon Sep 17 00:00:00 2001 From: Rodolphe Marques Date: Tue, 3 May 2016 11:52:23 +0200 Subject: [PATCH 9/9] added version.py to codecov ignore --- codecov.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/codecov.yml b/codecov.yml index d3956b6c..71dc2177 100644 --- a/codecov.yml +++ b/codecov.yml @@ -26,6 +26,7 @@ coverage: - "deploy-cluster-aws/*" - "docs/*" - "tests/*" + - "bigchaindb/version.py" comment: # @stevepeak (from codecov.io) suggested we change 'suggestions' to 'uncovered'