# Copyright © 2020 Interplanetary Database Association e.V., # Planetmint and IPDB software contributors. # SPDX-License-Identifier: (Apache-2.0 AND CC-BY-4.0) # Code is Apache-2.0 and docs are CC-BY-4.0 # ## Imports import time import json # For this test case we need the planetmint_driver.crypto package import base58 import sha3 from cryptoconditions import Ed25519Sha256, ThresholdSha256 from planetmint_driver.crypto import generate_keypair # Import helper to deal with multiple nodes from .helper.hosts import Hosts def prepare_condition_details(condition: ThresholdSha256): condition_details = { 'subconditions': [], 'threshold': condition.threshold, 'type': condition.TYPE_NAME } for s in condition.subconditions: if (s['type'] == 'fulfillment' and s['body'].TYPE_NAME == 'ed25519-sha-256'): condition_details['subconditions'].append({ 'type': s['body'].TYPE_NAME, 'public_key': base58.b58encode(s['body'].public_key).decode() }) else: condition_details['subconditions'].append(prepare_condition_details(s['body'])) return condition_details def test_threshold(): # Setup connection to test nodes hosts = Hosts('/shared/hostnames') pm = hosts.get_connection() # Generate Keypars for Alice, Bob an Carol! alice, bob, carol = generate_keypair(), generate_keypair(), generate_keypair() # ## Alice and Bob create a transaction # Alice and Bob just moved into a shared flat, no one can afford these # high rents anymore. Bob suggests to get a dish washer for the # kitchen. Alice agrees and here they go, creating the asset for their # dish washer. dw_asset = { 'data': { 'dish washer': { 'serial_number': 1337 } } } # Create subfulfillments alice_ed25519 = Ed25519Sha256(public_key=base58.b58decode(alice.public_key)) bob_ed25519 = Ed25519Sha256(public_key=base58.b58decode(bob.public_key)) carol_ed25519 = Ed25519Sha256(public_key=base58.b58decode(carol.public_key)) # Create threshold condition (2/3) and add subfulfillments threshold_sha256 = ThresholdSha256(2) threshold_sha256.add_subfulfillment(alice_ed25519) threshold_sha256.add_subfulfillment(bob_ed25519) threshold_sha256.add_subfulfillment(carol_ed25519) # Create a condition uri and details for the output object condition_uri = threshold_sha256.condition.serialize_uri() condition_details = prepare_condition_details(threshold_sha256) # Assemble output and input for the handcrafted tx output = { 'amount': '1', 'condition': { 'details': condition_details, 'uri': condition_uri, }, 'public_keys': (alice.public_key, bob.public_key, carol.public_key), } # The yet to be fulfilled input: input_ = { 'fulfillment': None, 'fulfills': None, 'owners_before': (alice.public_key, bob.public_key), } # Assemble the handcrafted transaction handcrafted_dw_tx = { 'operation': 'CREATE', 'asset': dw_asset, 'metadata': None, 'outputs': (output,), 'inputs': (input_,), 'version': '2.0', 'id': None, } # Create sha3-256 of message to sign message = json.dumps( handcrafted_dw_tx, sort_keys=True, separators=(',', ':'), ensure_ascii=False, ) message = sha3.sha3_256(message.encode()) # Sign message with Alice's und Bob's private key alice_ed25519.sign(message.digest(), base58.b58decode(alice.private_key)) bob_ed25519.sign(message.digest(), base58.b58decode(bob.private_key)) # Create fulfillment and add uri to inputs fulfillment_threshold = ThresholdSha256(2) fulfillment_threshold.add_subfulfillment(alice_ed25519) fulfillment_threshold.add_subfulfillment(bob_ed25519) fulfillment_threshold.add_subcondition(carol_ed25519.condition) fulfillment_uri = fulfillment_threshold.serialize_uri() handcrafted_dw_tx['inputs'][0]['fulfillment'] = fulfillment_uri # Create tx_id for handcrafted_dw_tx and send tx commit json_str_tx = json.dumps( handcrafted_dw_tx, sort_keys=True, separators=(',', ':'), ensure_ascii=False, ) dw_creation_txid = sha3.sha3_256(json_str_tx.encode()).hexdigest() handcrafted_dw_tx['id'] = dw_creation_txid pm.transactions.send_commit(handcrafted_dw_tx) time.sleep(1) # Assert that the tx is propagated to all nodes hosts.assert_transaction(dw_creation_txid) def test_weighted_threshold(): hosts = Hosts('/shared/hostnames') pm = hosts.get_connection() alice, bob, carol = generate_keypair(), generate_keypair(), generate_keypair() asset = { 'data': { 'trashcan': { 'animals': ['racoon_1', 'racoon_2'] } } } alice_ed25519 = Ed25519Sha256(public_key=base58.b58decode(alice.public_key)) bob_ed25519 = Ed25519Sha256(public_key=base58.b58decode(bob.public_key)) carol_ed25519 = Ed25519Sha256(public_key=base58.b58decode(carol.public_key)) threshold = ThresholdSha256(1) threshold.add_subfulfillment(alice_ed25519) sub_threshold = ThresholdSha256(2) sub_threshold.add_subfulfillment(bob_ed25519) sub_threshold.add_subfulfillment(carol_ed25519) threshold.add_subfulfillment(sub_threshold) condition_uri = threshold.condition.serialize_uri() condition_details = prepare_condition_details(threshold) # Assemble output and input for the handcrafted tx output = { 'amount': '1', 'condition': { 'details': condition_details, 'uri': condition_uri, }, 'public_keys': (alice.public_key, bob.public_key, carol.public_key), } # The yet to be fulfilled input: input_ = { 'fulfillment': None, 'fulfills': None, 'owners_before': (alice.public_key, bob.public_key), } # Assemble the handcrafted transaction handcrafted_tx = { 'operation': 'CREATE', 'asset': asset, 'metadata': None, 'outputs': (output,), 'inputs': (input_,), 'version': '2.0', 'id': None, } # Create sha3-256 of message to sign message = json.dumps( handcrafted_tx, sort_keys=True, separators=(',', ':'), ensure_ascii=False, ) message = sha3.sha3_256(message.encode()) # Sign message with Alice's und Bob's private key alice_ed25519.sign(message.digest(), base58.b58decode(alice.private_key)) # Create fulfillment and add uri to inputs sub_fulfillment_threshold = ThresholdSha256(2) sub_fulfillment_threshold.add_subcondition(bob_ed25519.condition) sub_fulfillment_threshold.add_subcondition(carol_ed25519.condition) fulfillment_threshold = ThresholdSha256(1) fulfillment_threshold.add_subfulfillment(alice_ed25519) fulfillment_threshold.add_subfulfillment(sub_fulfillment_threshold) fulfillment_uri = fulfillment_threshold.serialize_uri() handcrafted_tx['inputs'][0]['fulfillment'] = fulfillment_uri # Create tx_id for handcrafted_dw_tx and send tx commit json_str_tx = json.dumps( handcrafted_tx, sort_keys=True, separators=(',', ':'), ensure_ascii=False, ) creation_tx_id = sha3.sha3_256(json_str_tx.encode()).hexdigest() handcrafted_tx['id'] = creation_tx_id pm.transactions.send_commit(handcrafted_tx) time.sleep(1) # Assert that the tx is propagated to all nodes hosts.assert_transaction(creation_tx_id) # Now transfer created asset alice_transfer_ed25519 = Ed25519Sha256(public_key=base58.b58decode(alice.public_key)) bob_transfer_ed25519 = Ed25519Sha256(public_key=base58.b58decode(bob.public_key)) carol_transfer_ed25519 = Ed25519Sha256(public_key=base58.b58decode(carol.public_key)) transfer_condition_uri = alice_transfer_ed25519.condition.serialize_uri() # Assemble output and input for the handcrafted tx transfer_output = { 'amount': '1', 'condition': { 'details': { 'type': alice_transfer_ed25519.TYPE_NAME, 'public_key': base58.b58encode(alice_transfer_ed25519.public_key).decode() }, 'uri': transfer_condition_uri, }, 'public_keys': (alice.public_key,), } # The yet to be fulfilled input: transfer_input_ = { 'fulfillment': None, 'fulfills': { 'transaction_id': creation_tx_id, 'output_index': 0 }, 'owners_before': (alice.public_key, bob.public_key, carol.public_key), } # Assemble the handcrafted transaction handcrafted_transfer_tx = { 'operation': 'TRANSFER', 'asset': {'id': creation_tx_id}, 'metadata': None, 'outputs': (transfer_output,), 'inputs': (transfer_input_,), 'version': '2.0', 'id': None, } # Create sha3-256 of message to sign message = json.dumps( handcrafted_transfer_tx, sort_keys=True, separators=(',', ':'), ensure_ascii=False, ) message = sha3.sha3_256(message.encode()) message.update('{}{}'.format( handcrafted_transfer_tx['inputs'][0]['fulfills']['transaction_id'], handcrafted_transfer_tx['inputs'][0]['fulfills']['output_index']).encode()) # Sign message with Alice's und Bob's private key bob_transfer_ed25519.sign(message.digest(), base58.b58decode(bob.private_key)) carol_transfer_ed25519.sign(message.digest(), base58.b58decode(carol.private_key)) sub_fulfillment_threshold = ThresholdSha256(2) sub_fulfillment_threshold.add_subfulfillment(bob_transfer_ed25519) sub_fulfillment_threshold.add_subfulfillment(carol_transfer_ed25519) # Create fulfillment and add uri to inputs fulfillment_threshold = ThresholdSha256(1) fulfillment_threshold.add_subcondition(alice_transfer_ed25519.condition) fulfillment_threshold.add_subfulfillment(sub_fulfillment_threshold) fulfillment_uri = fulfillment_threshold.serialize_uri() handcrafted_transfer_tx['inputs'][0]['fulfillment'] = fulfillment_uri # Create tx_id for handcrafted_dw_tx and send tx commit json_str_tx = json.dumps( handcrafted_transfer_tx, sort_keys=True, separators=(',', ':'), ensure_ascii=False, ) transfer_tx_id = sha3.sha3_256(json_str_tx.encode()).hexdigest() handcrafted_transfer_tx['id'] = transfer_tx_id pm.transactions.send_commit(handcrafted_transfer_tx) time.sleep(1) # Assert that the tx is propagated to all nodes hosts.assert_transaction(transfer_tx_id)