From df7c1e1ccfa0673c9e8876091727a43d0be3718e Mon Sep 17 00:00:00 2001 From: Lorenz Herzberger <64837895+LaurentDeMontBlanc@users.noreply.github.com> Date: Thu, 24 Mar 2022 14:24:32 +0100 Subject: [PATCH] Enhance integration test suite (#62) * restructering, added helper, split cli tests for later Signed-off-by: Lorenz Herzberger * fixed threshold test Signed-off-by: Lorenz Herzberger * added acceptance tests to integration test suite Signed-off-by: Lorenz Herzberger * added different threshold signature test scenarios Signed-off-by: Lorenz Herzberger * started chain-migration test implementation Signed-off-by: Lorenz Herzberger * fixed linter errors Signed-off-by: Lorenz Herzberger * removed -s from test command Signed-off-by: Lorenz Herzberger --- docker-compose.integration.yml | 1 + docker-compose.yml | 12 - integration/cli/chain-migration.sh | 47 +++ integration/cli/upsert-new-validator.sh | 33 ++ integration/python/Dockerfile | 14 +- integration/python/src/__init__.py | 0 integration/python/src/conftest.py | 95 +++++ integration/python/src/helper/__init__.py | 0 integration/python/src/helper/hosts.py | 36 ++ integration/python/src/test_basic.py | 29 +- .../python/src/test_divisible_asset.py | 183 ++++++++++ integration/python/src/test_double_spend.py | 48 +++ ...st_multisig.py => test_multiple_owners.py} | 29 +- .../python/src/test_naughty_strings.py | 100 ++++++ integration/python/src/test_stream.py | 131 +++++++ integration/python/src/test_threshold.py | 336 ++++++++++++++++++ integration/python/src/test_zenroom.py | 84 +++++ integration/scripts/election.sh | 20 +- integration/scripts/test.sh | 24 +- 19 files changed, 1151 insertions(+), 71 deletions(-) create mode 100755 integration/cli/chain-migration.sh create mode 100755 integration/cli/upsert-new-validator.sh create mode 100644 integration/python/src/__init__.py create mode 100644 integration/python/src/conftest.py create mode 100644 integration/python/src/helper/__init__.py create mode 100644 integration/python/src/helper/hosts.py create mode 100644 integration/python/src/test_divisible_asset.py create mode 100644 integration/python/src/test_double_spend.py rename integration/python/src/{test_multisig.py => test_multiple_owners.py} (84%) create mode 100644 integration/python/src/test_naughty_strings.py create mode 100644 integration/python/src/test_stream.py create mode 100644 integration/python/src/test_threshold.py create mode 100644 integration/python/src/test_zenroom.py diff --git a/docker-compose.integration.yml b/docker-compose.integration.yml index b1cbdaf..52ac541 100644 --- a/docker-compose.integration.yml +++ b/docker-compose.integration.yml @@ -46,6 +46,7 @@ services: volumes: - ./integration/python/src:/src - ./integration/scripts:/scripts + - ./integration/cli:/tests - shared:/shared volumes: diff --git a/docker-compose.yml b/docker-compose.yml index 39005e9..c825a8f 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -83,18 +83,6 @@ services: environment: - PLANETMINT_ENDPOINT=planetmint - # Planetmint setup to do integration testing wtih Python - python-integration: - build: - context: . - dockerfile: ./integration/python/Dockerfile - volumes: - - ./integration/python/docs:/docs - - ./integration/python/src:/src - environment: - - PLANETMINT_ENDPOINT_1=https://itest1.planetmint.io - - PLANETMINT_ENDPOINT_2=https://itest2.planetmint.io - # Build docs only # docker-compose build bdocs # docker-compose up -d bdocs diff --git a/integration/cli/chain-migration.sh b/integration/cli/chain-migration.sh new file mode 100755 index 0000000..448ffb1 --- /dev/null +++ b/integration/cli/chain-migration.sh @@ -0,0 +1,47 @@ +#!/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=} \ No newline at end of file diff --git a/integration/cli/upsert-new-validator.sh b/integration/cli/upsert-new-validator.sh new file mode 100755 index 0000000..4f63568 --- /dev/null +++ b/integration/cli/upsert-new-validator.sh @@ -0,0 +1,33 @@ +#!/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 \ No newline at end of file diff --git a/integration/python/Dockerfile b/integration/python/Dockerfile index c0e47f1..65f5e15 100644 --- a/integration/python/Dockerfile +++ b/integration/python/Dockerfile @@ -1,9 +1,17 @@ FROM python:3.9 +RUN apt-get update \ + && pip install -U pip \ + && apt-get autoremove \ + && apt-get clean +RUN apt-get install -y vim zsh build-essential cmake openssh-client openssh-server + RUN mkdir -p /src RUN pip install --upgrade \ pytest~=6.2.5 \ planetmint-driver~=0.9.0 \ - pycco - -RUN apt-get update && apt-get install -y openssh-client openssh-server \ No newline at end of file + pycco \ + websocket-client~=0.47.0 \ + git+https://github.com/planetmint/cryptoconditions.git@gitzenroom \ + git+https://github.com/planetmint/planetmint-driver.git@gitzenroom \ + blns \ No newline at end of file diff --git a/integration/python/src/__init__.py b/integration/python/src/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/integration/python/src/conftest.py b/integration/python/src/conftest.py new file mode 100644 index 0000000..808914b --- /dev/null +++ b/integration/python/src/conftest.py @@ -0,0 +1,95 @@ +# 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 + +GENERATE_KEYPAIR = \ + """Rule input encoding base58 + Rule output encoding base58 + Scenario 'ecdh': Create the keypair + Given that I am known as 'Pippo' + When I create the ecdh key + When I create the testnet key + Then print data""" + +# secret key to public key +SK_TO_PK = \ + """Rule input encoding base58 + Rule output encoding base58 + Scenario 'ecdh': Create the keypair + Given that I am known as '{}' + Given I have the 'keys' + When I create the ecdh public key + When I create the testnet address + Then print my 'ecdh public key' + Then print my 'testnet address'""" + +FULFILL_SCRIPT = \ + """Rule input encoding base58 + Rule output encoding base58 + 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' inside 'asset' + Given I have a 'signature' named 'data.signature' inside 'result' + When I verify the 'houses' has a signature in 'data.signature' by 'Alice' + Then print the string 'ok'""" + +HOUSE_ASSETS = { + "data": { + "houses": [ + { + "name": "Harry", + "team": "Gryffindor", + }, + { + "name": "Draco", + "team": "Slytherin", + } + ], + } +} + +ZENROOM_DATA = { + 'also': 'more data' +} + +CONDITION_SCRIPT = """Rule input encoding base58 + Rule output encoding base58 + Scenario 'ecdh': create the signature of an object + Given I have the 'keys' + Given that I have a 'string dictionary' named 'houses' inside 'asset' + When I create the signature of 'houses' + When I rename the 'signature' to 'data.signature' + Then print the 'data.signature'""" + + +@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 HOUSE_ASSETS + + +@pytest.fixture +def zenroom_data(): + return ZENROOM_DATA diff --git a/integration/python/src/helper/__init__.py b/integration/python/src/helper/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/integration/python/src/helper/hosts.py b/integration/python/src/helper/hosts.py new file mode 100644 index 0000000..b14f875 --- /dev/null +++ b/integration/python/src/helper/hosts.py @@ -0,0 +1,36 @@ +# 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) diff --git a/integration/python/src/test_basic.py b/integration/python/src/test_basic.py index 4932638..691dbc3 100644 --- a/integration/python/src/test_basic.py +++ b/integration/python/src/test_basic.py @@ -4,21 +4,18 @@ # Code is Apache-2.0 and docs are CC-BY-4.0 # import Planetmint and create object -from planetmint_driver import Planetmint 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 = [] - with open('/shared/hostnames') as f: - hosts = f.readlines() - - pm_hosts = list(map(lambda x: Planetmint(x), hosts)) - - pm_alpha = pm_hosts[0] - pm_betas = pm_hosts[1:] + hosts = Hosts('/shared/hostnames') + pm_alpha = hosts.get_connection() # genarate a keypair alice = generate_keypair() @@ -50,13 +47,8 @@ def test_basic(): creation_tx_id = fulfilled_creation_tx['id'] - # retrieve transactions from all planetmint nodes - creation_tx_alpha = pm_alpha.transactions.retrieve(creation_tx_id) - creation_tx_betas = list(map(lambda beta: beta.transactions.retrieve(creation_tx_id), pm_betas)) - # Assert that transaction is stored on all planetmint nodes - for tx in creation_tx_betas: - assert creation_tx_alpha == tx + hosts.assert_transaction(creation_tx_id) # Transfer # create the output and inout for the transaction @@ -87,10 +79,5 @@ def test_basic(): transfer_tx_id = sent_transfer_tx['id'] - # retrieve transactions from both planetmint nodes - transfer_tx_alpha = pm_alpha.transactions.retrieve(transfer_tx_id) - transfer_tx_betas = list(map(lambda beta: beta.transactions.retrieve(transfer_tx_id), pm_betas)) - # Assert that transaction is stored on both planetmint nodes - for tx in transfer_tx_betas: - assert transfer_tx_alpha == tx + hosts.assert_transaction(transfer_tx_id) diff --git a/integration/python/src/test_divisible_asset.py b/integration/python/src/test_divisible_asset.py new file mode 100644 index 0000000..c324f61 --- /dev/null +++ b/integration/python/src/test_divisible_asset.py @@ -0,0 +1,183 @@ +# 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. +# +# This integration test is a rip-off of our +# [tutorial](https://docs.planetmint.com/projects/py-driver/en/latest/usage.html). + +# ## 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)], + asset=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_asset = {'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_asset, + 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_asset = {'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', + asset=transfer_asset, + 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 :) diff --git a/integration/python/src/test_double_spend.py b/integration/python/src/test_double_spend.py new file mode 100644 index 0000000..1a17738 --- /dev/null +++ b/integration/python/src/test_double_spend.py @@ -0,0 +1,48 @@ +# 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, + asset={'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 diff --git a/integration/python/src/test_multisig.py b/integration/python/src/test_multiple_owners.py similarity index 84% rename from integration/python/src/test_multisig.py rename to integration/python/src/test_multiple_owners.py index 94ce9dc..98f3ea6 100644 --- a/integration/python/src/test_multisig.py +++ b/integration/python/src/test_multiple_owners.py @@ -22,20 +22,16 @@ import time # For this test case we need import and use the Python driver -from planetmint_driver import Planetmint 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 = [] - with open('/shared/hostnames') as f: - hosts = f.readlines() - - pm_hosts = list(map(lambda x: Planetmint(x), hosts)) - - pm_alpha = pm_hosts[0] - pm_betas = pm_hosts[1:] + hosts = Hosts('/shared/hostnames') + pm_alpha = hosts.get_connection() # Generate Keypairs for Alice and Bob! alice, bob = generate_keypair(), generate_keypair() @@ -73,13 +69,9 @@ def test_multiple_owners(): dw_id = fulfilled_dw_tx['id'] time.sleep(1) - # Let's retrieve the transaction from both nodes - pm_alpha_tx = pm_alpha.transactions.retrieve(dw_id) - pm_betas_tx = list(map(lambda beta: beta.transactions.retrieve(dw_id), pm_betas)) - # Both retrieved transactions should be the same - for tx in pm_betas_tx: - assert pm_alpha_tx == tx + # 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), \ @@ -124,13 +116,8 @@ def test_multiple_owners(): sent_transfer_tx = pm_alpha.transactions.send_commit(fulfilled_transfer_tx) time.sleep(1) - # Retrieve the fulfilled transaction from both nodes - pm_alpha_tx = pm_alpha.transactions.retrieve(fulfilled_transfer_tx['id']) - pm_betas_tx = list(map(lambda beta: beta.transactions.retrieve(fulfilled_transfer_tx['id']), pm_betas)) - # Now compare if both nodes returned the same transaction - for tx in pm_betas_tx: - assert pm_alpha_tx == tx + hosts.assert_transaction(fulfilled_transfer_tx['id']) # They check if the transaction was successful. assert pm_alpha.transactions.retrieve( diff --git a/integration/python/src/test_naughty_strings.py b/integration/python/src/test_naughty_strings.py new file mode 100644 index 0000000..4a090c0 --- /dev/null +++ b/integration/python/src/test_naughty_strings.py @@ -0,0 +1,100 @@ +# 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() + + +# This is our base test case, but we'll reuse it to send naughty strings as both keys and values. +def send_naughty_tx(asset, 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, + asset=asset, + 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): + + asset = {'data': {naughty_string: 'nice_value'}} + metadata = {naughty_string: 'nice_value'} + + send_naughty_tx(asset, metadata) + + +@pytest.mark.parametrize("naughty_string", naughty_strings, ids=naughty_strings) +def test_naughty_values(naughty_string): + + asset = {'data': {'nice_key': naughty_string}} + metadata = {'nice_key': naughty_string} + + send_naughty_tx(asset, metadata) diff --git a/integration/python/src/test_stream.py b/integration/python/src/test_stream.py new file mode 100644 index 0000000..c93d5c6 --- /dev/null +++ b/integration/python/src/test_stream.py @@ -0,0 +1,131 @@ +# 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, + asset={'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 diff --git a/integration/python/src/test_threshold.py b/integration/python/src/test_threshold.py new file mode 100644 index 0000000..f118651 --- /dev/null +++ b/integration/python/src/test_threshold.py @@ -0,0 +1,336 @@ +# 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) diff --git a/integration/python/src/test_zenroom.py b/integration/python/src/test_zenroom.py new file mode 100644 index 0000000..8f749a6 --- /dev/null +++ b/integration/python/src/test_zenroom.py @@ -0,0 +1,84 @@ +# GOAL: +# In this script I tried to implement the ECDSA signature using zenroom + +# However, the scripts are customizable and so with the same procedure +# we can implement more complex smart contracts + +# PUBLIC IDENTITY +# The public identity of the users in this script (Bob and Alice) +# is the pair (ECDH public key, Testnet address) + +import json + +from cryptoconditions import ZenroomSha256 +from json.decoder import JSONDecodeError + + +def test_zenroom(gen_key_zencode, secret_key_to_private_key_zencode, fulfill_script_zencode, + condition_script_zencode, zenroom_data, zenroom_house_assets): + alice = json.loads(ZenroomSha256.run_zenroom(gen_key_zencode).output)['keys'] + bob = json.loads(ZenroomSha256.run_zenroom(gen_key_zencode).output)['keys'] + + zen_public_keys = json.loads(ZenroomSha256.run_zenroom(secret_key_to_private_key_zencode.format('Alice'), + keys={'keys': alice}).output) + zen_public_keys.update(json.loads(ZenroomSha256.run_zenroom(secret_key_to_private_key_zencode.format('Bob'), + keys={'keys': bob}).output)) + + # CRYPTO-CONDITIONS: instantiate an Ed25519 crypto-condition for buyer + zenSha = ZenroomSha256(script=fulfill_script_zencode, keys=zen_public_keys, data=zenroom_data) + + # CRYPTO-CONDITIONS: generate the condition uri + condition_uri = zenSha.condition.serialize_uri() + + # CRYPTO-CONDITIONS: construct an unsigned fulfillment dictionary + unsigned_fulfillment_dict = { + 'type': zenSha.TYPE_NAME, + 'script': fulfill_script_zencode, + 'keys': zen_public_keys, + } + + output = { + 'amount': '1000', + 'condition': { + 'details': unsigned_fulfillment_dict, + 'uri': condition_uri, + }, + 'data': zenroom_data, + 'script': fulfill_script_zencode, + 'conf': '', + 'public_keys': (zen_public_keys['Alice']['ecdh_public_key'], ), + } + + input_ = { + 'fulfillment': None, + 'fulfills': None, + 'owners_before': (zen_public_keys['Alice']['ecdh_public_key'], ), + } + + token_creation_tx = { + 'operation': 'CREATE', + 'asset': zenroom_house_assets, + 'metadata': None, + 'outputs': (output,), + 'inputs': (input_,), + 'version': '2.0', + 'id': None, + } + + # JSON: serialize the transaction-without-id to a json formatted string + message = json.dumps( + token_creation_tx, + sort_keys=True, + separators=(',', ':'), + ensure_ascii=False, + ) + + try: + assert(not zenSha.validate(message=message)) + except JSONDecodeError: + pass + except ValueError: + pass + + message = zenSha.sign(message, condition_script_zencode, alice) + assert(zenSha.validate(message=message)) diff --git a/integration/scripts/election.sh b/integration/scripts/election.sh index d55abc7..65d39cb 100755 --- a/integration/scripts/election.sh +++ b/integration/scripts/election.sh @@ -16,12 +16,17 @@ show_validator () { # Elect new voting power for node elect_validator () { - planetmint election new upsert-validator $1 $2 $3 --private-key /tendermint/config/priv_validator_key.json + 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 + planetmint election show $1 2>&1 } # Approve election @@ -33,7 +38,13 @@ approve_validator () { elect () { node_id=$(show_id) validator_pubkey=$(show_validator | jq -r .value) - proposal=$(elect_validator $validator_pubkey $1 $node_id 2>&1 | grep SUCCESS) + 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##* } } @@ -50,6 +61,9 @@ while [ "$1" != "" ]; do elect ) shift elect $1 ;; + migrate ) shift + migrate + ;; show_election ) shift show_election $1 ;; diff --git a/integration/scripts/test.sh b/integration/scripts/test.sh index de9271d..2d3b796 100755 --- a/integration/scripts/test.sh +++ b/integration/scripts/test.sh @@ -4,19 +4,21 @@ # SPDX-License-Identifier: (Apache-2.0 AND CC-BY-4.0) # Code is Apache-2.0 and docs are CC-BY-4.0 -# Read host names from shared -readarray -t HOSTNAMES < /shared/hostnames +# Start CLI Tests -# Split into proposer and approvers -ALPHA=${HOSTNAMES[0]} -BETAS=${HOSTNAMES[@]:1} +# Test upsert new validator +/tests/upsert-new-validator.sh -# Propose validator upsert -result=$(ssh -o "StrictHostKeyChecking=no" -i \~/.ssh/id_rsa root@${ALPHA} 'bash -s' < scripts/election.sh elect 2) +# Test chain migration +# TODO: implementation not finished +#/tests/chain-migration.sh -# Approve validator upsert -for BETA in ${BETAS[@]}; do - ssh -o "StrictHostKeyChecking=no" -i ~/.ssh/id_rsa root@${BETA} 'bash -s' < scripts/election.sh approve $result -done +# 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 "$@" \ No newline at end of file