mirror of
https://github.com/planetmint/planetmint.git
synced 2025-03-30 15:08:31 +00:00
removed integration tests from the repo
Signed-off-by: Jürgen Eckel <juergen@riddleandcode.com>
This commit is contained in:
parent
1fc306e09d
commit
93be1e788a
19
.github/workflows/integration-test.yml
vendored
19
.github/workflows/integration-test.yml
vendored
@ -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
|
9
Makefile
9
Makefile
@ -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
|
||||
|
||||
|
@ -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:
|
@ -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
|
||||
```
|
@ -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=}
|
@ -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
|
1
integration/python/.gitignore
vendored
1
integration/python/.gitignore
vendored
@ -1 +0,0 @@
|
||||
docs
|
@ -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
|
@ -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
|
@ -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)
|
@ -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)
|
@ -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 :)
|
@ -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
|
@ -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
|
||||
)
|
@ -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 ",
|
||||
"<script>alert(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)
|
@ -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
|
@ -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)
|
@ -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}")
|
@ -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
|
@ -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
|
@ -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
|
@ -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()
|
@ -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
|
@ -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
|
@ -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 "$@"
|
@ -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 "$@"
|
@ -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 "$@"
|
@ -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 "$@"
|
@ -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
|
Loading…
x
Reference in New Issue
Block a user