# 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 # # Basic Acceptance Test # Here we check that the primitives of the system behave as expected. # As you will see, this script tests basic stuff like: # # - create a transaction # - check if the transaction is stored # - check for the outputs of a given public key # - transfer the transaction to another key # # We run a series of checks for each steps, that is retrieving the transaction from # the remote system, and also checking the `outputs` of a given public key. # ## Imports # We need some utils from the `os` package, we will interact with # env variables. import os # For this test case we import and use the Python Driver. from planetmint_driver import Planetmint from planetmint_driver.crypto import generate_keypair from ipld import multihash, marshal def test_get_tests(): # ## Set up a connection to Planetmint # To use BighainDB we need a connection. Here we create one. By default we # connect to localhost, but you can override this value using the env variable # called `PLANETMINT_ENDPOINT`, a valid value must include the schema: # `https://example.com:9984` bdb = Planetmint(os.environ.get("PLANETMINT_ENDPOINT")) # ## Create keypairs # This test requires the interaction between two actors with their own keypair. # The two keypairs will be called—drum roll—Alice and Bob. alice, bob = generate_keypair(), generate_keypair() # ## Alice registers her bike in Planetmint # Alice has a nice bike, and here she creates the "digital twin" # of her bike. bike = {"data": multihash(marshal({"bicycle": {"serial_number": 420420}}))} # She prepares a `CREATE` transaction... prepared_creation_tx = bdb.transactions.prepare(operation="CREATE", signers=alice.public_key, asset=bike) # ... and she fulfills it with her private key. fulfilled_creation_tx = bdb.transactions.fulfill(prepared_creation_tx, private_keys=alice.private_key) # We will use the `id` of this transaction several time, so we store it in # a variable with a short and easy name bike_id = fulfilled_creation_tx["id"] # Now she is ready to send it to the Planetmint Network. sent_transfer_tx = bdb.transactions.send_commit(fulfilled_creation_tx) # And just to be 100% sure, she also checks if she can retrieve # it from the Planetmint node. assert bdb.transactions.retrieve(bike_id), "Cannot find transaction {}".format(bike_id) # Alice is now the proud owner of one unspent asset. assert len(bdb.outputs.get(alice.public_key, spent=False)) == 1 assert bdb.outputs.get(alice.public_key)[0]["transaction_id"] == bike_id # ## Alice transfers her bike to Bob # After registering her bike, Alice is ready to transfer it to Bob. # She needs to create a new `TRANSFER` transaction. # A `TRANSFER` transaction contains a pointer to the original asset. The original asset # is identified by the `id` of the `CREATE` transaction that defined it. transfer_asset = {"id": bike_id} # Alice wants to spend the one and only output available, the one with index `0`. output_index = 0 output = fulfilled_creation_tx["outputs"][output_index] # Here, she defines the `input` of the `TRANSFER` transaction. The `input` contains # several keys: # # - `fulfillment`, taken from the previous `CREATE` transaction. # - `fulfills`, that specifies which condition she is fulfilling. # - `owners_before`. transfer_input = { "fulfillment": output["condition"]["details"], "fulfills": {"output_index": output_index, "transaction_id": fulfilled_creation_tx["id"]}, "owners_before": output["public_keys"], } # Now that all the elements are set, she creates the actual transaction... prepared_transfer_tx = bdb.transactions.prepare( operation="TRANSFER", asset=transfer_asset, inputs=transfer_input, recipients=bob.public_key ) # ... and signs it with her private key. fulfilled_transfer_tx = bdb.transactions.fulfill(prepared_transfer_tx, private_keys=alice.private_key) # She finally sends the transaction to a Planetmint node. sent_transfer_tx = bdb.transactions.send_commit(fulfilled_transfer_tx) # And just to be 100% sure, she also checks if she can retrieve # it from the Planetmint node. assert bdb.transactions.retrieve(fulfilled_transfer_tx["id"]) == sent_transfer_tx # Now Alice has zero unspent transactions. assert len(bdb.outputs.get(alice.public_key, spent=False)) == 0 # While Bob has one.copy assert len(bdb.outputs.get(bob.public_key, spent=False)) == 1 # Bob double checks what he got was the actual bike. bob_tx_id = bdb.outputs.get(bob.public_key, spent=False)[0]["transaction_id"] assert bdb.transactions.retrieve(bob_tx_id) == sent_transfer_tx transfer_asset = {"id": bike_id} # Alice wants to spend the one and only output available, the one with index `0`. output_index = 0 output = fulfilled_transfer_tx["outputs"][output_index] # Here, she defines the `input` of the `TRANSFER` transaction. The `input` contains # several keys: # # - `fulfillment`, taken from the previous `CREATE` transaction. # - `fulfills`, that specifies which condition she is fulfilling. # - `owners_before`. transfer_input = { "fulfillment": output["condition"]["details"], "fulfills": {"output_index": output_index, "transaction_id": fulfilled_transfer_tx["id"]}, "owners_before": output["public_keys"], } # Now that all the elements are set, she creates the actual transaction... prepared_transfer_tx = bdb.transactions.prepare( operation="TRANSFER", asset=transfer_asset, inputs=transfer_input, recipients=bob.public_key ) # ... and signs it with her private key. fulfilled_transfer_tx = bdb.transactions.fulfill(prepared_transfer_tx, private_keys=bob.private_key) # She finally sends the transaction to a Planetmint node. sent_transfer_tx = bdb.transactions.send_commit(fulfilled_transfer_tx) assert bdb.transactions.retrieve(fulfilled_transfer_tx["id"]) == sent_transfer_tx # from urllib3 import request import urllib3 import json http = urllib3.PoolManager() # verify that 3 transactions contain the asset_id asset_id = bike_id url = "http://planetmint:9984/api/v1/transactions?asset_id=" + asset_id r = http.request("GET", url) tmp_json = http.request("GET", url) tmp_json = json.loads(tmp_json.data.decode("utf-8")) assert len(tmp_json) == 3 # verify that one transaction is the create TX url = "http://planetmint:9984/api/v1/transactions?asset_id=" + asset_id + "&operation=CREATE" r = http.request("GET", url) tmp_json = http.request("GET", url) tmp_json = json.loads(tmp_json.data.decode("utf-8")) assert len(tmp_json) == 1 # verify that 2 transactoins are of type transfer url = "http://planetmint:9984/api/v1/transactions?asset_id=" + asset_id + "&operation=transfer" r = http.request("GET", url) tmp_json = http.request("GET", url) tmp_json = json.loads(tmp_json.data.decode("utf-8")) assert len(tmp_json) == 2