removed integration tests from the repo

Signed-off-by: Jürgen Eckel <juergen@riddleandcode.com>
This commit is contained in:
Jürgen Eckel 2023-02-22 13:44:39 +01:00
parent 1fc306e09d
commit 93be1e788a
No known key found for this signature in database
31 changed files with 1 additions and 2054 deletions

View File

@ -1,19 +0,0 @@
# 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
name: Integration tests
on: [push, pull_request]
jobs:
test:
if: ${{ false }
runs-on: ubuntu-latest
steps:
- name: Check out repository code
uses: actions/checkout@v3
- name: Start test run
run: docker-compose -f docker-compose.integration.yml up test

View File

@ -1,4 +1,4 @@
.PHONY: help run start stop logs lint test test-unit test-unit-watch test-integration cov docs clean reset release dist check-deps clean-build clean-pyc clean-test
.PHONY: help run start stop logs lint test test-unit test-unit-watch cov docs clean reset release dist check-deps clean-build clean-pyc clean-test
.DEFAULT_GOAL := help
@ -72,16 +72,9 @@ test-unit: check-deps ## Run all tests once or specify a file/test with TEST=tes
poetry run pytest -m abci
@$(DC) down
test-unit-watch: check-deps ## Run all tests and wait. Every time you change code, tests will be run again
@$(DC) run --rm --no-deps planetmint pytest -f
test-integration: check-deps ## Run all integration tests
@./scripts/run-integration-test.sh
cov: check-deps ## Check code coverage and open the result in the browser
@$(DC) run --rm planetmint pytest -v --cov=planetmint --cov-report html

View File

@ -1,53 +0,0 @@
# 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
version: '2.2'
services:
clean-shared:
image: alpine
command: ["/scripts/clean-shared.sh"]
volumes:
- ./integration/scripts/clean-shared.sh:/scripts/clean-shared.sh
- shared:/shared
planetmint-all-in-one:
build:
context: .
dockerfile: Dockerfile-all-in-one
depends_on:
- clean-shared
expose:
- "22"
- "9984"
- "9985"
- "26656"
- "26657"
- "26658"
command: ["/usr/src/app/scripts/pre-config-planetmint.sh", "/usr/src/app/scripts/all-in-one.bash"]
environment:
SCALE: ${SCALE:-4}
volumes:
- ./integration/scripts:/usr/src/app/scripts
- shared:/shared
scale: ${SCALE:-4}
test:
build:
context: .
dockerfile: integration/python/Dockerfile
depends_on:
- planetmint-all-in-one
command: ["/scripts/pre-config-test.sh", "/scripts/wait-for-planetmint.sh", "/scripts/test.sh", "pytest", "/src"]
environment:
SCALE: ${SCALE:-4}
volumes:
- ./integration/python/src:/src
- ./integration/scripts:/scripts
- ./integration/cli:/tests
- shared:/shared
volumes:
shared:

View File

@ -1,23 +0,0 @@
<!---
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
--->
# Integration test suite
This directory contains the integration test suite for Planetmint.
The suite uses Docker Compose to spin up multiple Planetmint nodes, run tests with `pytest` as well as cli tests and teardown.
## Running the tests
Run `make test-integration` in the project root directory.
By default the integration test suite spins up four planetmint nodes. If you desire to run a different configuration you can pass `SCALE=<number of nodes>` as an environmental variable.
## Writing and documenting the tests
Tests are sometimes difficult to read. For integration tests, we try to be really explicit on what the test is doing, so please write code that is *simple* and easy to understand. We decided to use literate-programming documentation. To generate the documentation for python tests run:
```bash
make docs-integration
```

View File

@ -1,47 +0,0 @@
#!/bin/bash
# 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
# Add chain migration test
check_status () {
status=$(ssh -o "StrictHostKeyChecking=no" -i \~/.ssh/id_rsa root@$1 'bash -s' < scripts/election.sh show_election $2 | tail -n 1)
status=${status#*=}
if [ $status != $3 ]; then
exit 1
fi
}
# Read host names from shared
readarray -t HOSTNAMES < /shared/hostnames
# Split into proposer and approvers
PROPOSER=${HOSTNAMES[0]}
APPROVERS=${HOSTNAMES[@]:1}
# Propose chain migration
result=$(ssh -o "StrictHostKeyChecking=no" -i \~/.ssh/id_rsa root@${PROPOSER} 'bash -s' < scripts/election.sh migrate)
# Check if election is ongoing and approve chain migration
for APPROVER in ${APPROVERS[@]}; do
# Check if election is still ongoing
check_status ${APPROVER} $result ongoing
ssh -o "StrictHostKeyChecking=no" -i ~/.ssh/id_rsa root@${APPROVER} 'bash -s' < scripts/election.sh approve $result
done
# Status of election should be concluded
status=$(ssh -o "StrictHostKeyChecking=no" -i \~/.ssh/id_rsa root@${PROPOSER} 'bash -s' < scripts/election.sh show_election $result)
status=${status#*INFO:planetmint.commands.planetmint:}
status=("$status[@]")
# TODO: Get status, chain_id, app_hash and validators to restore planetmint on all nodes
# References:
# https://github.com/bigchaindb/BEPs/tree/master/42
# http://docs.bigchaindb.com/en/latest/installation/node-setup/bigchaindb-cli.html
for word in $status; do
echo $word
done
echo ${status#*validators=}

View File

@ -1,33 +0,0 @@
#!/bin/bash
# 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
check_status () {
status=$(ssh -o "StrictHostKeyChecking=no" -i \~/.ssh/id_rsa root@$1 'bash -s' < scripts/election.sh show_election $2 | tail -n 1)
status=${status#*=}
if [ $status != $3 ]; then
exit 1
fi
}
# Read host names from shared
readarray -t HOSTNAMES < /shared/hostnames
# Split into proposer and approvers
PROPOSER=${HOSTNAMES[0]}
APPROVERS=${HOSTNAMES[@]:1}
# Propose validator upsert
result=$(ssh -o "StrictHostKeyChecking=no" -i \~/.ssh/id_rsa root@${PROPOSER} 'bash -s' < scripts/election.sh elect 2)
# Check if election is ongoing and approve validator upsert
for APPROVER in ${APPROVERS[@]}; do
# Check if election is still ongoing
check_status ${APPROVER} $result ongoing
ssh -o "StrictHostKeyChecking=no" -i ~/.ssh/id_rsa root@${APPROVER} 'bash -s' < scripts/election.sh approve $result
done
# Status of election should be concluded
check_status ${PROPOSER} $result concluded

View File

@ -1 +0,0 @@
docs

View File

@ -1,19 +0,0 @@
FROM python:3.9
RUN apt-get update \
&& pip install -U pip \
&& apt-get autoremove \
&& apt-get clean
RUN apt-get install -y vim
RUN apt-get update
RUN apt-get install -y build-essential cmake openssh-client openssh-server git
RUN apt-get install -y zsh
RUN mkdir -p /src
RUN pip install --upgrade meson ninja
RUN pip install --upgrade \
pytest~=6.2.5 \
pycco \
websocket-client~=0.47.0 \
planetmint-driver>=9.2.0 \
blns

View File

@ -1,86 +0,0 @@
# 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
import pytest
CONDITION_SCRIPT = """Scenario 'ecdh': create the signature of an object
Given I have the 'keyring'
Given that I have a 'string dictionary' named 'houses'
When I create the signature of 'houses'
Then print the 'signature'"""
FULFILL_SCRIPT = """Scenario 'ecdh': Bob verifies the signature from Alice
Given I have a 'ecdh public key' from 'Alice'
Given that I have a 'string dictionary' named 'houses'
Given I have a 'signature' named 'signature'
When I verify the 'houses' has a signature in 'signature' by 'Alice'
Then print the string 'ok'"""
SK_TO_PK = """Scenario 'ecdh': Create the keypair
Given that I am known as '{}'
Given I have the 'keyring'
When I create the ecdh public key
When I create the bitcoin address
Then print my 'ecdh public key'
Then print my 'bitcoin address'"""
GENERATE_KEYPAIR = """Scenario 'ecdh': Create the keypair
Given that I am known as 'Pippo'
When I create the ecdh key
When I create the bitcoin key
Then print data"""
INITIAL_STATE = {"also": "more data"}
SCRIPT_INPUT = {
"houses": [
{
"name": "Harry",
"team": "Gryffindor",
},
{
"name": "Draco",
"team": "Slytherin",
},
],
}
metadata = {"units": 300, "type": "KG"}
ZENROOM_DATA = {"that": "is my data"}
@pytest.fixture
def gen_key_zencode():
return GENERATE_KEYPAIR
@pytest.fixture
def secret_key_to_private_key_zencode():
return SK_TO_PK
@pytest.fixture
def fulfill_script_zencode():
return FULFILL_SCRIPT
@pytest.fixture
def condition_script_zencode():
return CONDITION_SCRIPT
@pytest.fixture
def zenroom_house_assets():
return SCRIPT_INPUT
@pytest.fixture
def zenroom_script_input():
return SCRIPT_INPUT
@pytest.fixture
def zenroom_data():
return ZENROOM_DATA

View File

@ -1,35 +0,0 @@
# 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
from typing import List
from planetmint_driver import Planetmint
class Hosts:
hostnames = []
connections = []
def __init__(self, filepath):
self.set_hostnames(filepath=filepath)
self.set_connections()
def set_hostnames(self, filepath) -> None:
with open(filepath) as f:
self.hostnames = f.readlines()
def set_connections(self) -> None:
self.connections = list(map(lambda h: Planetmint(h), self.hostnames))
def get_connection(self, index=0) -> Planetmint:
return self.connections[index]
def get_transactions(self, tx_id) -> List:
return list(map(lambda connection: connection.transactions.retrieve(tx_id), self.connections))
def assert_transaction(self, tx_id) -> None:
txs = self.get_transactions(tx_id)
for tx in txs:
assert txs[0] == tx, "Cannot find transaction {}".format(tx_id)

View File

@ -1,87 +0,0 @@
# 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
# import Planetmint and create object
from planetmint_driver.crypto import generate_keypair
# import helper to manage multiple nodes
from .helper.hosts import Hosts
import time
def test_basic():
# Setup up connection to Planetmint integration test nodes
hosts = Hosts("/shared/hostnames")
pm_alpha = hosts.get_connection()
# genarate a keypair
alice = generate_keypair()
# create a digital asset for Alice
game_boy_token = [
{
"data": {
"hash": "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",
"storageID": "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",
},
}
]
# prepare the transaction with the digital asset and issue 10 tokens to bob
prepared_creation_tx = pm_alpha.transactions.prepare(
operation="CREATE",
metadata={
"hash": "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",
"storageID": "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",
},
signers=alice.public_key,
recipients=[([alice.public_key], 10)],
assets=game_boy_token,
)
# fulfill and send the transaction
fulfilled_creation_tx = pm_alpha.transactions.fulfill(prepared_creation_tx, private_keys=alice.private_key)
pm_alpha.transactions.send_commit(fulfilled_creation_tx)
time.sleep(1)
creation_tx_id = fulfilled_creation_tx["id"]
# Assert that transaction is stored on all planetmint nodes
hosts.assert_transaction(creation_tx_id)
# Transfer
# create the output and inout for the transaction
transfer_assets = [{"id": creation_tx_id}]
output_index = 0
output = fulfilled_creation_tx["outputs"][output_index]
transfer_input = {
"fulfillment": output["condition"]["details"],
"fulfills": {"output_index": output_index, "transaction_id": transfer_assets[0]["id"]},
"owners_before": output["public_keys"],
}
# prepare the transaction and use 3 tokens
prepared_transfer_tx = pm_alpha.transactions.prepare(
operation="TRANSFER",
asset=transfer_assets,
inputs=transfer_input,
metadata={
"hash": "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",
"storageID": "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",
},
recipients=[([alice.public_key], 10)],
)
# fulfill and send the transaction
fulfilled_transfer_tx = pm_alpha.transactions.fulfill(prepared_transfer_tx, private_keys=alice.private_key)
sent_transfer_tx = pm_alpha.transactions.send_commit(fulfilled_transfer_tx)
time.sleep(1)
transfer_tx_id = sent_transfer_tx["id"]
# Assert that transaction is stored on both planetmint nodes
hosts.assert_transaction(transfer_tx_id)

View File

@ -1,167 +0,0 @@
# 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
# # Divisible assets integration testing
# This test checks if we can successfully divide assets.
# The script tests various things like:
#
# - create a transaction with a divisible asset and issue them to someone
# - check if the transaction is stored and has the right amount of tokens
# - spend some tokens
# - try to spend more tokens than available
#
# We run a series of checks for each step, that is retrieving
# the transaction from the remote system, and also checking the `amount`
# of a given transaction.
# ## Imports
# We need the `pytest` package to catch the `BadRequest` exception properly.
# And of course, we also need the `BadRequest`.
import pytest
from planetmint_driver.exceptions import BadRequest
# Import generate_keypair to create actors
from planetmint_driver.crypto import generate_keypair
# import helper to manage multiple nodes
from .helper.hosts import Hosts
def test_divisible_assets():
# ## Set up a connection to Planetmint
# Check [test_basic.py](./test_basic.html) to get some more details
# about the endpoint.
hosts = Hosts("/shared/hostnames")
pm = hosts.get_connection()
# Oh look, it is Alice again and she brought her friend Bob along.
alice, bob = generate_keypair(), generate_keypair()
# ## Alice creates a time sharing token
# Alice wants to go on vacation, while Bobs bike just broke down.
# Alice decides to rent her bike to Bob while she is gone.
# So she prepares a `CREATE` transaction to issues 10 tokens.
# First, she prepares an asset for a time sharing token. As you can see in
# the description, Bob and Alice agree that each token can be used to ride
# the bike for one hour.
bike_token = [
{
"data": {
"token_for": {"bike": {"serial_number": 420420}},
"description": "Time share token. Each token equals one hour of riding.",
},
}
]
# She prepares a `CREATE` transaction and issues 10 tokens.
# Here, Alice defines in a tuple that she wants to assign
# these 10 tokens to Bob.
prepared_token_tx = pm.transactions.prepare(
operation="CREATE", signers=alice.public_key, recipients=[([bob.public_key], 10)], assets=bike_token
)
# She fulfills and sends the transaction.
fulfilled_token_tx = pm.transactions.fulfill(prepared_token_tx, private_keys=alice.private_key)
pm.transactions.send_commit(fulfilled_token_tx)
# We store the `id` of the transaction to use it later on.
bike_token_id = fulfilled_token_tx["id"]
# Let's check if the transaction was successful.
assert pm.transactions.retrieve(bike_token_id), "Cannot find transaction {}".format(bike_token_id)
# Bob owns 10 tokens now.
assert pm.transactions.retrieve(bike_token_id)["outputs"][0]["amount"] == "10"
# ## Bob wants to use the bike
# Now that Bob got the tokens and the sun is shining, he wants to get out
# with the bike for three hours.
# To use the bike he has to send the tokens back to Alice.
# To learn about the details of transferring a transaction check out
# [test_basic.py](./test_basic.html)
transfer_assets = [{"id": bike_token_id}]
output_index = 0
output = fulfilled_token_tx["outputs"][output_index]
transfer_input = {
"fulfillment": output["condition"]["details"],
"fulfills": {"output_index": output_index, "transaction_id": fulfilled_token_tx["id"]},
"owners_before": output["public_keys"],
}
# To use the tokens Bob has to reassign 7 tokens to himself and the
# amount he wants to use to Alice.
prepared_transfer_tx = pm.transactions.prepare(
operation="TRANSFER",
asset=transfer_assets,
inputs=transfer_input,
recipients=[([alice.public_key], 3), ([bob.public_key], 7)],
)
# He signs and sends the transaction.
fulfilled_transfer_tx = pm.transactions.fulfill(prepared_transfer_tx, private_keys=bob.private_key)
sent_transfer_tx = pm.transactions.send_commit(fulfilled_transfer_tx)
# First, Bob checks if the transaction was successful.
assert pm.transactions.retrieve(fulfilled_transfer_tx["id"]) == sent_transfer_tx
hosts.assert_transaction(fulfilled_transfer_tx["id"])
# There are two outputs in the transaction now.
# The first output shows that Alice got back 3 tokens...
assert pm.transactions.retrieve(fulfilled_transfer_tx["id"])["outputs"][0]["amount"] == "3"
# ... while Bob still has 7 left.
assert pm.transactions.retrieve(fulfilled_transfer_tx["id"])["outputs"][1]["amount"] == "7"
# ## Bob wants to ride the bike again
# It's been a week and Bob wants to right the bike again.
# Now he wants to ride for 8 hours, that's a lot Bob!
# He prepares the transaction again.
transfer_assets = [{"id": bike_token_id}]
# This time we need an `output_index` of 1, since we have two outputs
# in the `fulfilled_transfer_tx` we created before. The first output with
# index 0 is for Alice and the second output is for Bob.
# Since Bob wants to spend more of his tokens he has to provide the
# correct output with the correct amount of tokens.
output_index = 1
output = fulfilled_transfer_tx["outputs"][output_index]
transfer_input = {
"fulfillment": output["condition"]["details"],
"fulfills": {"output_index": output_index, "transaction_id": fulfilled_transfer_tx["id"]},
"owners_before": output["public_keys"],
}
# This time Bob only provides Alice in the `recipients` because he wants
# to spend all his tokens
prepared_transfer_tx = pm.transactions.prepare(
operation="TRANSFER", assets=transfer_assets, inputs=transfer_input, recipients=[([alice.public_key], 8)]
)
fulfilled_transfer_tx = pm.transactions.fulfill(prepared_transfer_tx, private_keys=bob.private_key)
# Oh Bob, what have you done?! You tried to spend more tokens than you had.
# Remember Bob, last time you spent 3 tokens already,
# so you only have 7 left.
with pytest.raises(BadRequest) as error:
pm.transactions.send_commit(fulfilled_transfer_tx)
# Now Bob gets an error saying that the amount he wanted to spent is
# higher than the amount of tokens he has left.
assert error.value.args[0] == 400
message = (
"Invalid transaction (AmountError): The amount used in the "
"inputs `7` needs to be same as the amount used in the "
"outputs `8`"
)
assert error.value.args[2]["message"] == message
# We have to stop this test now, I am sorry, but Bob is pretty upset
# about his mistake. See you next time :)

View File

@ -1,48 +0,0 @@
# 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
# # Double Spend testing
# This test challenge the system with double spends.
from uuid import uuid4
from threading import Thread
import queue
import planetmint_driver.exceptions
from planetmint_driver.crypto import generate_keypair
from .helper.hosts import Hosts
def test_double_create():
hosts = Hosts("/shared/hostnames")
pm = hosts.get_connection()
alice = generate_keypair()
results = queue.Queue()
tx = pm.transactions.fulfill(
pm.transactions.prepare(
operation="CREATE", signers=alice.public_key, assets=[{"data": {"uuid": str(uuid4())}}]
),
private_keys=alice.private_key,
)
def send_and_queue(tx):
try:
pm.transactions.send_commit(tx)
results.put("OK")
except planetmint_driver.exceptions.TransportError:
results.put("FAIL")
t1 = Thread(target=send_and_queue, args=(tx,))
t2 = Thread(target=send_and_queue, args=(tx,))
t1.start()
t2.start()
results = [results.get(timeout=2), results.get(timeout=2)]
assert results.count("OK") == 1
assert results.count("FAIL") == 1

View File

@ -1,115 +0,0 @@
# 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
# # Multisignature integration testing
# This test checks if we can successfully create and transfer a transaction
# with multiple owners.
# The script tests various things like:
#
# - create a transaction with multiple owners
# - check if the transaction is stored and has the right amount of public keys
# - transfer the transaction to a third person
#
# We run a series of checks for each step, that is retrieving
# the transaction from the remote system, and also checking the public keys
# of a given transaction.
# # Imports
import time
# For this test case we need import and use the Python driver
from planetmint_driver.crypto import generate_keypair
# Import helper to deal with multiple nodes
from .helper.hosts import Hosts
def test_multiple_owners():
# Setup up connection to Planetmint integration test nodes
hosts = Hosts("/shared/hostnames")
pm_alpha = hosts.get_connection()
# Generate Keypairs for Alice and Bob!
alice, bob = 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}}}]
# They prepare a `CREATE` transaction. To have multiple owners, both
# Bob and Alice need to be the recipients.
prepared_dw_tx = pm_alpha.transactions.prepare(
operation="CREATE", signers=alice.public_key, recipients=(alice.public_key, bob.public_key), assets=dw_asset
)
# Now they both sign the transaction by providing their private keys.
# And send it afterwards.
fulfilled_dw_tx = pm_alpha.transactions.fulfill(prepared_dw_tx, private_keys=[alice.private_key, bob.private_key])
pm_alpha.transactions.send_commit(fulfilled_dw_tx)
# We store the `id` of the transaction to use it later on.
dw_id = fulfilled_dw_tx["id"]
time.sleep(1)
# Use hosts to assert that the transaction is properly propagated to every node
hosts.assert_transaction(dw_id)
# Let's check if the transaction was successful.
assert pm_alpha.transactions.retrieve(dw_id), "Cannot find transaction {}".format(dw_id)
# The transaction should have two public keys in the outputs.
assert len(pm_alpha.transactions.retrieve(dw_id)["outputs"][0]["public_keys"]) == 2
# ## Alice and Bob transfer a transaction to Carol.
# Alice and Bob save a lot of money living together. They often go out
# for dinner and don't cook at home. But now they don't have any dishes to
# wash, so they decide to sell the dish washer to their friend Carol.
# Hey Carol, nice to meet you!
carol = generate_keypair()
# Alice and Bob prepare the transaction to transfer the dish washer to
# Carol.
transfer_assets = [{"id": dw_id}]
output_index = 0
output = fulfilled_dw_tx["outputs"][output_index]
transfer_input = {
"fulfillment": output["condition"]["details"],
"fulfills": {"output_index": output_index, "transaction_id": fulfilled_dw_tx["id"]},
"owners_before": output["public_keys"],
}
# Now they create the transaction...
prepared_transfer_tx = pm_alpha.transactions.prepare(
operation="TRANSFER", assets=transfer_assets, inputs=transfer_input, recipients=carol.public_key
)
# ... and sign it with their private keys, then send it.
fulfilled_transfer_tx = pm_alpha.transactions.fulfill(
prepared_transfer_tx, private_keys=[alice.private_key, bob.private_key]
)
sent_transfer_tx = pm_alpha.transactions.send_commit(fulfilled_transfer_tx)
time.sleep(1)
# Now compare if both nodes returned the same transaction
hosts.assert_transaction(fulfilled_transfer_tx["id"])
# They check if the transaction was successful.
assert pm_alpha.transactions.retrieve(fulfilled_transfer_tx["id"]) == sent_transfer_tx
# The owners before should include both Alice and Bob.
assert len(pm_alpha.transactions.retrieve(fulfilled_transfer_tx["id"])["inputs"][0]["owners_before"]) == 2
# While the new owner is Carol.
assert (
pm_alpha.transactions.retrieve(fulfilled_transfer_tx["id"])["outputs"][0]["public_keys"][0] == carol.public_key
)

View File

@ -1,129 +0,0 @@
# 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
# ## Testing potentially hazardous strings
# This test uses a library of `naughty` strings (code injections, weird unicode chars., etc.) as both keys and values.
# We look for either a successful tx, or in the case that we use a naughty string as a key, and it violates some key
# constraints, we expect to receive a well formatted error message.
# ## Imports
# Since the naughty strings get encoded and decoded in odd ways,
# we'll use a regex to sweep those details under the rug.
import re
# We'll use a nice library of naughty strings...
from blns import blns
# And parameterize our test so each one is treated as a separate test case
import pytest
# For this test case we import and use the Python Driver.
from planetmint_driver.crypto import generate_keypair
from planetmint_driver.exceptions import BadRequest
# import helper to manage multiple nodes
from .helper.hosts import Hosts
naughty_strings = blns.all()
skipped_naughty_strings = [
"1.00",
"$1.00",
"-1.00",
"-$1.00",
"0.00",
"0..0",
".",
"0.0.0",
"-.",
",./;'[]\\-=",
"ثم نفس سقطت وبالتحديد،, جزيرتي باستخدام أن دنو. إذ هنا؟ الستار وتنصيب كان. أهّل ايطاليا، بريطانيا-فرنسا قد أخذ. سليمان، إتفاقية بين ما, يذكر الحدود أي بعد, معاملة بولندا، الإطلاق عل إيو.",
"test\x00",
"Ṱ̺̺̕o͞ ̷i̲̬͇̪͙n̝̗͕v̟̜̘̦͟o̶̙̰̠kè͚̮̺̪̹̱̤ ̖t̝͕̳̣̻̪͞h̼͓̲̦̳̘̲e͇̣̰̦̬͎ ̢̼̻̱̘h͚͎͙̜̣̲ͅi̦̲̣̰̤v̻͍e̺̭̳̪̰-m̢iͅn̖̺̞̲̯̰d̵̼̟͙̩̼̘̳ ̞̥̱̳̭r̛̗̘e͙p͠r̼̞̻̭̗e̺̠̣͟s̘͇̳͍̝͉e͉̥̯̞̲͚̬͜ǹ̬͎͎̟̖͇̤t͍̬̤͓̼̭͘ͅi̪̱n͠g̴͉ ͏͉ͅc̬̟h͡a̫̻̯͘o̫̟̖͍̙̝͉s̗̦̲.̨̹͈̣",
"̡͓̞ͅI̗̘̦͝n͇͇͙v̮̫ok̲̫̙͈i̖͙̭̹̠̞n̡̻̮̣̺g̲͈͙̭͙̬͎ ̰t͔̦h̞̲e̢̤ ͍̬̲͖f̴̘͕̣è͖ẹ̥̩l͖͔͚i͓͚̦͠n͖͍̗͓̳̮g͍ ̨o͚̪͡f̘̣̬ ̖̘͖̟͙̮c҉͔̫͖͓͇͖ͅh̵̤̣͚͔á̗̼͕ͅo̼̣̥s̱͈̺̖̦̻͢.̛̖̞̠̫̰",
"̗̺͖̹̯͓Ṯ̤͍̥͇͈h̲́e͏͓̼̗̙̼̣͔ ͇̜̱̠͓͍ͅN͕͠e̗̱z̘̝̜̺͙p̤̺̹͍̯͚e̠̻̠͜r̨̤͍̺̖͔̖̖d̠̟̭̬̝͟i̦͖̩͓͔̤a̠̗̬͉̙n͚͜ ̻̞̰͚ͅh̵͉i̳̞v̢͇ḙ͎͟-҉̭̩̼͔m̤̭̫i͕͇̝̦n̗͙ḍ̟ ̯̲͕͞ǫ̟̯̰̲͙̻̝f ̪̰̰̗̖̭̘͘c̦͍̲̞͍̩̙ḥ͚a̮͎̟̙͜ơ̩̹͎s̤.̝̝ ҉Z̡̖̜͖̰̣͉̜a͖̰͙̬͡l̲̫̳͍̩g̡̟̼̱͚̞̬ͅo̗͜.̟",
"̦H̬̤̗̤͝e͜ ̜̥̝̻͍̟́w̕h̖̯͓o̝͙̖͎̱̮ ҉̺̙̞̟͈W̷̼̭a̺̪͍į͈͕̭͙̯̜t̶̼̮s̘͙͖̕ ̠̫̠B̻͍͙͉̳ͅe̵h̵̬͇̫͙i̹͓̳̳̮͎̫̕n͟d̴̪̜̖ ̰͉̩͇͙̲͞ͅT͖̼͓̪͢h͏͓̮̻e̬̝̟ͅ ̤̹̝W͙̞̝͔͇͝ͅa͏͓͔̹̼̣l̴͔̰̤̟͔ḽ̫.͕",
'"><script>alert(document.title)</script>',
"'><script>alert(document.title)</script>",
"><script>alert(document.title)</script>",
"</script><script>alert(document.title)</script>",
"< / script >< script >alert(document.title)< / script >",
" onfocus=alert(document.title) autofocus ",
'" onfocus=alert(document.title) autofocus ',
"' onfocus=alert(document.title) autofocus ",
"scriptalert(document.title)/script",
"/dev/null; touch /tmp/blns.fail ; echo",
"../../../../../../../../../../../etc/passwd%00",
"../../../../../../../../../../../etc/hosts",
"() { 0; }; touch /tmp/blns.shellshock1.fail;",
"() { _; } >_[$($())] { touch /tmp/blns.shellshock2.fail; }",
]
naughty_strings = [naughty for naughty in naughty_strings if naughty not in skipped_naughty_strings]
# This is our base test case, but we'll reuse it to send naughty strings as both keys and values.
def send_naughty_tx(assets, metadata):
# ## Set up a connection to Planetmint
# Check [test_basic.py](./test_basic.html) to get some more details
# about the endpoint.
hosts = Hosts("/shared/hostnames")
pm = hosts.get_connection()
# Here's Alice.
alice = generate_keypair()
# Alice is in a naughty mood today, so she creates a tx with some naughty strings
prepared_transaction = pm.transactions.prepare(
operation="CREATE", signers=alice.public_key, assets=assets, metadata=metadata
)
# She fulfills the transaction
fulfilled_transaction = pm.transactions.fulfill(prepared_transaction, private_keys=alice.private_key)
# The fulfilled tx gets sent to the pm network
try:
sent_transaction = pm.transactions.send_commit(fulfilled_transaction)
except BadRequest as e:
sent_transaction = e
# If her key contained a '.', began with a '$', or contained a NUL character
regex = r".*\..*|\$.*|.*\x00.*"
key = next(iter(metadata))
if re.match(regex, key):
# Then she expects a nicely formatted error code
status_code = sent_transaction.status_code
error = sent_transaction.error
regex = (
r"\{\s*\n*"
r'\s*"message":\s*"Invalid transaction \(ValidationError\):\s*'
r"Invalid key name.*The key name cannot contain characters.*\n*"
r'\s*"status":\s*400\n*'
r"\s*\}\n*"
)
assert status_code == 400
assert re.fullmatch(regex, error), sent_transaction
# Otherwise, she expects to see her transaction in the database
elif "id" in sent_transaction.keys():
tx_id = sent_transaction["id"]
assert pm.transactions.retrieve(tx_id)
# If neither condition was true, then something weird happened...
else:
raise TypeError(sent_transaction)
@pytest.mark.parametrize("naughty_string", naughty_strings, ids=naughty_strings)
def test_naughty_keys(naughty_string):
assets = [{"data": {naughty_string: "nice_value"}}]
metadata = {naughty_string: "nice_value"}
send_naughty_tx(assets, metadata)
@pytest.mark.parametrize("naughty_string", naughty_strings, ids=naughty_strings)
def test_naughty_values(naughty_string):
assets = [{"data": {"nice_key": naughty_string}}]
metadata = {"nice_key": naughty_string}
send_naughty_tx(assets, metadata)

View File

@ -1,131 +0,0 @@
# 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
# # Stream Acceptance Test
# This test checks if the event stream works correctly. The basic idea of this
# test is to generate some random **valid** transaction, send them to a
# Planetmint node, and expect those transactions to be returned by the valid
# transactions Stream API. During this test, two threads work together,
# sharing a queue to exchange events.
#
# - The *main thread* first creates and sends the transactions to Planetmint;
# then it run through all events in the shared queue to check if all
# transactions sent have been validated by Planetmint.
# - The *listen thread* listens to the events coming from Planetmint and puts
# them in a queue shared with the main thread.
import queue
import json
from threading import Thread, Event
from uuid import uuid4
# For this script, we need to set up a websocket connection, that's the reason
# we import the
# [websocket](https://github.com/websocket-client/websocket-client) module
from websocket import create_connection
from planetmint_driver.crypto import generate_keypair
# import helper to manage multiple nodes
from .helper.hosts import Hosts
def test_stream():
# ## Set up the test
# We use the env variable `BICHAINDB_ENDPOINT` to know where to connect.
# Check [test_basic.py](./test_basic.html) for more information.
hosts = Hosts("/shared/hostnames")
pm = hosts.get_connection()
# *That's pretty bad, but let's do like this for now.*
WS_ENDPOINT = "ws://{}:9985/api/v1/streams/valid_transactions".format(hosts.hostnames[0])
# Hello to Alice again, she is pretty active in those tests, good job
# Alice!
alice = generate_keypair()
# We need few variables to keep the state, specifically we need `sent` to
# keep track of all transactions Alice sent to Planetmint, while `received`
# are the transactions Planetmint validated and sent back to her.
sent = []
received = queue.Queue()
# In this test we use a websocket. The websocket must be started **before**
# sending transactions to Planetmint, otherwise we might lose some
# transactions. The `ws_ready` event is used to synchronize the main thread
# with the listen thread.
ws_ready = Event()
# ## Listening to events
# This is the function run by the complementary thread.
def listen():
# First we connect to the remote endpoint using the WebSocket protocol.
ws = create_connection(WS_ENDPOINT)
# After the connection has been set up, we can signal the main thread
# to proceed (continue reading, it should make sense in a second.)
ws_ready.set()
# It's time to consume all events coming from the Planetmint stream API.
# Every time a new event is received, it is put in the queue shared
# with the main thread.
while True:
result = ws.recv()
received.put(result)
# Put `listen` in a thread, and start it. Note that `listen` is a local
# function and it can access all variables in the enclosing function.
t = Thread(target=listen, daemon=True)
t.start()
# ## Pushing the transactions to Planetmint
# After starting the listen thread, we wait for it to connect, and then we
# proceed.
ws_ready.wait()
# Here we prepare, sign, and send ten different `CREATE` transactions. To
# make sure each transaction is different from the other, we generate a
# random `uuid`.
for _ in range(10):
tx = pm.transactions.fulfill(
pm.transactions.prepare(
operation="CREATE", signers=alice.public_key, assets=[{"data": {"uuid": str(uuid4())}}]
),
private_keys=alice.private_key,
)
# We don't want to wait for each transaction to be in a block. By using
# `async` mode, we make sure that the driver returns as soon as the
# transaction is pushed to the Planetmint API. Remember: we expect all
# transactions to be in the shared queue: this is a two phase test,
# first we send a bunch of transactions, then we check if they are
# valid (and, in this case, they should).
pm.transactions.send_async(tx)
# The `id` of every sent transaction is then stored in a list.
sent.append(tx["id"])
# ## Check the valid transactions coming from Planetmint
# Now we are ready to check if Planetmint did its job. A simple way to
# check if all sent transactions have been processed is to **remove** from
# `sent` the transactions we get from the *listen thread*. At one point in
# time, `sent` should be empty, and we exit the test.
while sent:
# To avoid waiting forever, we have an arbitrary timeout of 5
# seconds: it should be enough time for Planetmint to create
# blocks, in fact a new block is created every second. If we hit
# the timeout, then game over ¯\\\_(ツ)\_/¯
try:
event = received.get(timeout=5)
txid = json.loads(event)["transaction_id"]
except queue.Empty:
assert False, "Did not receive all expected transactions"
# Last thing is to try to remove the `txid` from the set of sent
# transactions. If this test is running in parallel with others, we
# might get a transaction id of another test, and `remove` can fail.
# It's OK if this happens.
try:
sent.remove(txid)
except ValueError:
pass

View File

@ -1,319 +0,0 @@
# 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 planetmint_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()
assets = [{"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": assets,
"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",
"assets": [{"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)

View File

@ -1,130 +0,0 @@
import json
import base58
from hashlib import sha3_256
from planetmint_cryptoconditions.types.zenroom import ZenroomSha256
from planetmint_driver.crypto import generate_keypair
from .helper.hosts import Hosts
from zenroom import zencode_exec
import time
def test_zenroom_signing(
gen_key_zencode,
secret_key_to_private_key_zencode,
fulfill_script_zencode,
zenroom_data,
zenroom_house_assets,
zenroom_script_input,
condition_script_zencode,
):
biolabs = generate_keypair()
version = "2.0"
alice = json.loads(zencode_exec(gen_key_zencode).output)["keyring"]
bob = json.loads(zencode_exec(gen_key_zencode).output)["keyring"]
zen_public_keys = json.loads(
zencode_exec(secret_key_to_private_key_zencode.format("Alice"), keys=json.dumps({"keyring": alice})).output
)
zen_public_keys.update(
json.loads(
zencode_exec(secret_key_to_private_key_zencode.format("Bob"), keys=json.dumps({"keyring": bob})).output
)
)
zenroomscpt = ZenroomSha256(script=fulfill_script_zencode, data=zenroom_data, keys=zen_public_keys)
print(f"zenroom is: {zenroomscpt.script}")
# CRYPTO-CONDITIONS: generate the condition uri
condition_uri_zen = zenroomscpt.condition.serialize_uri()
print(f"\nzenroom condition URI: {condition_uri_zen}")
# CRYPTO-CONDITIONS: construct an unsigned fulfillment dictionary
unsigned_fulfillment_dict_zen = {
"type": zenroomscpt.TYPE_NAME,
"public_key": base58.b58encode(biolabs.public_key).decode(),
}
output = {
"amount": "10",
"condition": {
"details": unsigned_fulfillment_dict_zen,
"uri": condition_uri_zen,
},
"public_keys": [
biolabs.public_key,
],
}
input_ = {
"fulfillment": None,
"fulfills": None,
"owners_before": [
biolabs.public_key,
],
}
metadata = {"result": {"output": ["ok"]}}
script_ = {
"code": {"type": "zenroom", "raw": "test_string", "parameters": [{"obj": "1"}, {"obj": "2"}]},
"state": "dd8bbd234f9869cab4cc0b84aa660e9b5ef0664559b8375804ee8dce75b10576",
"input": zenroom_script_input,
"output": ["ok"],
"policies": {},
}
metadata = {"result": {"output": ["ok"]}}
token_creation_tx = {
"operation": "CREATE",
"asset": {"data": {"test": "my asset"}},
"script": script_,
"metadata": metadata,
"outputs": [
output,
],
"inputs": [
input_,
],
"version": version,
"id": None,
}
# JSON: serialize the transaction-without-id to a json formatted string
tx = json.dumps(
token_creation_tx,
sort_keys=True,
separators=(",", ":"),
ensure_ascii=False,
)
script_ = json.dumps(script_)
# major workflow:
# we store the fulfill script in the transaction/message (zenroom-sha)
# the condition script is used to fulfill the transaction and create the signature
#
# the server should ick the fulfill script and recreate the zenroom-sha and verify the signature
signed_input = zenroomscpt.sign(script_, condition_script_zencode, alice)
input_signed = json.loads(signed_input)
input_signed["input"]["signature"] = input_signed["output"]["signature"]
del input_signed["output"]["signature"]
del input_signed["output"]["logs"]
input_signed["output"] = ["ok"] # define expected output that is to be compared
input_msg = json.dumps(input_signed)
assert zenroomscpt.validate(message=input_msg)
tx = json.loads(tx)
fulfillment_uri_zen = zenroomscpt.serialize_uri()
tx["inputs"][0]["fulfillment"] = fulfillment_uri_zen
tx["script"] = input_signed
tx["id"] = None
json_str_tx = json.dumps(tx, sort_keys=True, skipkeys=False, separators=(",", ":"))
# SHA3: hash the serialized id-less transaction to generate the id
shared_creation_txid = sha3_256(json_str_tx.encode()).hexdigest()
tx["id"] = shared_creation_txid
hosts = Hosts("/shared/hostnames")
pm_alpha = hosts.get_connection()
sent_transfer_tx = pm_alpha.transactions.send_commit(tx)
time.sleep(1)
# Assert that transaction is stored on both planetmint nodes
hosts.assert_transaction(shared_creation_txid)
print(f"\n\nstatus and result : + {sent_transfer_tx}")

View File

@ -1,14 +0,0 @@
#!/bin/bash
# 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
# Planetmint configuration
/usr/src/app/scripts/planetmint-monit-config
# Tarantool startup and configuration
tarantool /usr/src/app/scripts/init.lua
# Start services
monit -d 5 -I -B

View File

@ -1,11 +0,0 @@
#!/bin/sh
# 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
rm /shared/hostnames
rm /shared/lock
rm /shared/*node_id
rm /shared/*.json
rm /shared/id_rsa.pub

View File

@ -1,81 +0,0 @@
#!/bin/bash
# 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
# Show tendermint node id
show_id () {
tendermint --home=/tendermint show_node_id | tail -n 1
}
# Show validator public key
show_validator () {
tendermint --home=/tendermint show_validator | tail -n 1
}
# Elect new voting power for node
elect_validator () {
planetmint election new upsert-validator $1 $2 $3 --private-key /tendermint/config/priv_validator_key.json 2>&1
}
# Propose new chain migration
propose_migration () {
planetmint election new chain-migration --private-key /tendermint/config/priv_validator_key.json 2>&1
}
# Show election state
show_election () {
planetmint election show $1 2>&1
}
# Approve election
approve_validator () {
planetmint election approve $1 --private-key /tendermint/config/priv_validator_key.json
}
# Fetch tendermint id and pubkey and create upsert proposal
elect () {
node_id=$(show_id)
validator_pubkey=$(show_validator | jq -r .value)
proposal=$(elect_validator $validator_pubkey $1 $node_id | grep SUCCESS)
echo ${proposal##* }
}
# Create chain migration proposal and return election id
migrate () {
proposal=$(propose_migration | grep SUCCESS)
echo ${proposal##* }
}
usage () {
echo "usage: TODO"
}
while [ "$1" != "" ]; do
case $1 in
show_id ) show_id
;;
show_validator ) show_validator
;;
elect ) shift
elect $1
;;
migrate ) shift
migrate
;;
show_election ) shift
show_election $1
;;
approve ) shift
approve_validator $1
;;
* ) usage
exit 1
esac
shift
done
exitcode=$?
exit $exitcode

View File

@ -1,33 +0,0 @@
#!/usr/bin/env python3
# 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
import json
import sys
def edit_genesis() -> None:
file_names = sys.argv[1:]
validators = []
for file_name in file_names:
file = open(file_name)
genesis = json.load(file)
validators.extend(genesis["validators"])
file.close()
genesis_file = open(file_names[0])
genesis_json = json.load(genesis_file)
genesis_json["validators"] = validators
genesis_file.close()
with open("/shared/genesis.json", "w") as f:
json.dump(genesis_json, f, indent=True)
return None
if __name__ == "__main__":
edit_genesis()

View File

@ -1,86 +0,0 @@
#!/usr/bin/env tarantool
box.cfg {
listen = 3303,
background = true,
log = '.planetmint-monit/logs/tarantool.log',
pid_file = '.planetmint-monit/monit_processes/tarantool.pid'
}
box.schema.user.grant('guest','read,write,execute,create,drop','universe')
function indexed_pattern_search(space_name, field_no, pattern)
if (box.space[space_name] == nil) then
print("Error: Failed to find the specified space")
return nil
end
local index_no = -1
for i=0,box.schema.INDEX_MAX,1 do
if (box.space[space_name].index[i] == nil) then break end
if (box.space[space_name].index[i].type == "TREE"
and box.space[space_name].index[i].parts[1].fieldno == field_no
and (box.space[space_name].index[i].parts[1].type == "scalar"
or box.space[space_name].index[i].parts[1].type == "string")) then
index_no = i
break
end
end
if (index_no == -1) then
print("Error: Failed to find an appropriate index")
return nil
end
local index_search_key = ""
local index_search_key_length = 0
local last_character = ""
local c = ""
local c2 = ""
for i=1,string.len(pattern),1 do
c = string.sub(pattern, i, i)
if (last_character ~= "%") then
if (c == '^' or c == "$" or c == "(" or c == ")" or c == "."
or c == "[" or c == "]" or c == "*" or c == "+"
or c == "-" or c == "?") then
break
end
if (c == "%") then
c2 = string.sub(pattern, i + 1, i + 1)
if (string.match(c2, "%p") == nil) then break end
index_search_key = index_search_key .. c2
else
index_search_key = index_search_key .. c
end
end
last_character = c
end
index_search_key_length = string.len(index_search_key)
local result_set = {}
local number_of_tuples_in_result_set = 0
local previous_tuple_field = ""
while true do
local number_of_tuples_since_last_yield = 0
local is_time_for_a_yield = false
for _,tuple in box.space[space_name].index[index_no]:
pairs(index_search_key,{iterator = box.index.GE}) do
if (string.sub(tuple[field_no], 1, index_search_key_length)
> index_search_key) then
break
end
number_of_tuples_since_last_yield = number_of_tuples_since_last_yield + 1
if (number_of_tuples_since_last_yield >= 10
and tuple[field_no] ~= previous_tuple_field) then
index_search_key = tuple[field_no]
is_time_for_a_yield = true
break
end
previous_tuple_field = tuple[field_no]
if (string.match(tuple[field_no], pattern) ~= nil) then
number_of_tuples_in_result_set = number_of_tuples_in_result_set + 1
result_set[number_of_tuples_in_result_set] = tuple
end
end
if (is_time_for_a_yield ~= true) then
break
end
require('fiber').yield()
end
return result_set
end

View File

@ -1,208 +0,0 @@
#!/bin/bash
# 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
set -o nounset
# Check if directory for monit logs exists
if [ ! -d "$HOME/.planetmint-monit" ]; then
mkdir -p "$HOME/.planetmint-monit"
fi
monit_pid_path=${MONIT_PID_PATH:=$HOME/.planetmint-monit/monit_processes}
monit_script_path=${MONIT_SCRIPT_PATH:=$HOME/.planetmint-monit/monit_script}
monit_log_path=${MONIT_LOG_PATH:=$HOME/.planetmint-monit/logs}
monitrc_path=${MONITRC_PATH:=$HOME/.monitrc}
function usage() {
cat <<EOM
Usage: ${0##*/} [-h]
Configure Monit for Planetmint and Tendermint process management.
ENV[MONIT_PID_PATH] || --monit-pid-path PATH
Absolute path to directory where the the program's pid-file will reside.
The pid-file contains the ID(s) of the process(es). (default: ${monit_pid_path})
ENV[MONIT_SCRIPT_PATH] || --monit-script-path PATH
Absolute path to the directory where the executable program or
script is present. (default: ${monit_script_path})
ENV[MONIT_LOG_PATH] || --monit-log-path PATH
Absolute path to the directory where all the logs for processes
monitored by Monit are stored. (default: ${monit_log_path})
ENV[MONITRC_PATH] || --monitrc-path PATH
Absolute path to the monit control file(monitrc). (default: ${monitrc_path})
-h|--help
Show this help and exit.
EOM
}
while [[ $# -gt 0 ]]; do
arg="$1"
case $arg in
--monit-pid-path)
monit_pid_path="$2"
shift
;;
--monit-script-path)
monit_script_path="$2"
shift
;;
--monit-log-path)
monit_log_path="$2"
shift
;;
--monitrc-path)
monitrc_path="$2"
shift
;;
-h | --help)
usage
exit
;;
*)
echo "Unknown option: $1"
usage
exit 1
;;
esac
shift
done
# Check if directory for monit logs exists
if [ ! -d "$monit_log_path" ]; then
mkdir -p "$monit_log_path"
fi
# Check if directory for monit pid files exists
if [ ! -d "$monit_pid_path" ]; then
mkdir -p "$monit_pid_path"
fi
cat >${monit_script_path} <<EOF
#!/bin/bash
case \$1 in
start_planetmint)
pushd \$4
nohup planetmint start > /dev/null 2>&1 &
echo \$! > \$2
popd
;;
stop_planetmint)
kill -2 \`cat \$2\`
rm -f \$2
;;
start_tendermint)
pushd \$4
nohup tendermint node \
--p2p.laddr "tcp://0.0.0.0:26656" \
--rpc.laddr "tcp://0.0.0.0:26657" \
--proxy_app="tcp://0.0.0.0:26658" \
--consensus.create_empty_blocks=false \
--p2p.pex=false >> \$3/tendermint.out.log 2>> \$3/tendermint.err.log &
echo \$! > \$2
popd
;;
stop_tendermint)
kill -2 \`cat \$2\`
rm -f \$2
;;
esac
exit 0
EOF
chmod +x ${monit_script_path}
cat >${monit_script_path}_logrotate <<EOF
#!/bin/bash
case \$1 in
rotate_tendermint_logs)
/bin/cp \$2 \$2.\$(date +%y-%m-%d)
/bin/tar -cvf \$2.\$(date +%Y%m%d_%H%M%S).tar.gz \$2.\$(date +%y-%m-%d)
/bin/rm \$2.\$(date +%y-%m-%d)
/bin/cp /dev/null \$2
;;
esac
exit 0
EOF
chmod +x ${monit_script_path}_logrotate
# Handling overwriting of control file interactively
if [ -f "$monitrc_path" ]; then
echo "$monitrc_path already exists."
read -p "Overwrite[Y]? " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
echo "Overriding $monitrc_path"
else
read -p "Enter absolute path to store Monit control file: " monitrc_path
eval monitrc_path="$monitrc_path"
if [ ! -d "$(dirname $monitrc_path)" ]; then
echo "Failed to save monit control file '$monitrc_path': No such file or directory."
exit 1
fi
fi
fi
# configure monitrc
cat >${monitrc_path} <<EOF
set httpd
port 2812
allow localhost
check process planetmint
with pidfile ${monit_pid_path}/planetmint.pid
start program "${monit_script_path} start_planetmint $monit_pid_path/planetmint.pid ${monit_log_path} ${monit_log_path}"
restart program "${monit_script_path} start_planetmint $monit_pid_path/planetmint.pid ${monit_log_path} ${monit_log_path}"
stop program "${monit_script_path} stop_planetmint $monit_pid_path/planetmint.pid ${monit_log_path} ${monit_log_path}"
check process tendermint
with pidfile ${monit_pid_path}/tendermint.pid
start program "${monit_script_path} start_tendermint ${monit_pid_path}/tendermint.pid ${monit_log_path} ${monit_log_path}"
restart program "${monit_script_path} start_tendermint ${monit_pid_path}/tendermint.pid ${monit_log_path} ${monit_log_path}"
stop program "${monit_script_path} stop_tendermint ${monit_pid_path}/tendermint.pid ${monit_log_path} ${monit_log_path}"
depends on planetmint
check file tendermint.out.log with path ${monit_log_path}/tendermint.out.log
if size > 200 MB then
exec "${monit_script_path}_logrotate rotate_tendermint_logs ${monit_log_path}/tendermint.out.log $monit_pid_path/tendermint.pid"
check file tendermint.err.log with path ${monit_log_path}/tendermint.err.log
if size > 200 MB then
exec "${monit_script_path}_logrotate rotate_tendermint_logs ${monit_log_path}/tendermint.err.log $monit_pid_path/tendermint.pid"
EOF
# Setting permissions for control file
chmod 0700 ${monitrc_path}
echo -e "Planetmint process manager configured!"
set -o errexit

View File

@ -1,83 +0,0 @@
#!/bin/bash
# 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
# Write hostname to list
echo $(hostname) >> /shared/hostnames
# Create ssh folder
mkdir ~/.ssh
# Wait for test container pubkey
while [ ! -f /shared/id_rsa.pub ]; do
echo "WAIT FOR PUBKEY"
sleep 1
done
# Add pubkey to authorized keys
cat /shared/id_rsa.pub > ~/.ssh/authorized_keys
# Allow root user login
sed -i "s/#PermitRootLogin prohibit-password/PermitRootLogin yes/" /etc/ssh/sshd_config
# Restart ssh service
service ssh restart
# Tendermint configuration
tendermint init
# Write node id to shared folder
HOSTNAME=$(hostname)
NODE_ID=$(tendermint show_node_id | tail -n 1)
echo $NODE_ID > /shared/${HOSTNAME}_node_id
# Wait for other node ids
FILES=()
while [ ! ${#FILES[@]} == $SCALE ]; do
echo "WAIT FOR NODE IDS"
sleep 1
FILES=(/shared/*node_id)
done
# Write node ids to persistent peers
PEERS="persistent_peers = \""
for f in ${FILES[@]}; do
ID=$(cat $f)
HOST=$(echo $f | cut -c 9-20)
if [ ! $HOST == $HOSTNAME ]; then
PEERS+="${ID}@${HOST}:26656, "
fi
done
PEERS=$(echo $PEERS | rev | cut -c 2- | rev)
PEERS+="\""
sed -i "/persistent_peers = \"\"/c\\${PEERS}" /tendermint/config/config.toml
# Copy genesis.json to shared folder
cp /tendermint/config/genesis.json /shared/${HOSTNAME}_genesis.json
# Await config file of all services to be present
FILES=()
while [ ! ${#FILES[@]} == $SCALE ]; do
echo "WAIT FOR GENESIS FILES"
sleep 1
FILES=(/shared/*_genesis.json)
done
# Create genesis.json for nodes
if [ ! -f /shared/lock ]; then
echo LOCKING
touch /shared/lock
/usr/src/app/scripts/genesis.py ${FILES[@]}
fi
while [ ! -f /shared/genesis.json ]; do
echo "WAIT FOR GENESIS"
sleep 1
done
# Copy genesis.json to tendermint config
cp /shared/genesis.json /tendermint/config/genesis.json
exec "$@"

View File

@ -1,16 +0,0 @@
#!/bin/bash
# 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
# Create ssh folder
mkdir ~/.ssh
# Create ssh keys
ssh-keygen -q -t rsa -N '' -f ~/.ssh/id_rsa
# Publish pubkey to shared folder
cp ~/.ssh/id_rsa.pub /shared
exec "$@"

View File

@ -1,24 +0,0 @@
#!/bin/bash
# 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
# Start CLI Tests
# Test upsert new validator
/tests/upsert-new-validator.sh
# Test chain migration
# TODO: implementation not finished
#/tests/chain-migration.sh
# TODO: Implement test for voting edge cases or implicit in chain migration and upsert validator?
exitcode=$?
if [ $exitcode -ne 0 ]; then
exit $exitcode
fi
exec "$@"

View File

@ -1,29 +0,0 @@
#!/bin/bash
# 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
# Only continue if all services are ready
HOSTNAMES=()
while [ ! ${#HOSTNAMES[@]} == $SCALE ]; do
echo "WAIT FOR HOSTNAMES"
sleep 1
readarray -t HOSTNAMES < /shared/hostnames
done
for host in ${HOSTNAMES[@]}; do
while [[ "$(curl -s -o /dev/null -w ''%{http_code}'' $host:9984)" != "200" ]]; do
echo "WAIT FOR PLANETMINT $host"
sleep 1
done
done
for host in ${HOSTNAMES[@]}; do
while [[ "$(curl -s -o /dev/null -w ''%{http_code}'' $host:26657)" != "200" ]]; do
echo "WAIT FOR TENDERMINT $host"
sleep 1
done
done
exec "$@"

View File

@ -1,19 +0,0 @@
#!/bin/bash
# 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
run_test() {
docker-compose -f docker-compose.integration.yml up test
}
teardown () {
docker-compose -f docker-compose.integration.yml down
}
run_test
exitcode=$?
teardown
exit $exitcode