removed duplicate transactions test suite

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>
This commit is contained in:
Lorenz Herzberger 2022-10-12 13:09:20 +02:00
parent ccf55d1b8d
commit ebaa3e1103
No known key found for this signature in database
GPG Key ID: FA5EE906EB55316A
6 changed files with 0 additions and 1445 deletions

View File

@ -1,311 +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 base58 import b58decode
import pytest
from cryptoconditions import ThresholdSha256, Ed25519Sha256
USER_PRIVATE_KEY = "8eJ8q9ZQpReWyQT5aFCiwtZ5wDZC4eDnCen88p3tQ6ie"
USER_PUBLIC_KEY = "JEAkEJqLbbgDRAtMm8YAjGp759Aq2qTn9eaEHUj2XePE"
USER2_PRIVATE_KEY = "F86PQPiqMTwM2Qi2Sda3U4Vdh3AgadMdX3KNVsu5wNJr"
USER2_PUBLIC_KEY = "GDxwMFbwdATkQELZbMfW8bd9hbNYMZLyVXA3nur2aNbE"
USER3_PRIVATE_KEY = "4rNQFzWQbVwuTiDVxwuFMvLG5zd8AhrQKCtVovBvcYsB"
USER3_PUBLIC_KEY = "Gbrg7JtxdjedQRmr81ZZbh1BozS7fBW88ZyxNDy7WLNC"
CC_FULFILLMENT_URI = (
"pGSAINdamAGCsQq31Uv-08lkBzoO4XLz2qYjJa8CGmj3B1EagUDlVkMAw2CscpCG4syAboKKh"
"Id_Hrjl2XTYc-BlIkkBVV-4ghWQozusxh45cBz5tGvSW_XwWVu-JGVRQUOOehAL"
)
CC_CONDITION_URI = "ni:///sha-256;" "eZI5q6j8T_fqv7xMROaei9_tmTMk4S7WR5Kr4onPHV8" "?fpt=ed25519-sha-256&cost=131072"
ASSET_DEFINITION = {"data": "QmaozNR7DZHQK1ZcU9p7QdrshMvXqWK6gpu5rmrkPdT3L4"}
DATA = "QmaozNR7DZHQK1ZcU9p7QdrshMvXqWK6gpu5rmrkPdT3L4"
@pytest.fixture
def user_priv():
return USER_PRIVATE_KEY
@pytest.fixture
def user_pub():
return USER_PUBLIC_KEY
@pytest.fixture
def user2_priv():
return USER2_PRIVATE_KEY
@pytest.fixture
def user2_pub():
return USER2_PUBLIC_KEY
@pytest.fixture
def user3_priv():
return USER3_PRIVATE_KEY
@pytest.fixture
def user3_pub():
return USER3_PUBLIC_KEY
@pytest.fixture
def ffill_uri():
return CC_FULFILLMENT_URI
@pytest.fixture
def cond_uri():
return CC_CONDITION_URI
@pytest.fixture
def user_Ed25519(user_pub):
return Ed25519Sha256(public_key=b58decode(user_pub))
@pytest.fixture
def user_user2_threshold(user_pub, user2_pub):
user_pub_keys = [user_pub, user2_pub]
threshold = ThresholdSha256(threshold=len(user_pub_keys))
for user_pub in user_pub_keys:
threshold.add_subfulfillment(Ed25519Sha256(public_key=b58decode(user_pub)))
return threshold
@pytest.fixture
def user2_Ed25519(user2_pub):
return Ed25519Sha256(public_key=b58decode(user2_pub))
@pytest.fixture
def user_input(user_Ed25519, user_pub):
from transactions.common.transaction import Input
return Input(user_Ed25519, [user_pub])
@pytest.fixture
def user_user2_threshold_output(user_user2_threshold, user_pub, user2_pub):
from transactions.common.transaction import Output
return Output(user_user2_threshold, [user_pub, user2_pub])
@pytest.fixture
def user_user2_threshold_input(user_user2_threshold, user_pub, user2_pub):
from transactions.common.transaction import Input
return Input(user_user2_threshold, [user_pub, user2_pub])
@pytest.fixture
def user_output(user_Ed25519, user_pub):
from transactions.common.transaction import Output
return Output(user_Ed25519, [user_pub])
@pytest.fixture
def user2_output(user2_Ed25519, user2_pub):
from transactions.common.transaction import Output
return Output(user2_Ed25519, [user2_pub])
@pytest.fixture
def asset_definition():
return ASSET_DEFINITION
@pytest.fixture
def data():
return DATA
@pytest.fixture
def utx(user_input, user_output):
from transactions.common.transaction import Transaction
return Transaction(Transaction.CREATE, {"data": None}, [user_input], [user_output])
@pytest.fixture
def tx(utx, user_priv):
return utx.sign([user_priv])
@pytest.fixture
def transfer_utx(user_output, user2_output, utx):
from transactions.common.transaction import Input, TransactionLink, Transaction
user_output = user_output.to_dict()
input = Input(utx.outputs[0].fulfillment, user_output["public_keys"], TransactionLink(utx.id, 0))
return Transaction("TRANSFER", {"id": utx.id}, [input], [user2_output])
@pytest.fixture
def transfer_tx(transfer_utx, user_priv):
return transfer_utx.sign([user_priv])
@pytest.fixture(scope="session")
def dummy_transaction():
return {
"asset": {"data": None},
"id": 64 * "a",
"inputs": [
{
"fulfillment": "dummy",
"fulfills": None,
"owners_before": [58 * "a"],
}
],
"metadata": None,
"operation": "CREATE",
"outputs": [
{
"amount": "1",
"condition": {
"details": {"public_key": 58 * "b", "type": "ed25519-sha-256"},
"uri": "dummy",
},
"public_keys": [58 * "b"],
}
],
"version": "2.0",
}
@pytest.fixture
def unfulfilled_transaction():
return {
"asset": {"data": "QmaozNR7DZHQK1ZcU9p7QdrshMvXqWK6gpu5rmrkPdT3L4"},
"id": None,
"inputs": [
{
# XXX This could be None, see #1925
# https://github.com/planetmint/planetmint/issues/1925
"fulfillment": {
"public_key": "JEAkEJqLbbgDRAtMm8YAjGp759Aq2qTn9eaEHUj2XePE",
"type": "ed25519-sha-256",
},
"fulfills": None,
"owners_before": ["JEAkEJqLbbgDRAtMm8YAjGp759Aq2qTn9eaEHUj2XePE"],
}
],
"metadata": None,
"operation": "CREATE",
"outputs": [
{
"amount": "1",
"condition": {
"details": {
"public_key": "JEAkEJqLbbgDRAtMm8YAjGp759Aq2qTn9eaEHUj2XePE",
"type": "ed25519-sha-256",
},
"uri": "ni:///sha-256;49C5UWNODwtcINxLgLc90bMCFqCymFYONGEmV4a0sG4?fpt=ed25519-sha-256&cost=131072",
},
"public_keys": ["JEAkEJqLbbgDRAtMm8YAjGp759Aq2qTn9eaEHUj2XePE"],
}
],
"version": "1.0",
}
@pytest.fixture
def fulfilled_transaction():
return {
"asset": {"data": "QmaozNR7DZHQK1ZcU9p7QdrshMvXqWK6gpu5rmrkPdT3L4"},
"id": None,
"inputs": [
{
"fulfillment": (
"pGSAIP_2P1Juh-94sD3uno1lxMPd9EkIalRo7QB014pT6dD9g"
"UANRNxasDy1Dfg9C2Fk4UgHdYFsJzItVYi5JJ_vWc6rKltn0k"
"jagynI0xfyR6X9NhzccTt5oiNH9mThEb4QmagN"
),
"fulfills": None,
"owners_before": ["JEAkEJqLbbgDRAtMm8YAjGp759Aq2qTn9eaEHUj2XePE"],
}
],
"metadata": None,
"operation": "CREATE",
"outputs": [
{
"amount": "1",
"condition": {
"details": {
"public_key": "JEAkEJqLbbgDRAtMm8YAjGp759Aq2qTn9eaEHUj2XePE",
"type": "ed25519-sha-256",
},
"uri": "ni:///sha-256;49C5UWNODwtcINxLgLc90bMCFqCymFYONGEmV4a0sG4?fpt=ed25519-sha-256&cost=131072",
},
"public_keys": ["JEAkEJqLbbgDRAtMm8YAjGp759Aq2qTn9eaEHUj2XePE"],
}
],
"version": "1.0",
}
# TODO For reviewers: Pick which approach you like best: parametrized or not?
@pytest.fixture(
params=(
{
"id": None,
"fulfillment": {"public_key": "JEAkEJqLbbgDRAtMm8YAjGp759Aq2qTn9eaEHUj2XePE", "type": "ed25519-sha-256"},
},
{
"id": None,
"fulfillment": (
"pGSAIP_2P1Juh-94sD3uno1lxMPd9EkIalRo7QB014pT6dD9g"
"UANRNxasDy1Dfg9C2Fk4UgHdYFsJzItVYi5JJ_vWc6rKltn0k"
"jagynI0xfyR6X9NhzccTt5oiNH9mThEb4QmagN"
),
},
{
"id": "7a7c827cf4ef7985f08f4e9d16f5ffc58ca4e82271921dfbed32e70cb462485f",
"fulfillment": (
"pGSAIP_2P1Juh-94sD3uno1lxMPd9EkIalRo7QB014pT6dD9g"
"UANRNxasDy1Dfg9C2Fk4UgHdYFsJzItVYi5JJ_vWc6rKltn0k"
"jagynI0xfyR6X9NhzccTt5oiNH9mThEb4QmagN"
),
},
)
)
def tri_state_transaction(request):
tx = {
"asset": {"data": "QmaozNR7DZHQK1ZcU9p7QdrshMvXqWK6gpu5rmrkPdT3L4"},
"id": None,
"inputs": [
{"fulfillment": None, "fulfills": None, "owners_before": ["JEAkEJqLbbgDRAtMm8YAjGp759Aq2qTn9eaEHUj2XePE"]}
],
"metadata": None,
"operation": "CREATE",
"outputs": [
{
"amount": "1",
"condition": {
"details": {
"public_key": "JEAkEJqLbbgDRAtMm8YAjGp759Aq2qTn9eaEHUj2XePE",
"type": "ed25519-sha-256",
},
"uri": "ni:///sha-256;49C5UWNODwtcINxLgLc90bMCFqCymFYONGEmV4a0sG4?fpt=ed25519-sha-256&cost=131072",
},
"public_keys": ["JEAkEJqLbbgDRAtMm8YAjGp759Aq2qTn9eaEHUj2XePE"],
}
],
"version": "2.0",
}
tx["id"] = request.param["id"]
tx["inputs"][0]["fulfillment"] = request.param["fulfillment"]
return tx

View File

@ -1,91 +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
from copy import deepcopy
from transactions.common.transaction import Transaction
from transactions.types.assets.create import Create
from transactions.common.crypto import generate_key_pair
from transactions.common.memoize import to_dict, from_dict
pytestmark = pytest.mark.bdb
def test_memoize_to_dict(b):
alice = generate_key_pair()
asset = {"data": "QmaozNR7DZHQK1ZcU9p7QdrshMvXqWK6gpu5rmrkPdT3L4"}
assert to_dict.cache_info().hits == 0
assert to_dict.cache_info().misses == 0
tx = Create.generate(
[alice.public_key],
[([alice.public_key], 1)],
asset=asset,
).sign([alice.private_key])
tx.to_dict()
assert to_dict.cache_info().hits == 0
assert to_dict.cache_info().misses == 1
tx.to_dict()
tx.to_dict()
assert to_dict.cache_info().hits == 2
assert to_dict.cache_info().misses == 1
def test_memoize_from_dict(b):
alice = generate_key_pair()
asset = {"data": "QmaozNR7DZHQK1ZcU9p7QdrshMvXqWK6gpu5rmrkPdT3L4"}
assert from_dict.cache_info().hits == 0
assert from_dict.cache_info().misses == 0
tx = Create.generate(
[alice.public_key],
[([alice.public_key], 1)],
asset=asset,
).sign([alice.private_key])
tx_dict = deepcopy(tx.to_dict())
Transaction.from_dict(tx_dict)
assert from_dict.cache_info().hits == 0
assert from_dict.cache_info().misses == 1
Transaction.from_dict(tx_dict)
Transaction.from_dict(tx_dict)
assert from_dict.cache_info().hits == 2
assert from_dict.cache_info().misses == 1
def test_memoize_input_valid(b):
alice = generate_key_pair()
asset = {"data": "QmaozNR7DZHQK1ZcU9p7QdrshMvXqWK6gpu5rmrkPdT3L4"}
assert Transaction._input_valid.cache_info().hits == 0
assert Transaction._input_valid.cache_info().misses == 0
tx = Create.generate(
[alice.public_key],
[([alice.public_key], 1)],
asset=asset,
).sign([alice.private_key])
tx.inputs_valid()
assert Transaction._input_valid.cache_info().hits == 0
assert Transaction._input_valid.cache_info().misses == 1
tx.inputs_valid()
tx.inputs_valid()
assert Transaction._input_valid.cache_info().hits == 2
assert Transaction._input_valid.cache_info().misses == 1

View File

@ -1,142 +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
"""This module is tests related to schema checking, but _not_ of granular schematic
properties related to validation.
"""
from unittest.mock import patch
from hypothesis import given
from hypothesis.strategies import from_regex as regex
from pytest import raises
from transactions.common.exceptions import SchemaValidationError
from transactions.common.schema import (
TX_SCHEMA_COMMON,
validate_transaction_schema,
)
SUPPORTED_CRYPTOCONDITION_TYPES = ("threshold-sha-256", "ed25519-sha-256")
UNSUPPORTED_CRYPTOCONDITION_TYPES = ("preimage-sha-256", "prefix-sha-256", "rsa-sha-256")
################################################################################
# Test of schema utils
def _test_additionalproperties(node, path=""):
"""Validate that each object node has additionalProperties set, so that
objects with junk keys do not pass as valid.
"""
if isinstance(node, list):
for i, nnode in enumerate(node):
_test_additionalproperties(nnode, path + str(i) + ".")
if isinstance(node, dict):
if node.get("type") == "object":
assert "additionalProperties" in node, "additionalProperties not set at path:" + path
for name, val in node.items():
_test_additionalproperties(val, path + name + ".")
def test_transaction_schema_additionalproperties():
_test_additionalproperties(TX_SCHEMA_COMMON)
################################################################################
# Test call transaction schema
def test_validate_transaction_create(create_tx):
validate_transaction_schema(create_tx.to_dict())
def test_validate_transaction_signed_create(signed_create_tx):
validate_transaction_schema(signed_create_tx.to_dict())
def test_validate_transaction_signed_transfer(signed_transfer_tx):
validate_transaction_schema(signed_transfer_tx.to_dict())
def test_validate_transaction_fails():
with raises(SchemaValidationError):
validate_transaction_schema({})
def test_validate_failure_inconsistent():
with patch("jsonschema.validate"):
with raises(SchemaValidationError):
validate_transaction_schema({})
@given(
condition_uri=regex(
r"^ni:\/\/\/sha-256;([a-zA-Z0-9_-]{{0,86}})\?fpt=({})"
r"&cost=[0-9]+(?![\n])$".format("|".join(t for t in SUPPORTED_CRYPTOCONDITION_TYPES))
)
)
def test_condition_uri_with_supported_fpt(dummy_transaction, condition_uri):
dummy_transaction["outputs"][0]["condition"]["uri"] = condition_uri
validate_transaction_schema(dummy_transaction)
@given(
condition_uri=regex(
r"^ni:\/\/\/sha-256;([a-zA-Z0-9_-]{{0,86}})\?fpt="
r"({})&cost=[0-9]+(?![\n])$".format("|".join(UNSUPPORTED_CRYPTOCONDITION_TYPES))
)
)
def test_condition_uri_with_unsupported_fpt(dummy_transaction, condition_uri):
dummy_transaction["outputs"][0]["condition"]["uri"] = condition_uri
with raises(SchemaValidationError):
validate_transaction_schema(dummy_transaction)
@given(
condition_uri=regex(
r"^ni:\/\/\/sha-256;([a-zA-Z0-9_-]{{0,86}})\?fpt=(?!{})"
r"&cost=[0-9]+(?![\n])$".format("$|".join(t for t in SUPPORTED_CRYPTOCONDITION_TYPES))
)
)
def test_condition_uri_with_unknown_fpt(dummy_transaction, condition_uri):
dummy_transaction["outputs"][0]["condition"]["uri"] = condition_uri
with raises(SchemaValidationError):
validate_transaction_schema(dummy_transaction)
@given(
condition_uri=regex(
r"^ni:\/\/\/sha-256;([a-zA-Z0-9_-]{0,86})\?fpt=threshold-sha-256"
r"&cost=[0-9]+&subtypes=ed25519-sha-256(?![\n])$"
)
)
def test_condition_uri_with_supported_subtype(dummy_transaction, condition_uri):
dummy_transaction["outputs"][0]["condition"]["uri"] = condition_uri
validate_transaction_schema(dummy_transaction)
@given(
condition_uri=regex(
r"^ni:\/\/\/sha-256;([a-zA-Z0-9_-]{0,86})\?fpt=threshold-sha-256&cost="
r"[0-9]+&subtypes=(preimage-sha-256|prefix-sha-256|rsa-sha-256)(?![\n])$"
)
)
def test_condition_uri_with_unsupported_subtype(dummy_transaction, condition_uri):
dummy_transaction["outputs"][0]["condition"]["uri"] = condition_uri
with raises(SchemaValidationError):
validate_transaction_schema(dummy_transaction)
@given(
condition_uri=regex(
r"^ni:\/\/\/sha-256;([a-zA-Z0-9_-]{{0,86}})\?fpt=threshold-sha-256"
r"&cost=[0-9]+&subtypes=(?!{})(?![\n])$".format("$|".join(t for t in SUPPORTED_CRYPTOCONDITION_TYPES))
)
)
def test_condition_uri_with_unknown_subtype(dummy_transaction, condition_uri):
dummy_transaction["outputs"][0]["condition"]["uri"] = condition_uri
with raises(SchemaValidationError):
validate_transaction_schema(dummy_transaction)

View File

@ -1,886 +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
"""These are tests of the API of the Transaction class and associated classes.
Tests for transaction validation are separate.
"""
import json
from copy import deepcopy
from base58 import b58encode, b58decode
from transactions.types.assets.create import Create
from transactions.types.assets.transfer import Transfer
from transactions.common.transaction import Output
from transactions.common.transaction import Input
from transactions.common.exceptions import AmountError
from transactions.common.transaction import Transaction
from transactions.common.transaction import TransactionLink
from cryptoconditions import ThresholdSha256
from cryptoconditions import Fulfillment
from cryptoconditions import PreimageSha256
from cryptoconditions import Ed25519Sha256
from pytest import mark, raises
from hashlib import sha3_256
pytestmark = mark.bdb
def test_input_serialization(ffill_uri, user_pub):
expected = {
"owners_before": [user_pub],
"fulfillment": ffill_uri,
"fulfills": None,
}
input = Input(Fulfillment.from_uri(ffill_uri), [user_pub])
assert input.to_dict() == expected
def test_input_deserialization_with_uri(ffill_uri, user_pub):
expected = Input(Fulfillment.from_uri(ffill_uri), [user_pub])
ffill = {
"owners_before": [user_pub],
"fulfillment": ffill_uri,
"fulfills": None,
}
input = Input.from_dict(ffill)
assert input == expected
@mark.skip(reason="None is tolerated because it is None before fulfilling.")
def test_input_deserialization_with_invalid_input(user_pub):
from transactions.common.transaction import Input
ffill = {
"owners_before": [user_pub],
"fulfillment": None,
"fulfills": None,
}
with raises(TypeError):
Input.from_dict(ffill)
def test_input_deserialization_with_invalid_fulfillment_uri(user_pub):
from transactions.common.exceptions import InvalidSignature
from transactions.common.transaction import Input
ffill = {
"owners_before": [user_pub],
"fulfillment": "an invalid fulfillment",
"fulfills": None,
}
with raises(InvalidSignature):
Input.from_dict(ffill)
def test_input_deserialization_with_unsigned_fulfillment(ffill_uri, user_pub):
expected = Input(Fulfillment.from_uri(ffill_uri), [user_pub])
ffill = {
"owners_before": [user_pub],
"fulfillment": Fulfillment.from_uri(ffill_uri),
"fulfills": None,
}
input = Input.from_dict(ffill)
assert input == expected
def test_output_serialization(user_Ed25519, user_pub):
from transactions.common.transaction import Output
expected = {
"condition": {
"uri": user_Ed25519.condition_uri,
"details": {
"type": "ed25519-sha-256",
"public_key": b58encode(user_Ed25519.public_key).decode(),
},
},
"public_keys": [user_pub],
"amount": "1",
}
cond = Output(user_Ed25519, [user_pub], 1)
assert cond.to_dict() == expected
def test_output_deserialization(user_Ed25519, user_pub):
from transactions.common.transaction import Output
expected = Output(user_Ed25519, [user_pub], 1)
cond = {
"condition": {
"uri": user_Ed25519.condition_uri,
"details": {
"type": "ed25519-sha-256",
"public_key": b58encode(user_Ed25519.public_key).decode(),
},
},
"public_keys": [user_pub],
"amount": "1",
}
cond = Output.from_dict(cond)
assert cond == expected
def test_output_hashlock_serialization():
secret = b"wow much secret"
hashlock = PreimageSha256(preimage=secret).condition_uri
expected = {
"condition": {
"uri": hashlock,
},
"public_keys": None,
"amount": "1",
}
cond = Output(hashlock, amount=1)
assert cond.to_dict() == expected
def test_output_hashlock_deserialization():
secret = b"wow much secret"
hashlock = PreimageSha256(preimage=secret).condition_uri
expected = Output(hashlock, amount=1)
cond = {
"condition": {"uri": hashlock},
"public_keys": None,
"amount": "1",
}
cond = Output.from_dict(cond)
assert cond == expected
def test_invalid_output_initialization(cond_uri, user_pub):
with raises(TypeError):
Output(cond_uri, user_pub)
with raises(TypeError):
Output(cond_uri, [user_pub], "amount")
with raises(AmountError):
Output(cond_uri, [user_pub], 0)
def test_generate_output_split_half_recursive(user_pub, user2_pub, user3_pub):
expected_simple1 = Ed25519Sha256(public_key=b58decode(user_pub))
expected_simple2 = Ed25519Sha256(public_key=b58decode(user2_pub))
expected_simple3 = Ed25519Sha256(public_key=b58decode(user3_pub))
expected = ThresholdSha256(threshold=2)
expected.add_subfulfillment(expected_simple1)
expected_threshold = ThresholdSha256(threshold=2)
expected_threshold.add_subfulfillment(expected_simple2)
expected_threshold.add_subfulfillment(expected_simple3)
expected.add_subfulfillment(expected_threshold)
cond = Output.generate([user_pub, [user2_pub, expected_simple3]], 1)
assert cond.fulfillment.to_dict() == expected.to_dict()
def test_generate_outputs_split_half_single_owner(user_pub, user2_pub, user3_pub):
expected_simple1 = Ed25519Sha256(public_key=b58decode(user_pub))
expected_simple2 = Ed25519Sha256(public_key=b58decode(user2_pub))
expected_simple3 = Ed25519Sha256(public_key=b58decode(user3_pub))
expected = ThresholdSha256(threshold=2)
expected_threshold = ThresholdSha256(threshold=2)
expected_threshold.add_subfulfillment(expected_simple2)
expected_threshold.add_subfulfillment(expected_simple3)
expected.add_subfulfillment(expected_threshold)
expected.add_subfulfillment(expected_simple1)
cond = Output.generate([[expected_simple2, user3_pub], user_pub], 1)
assert cond.fulfillment.to_dict() == expected.to_dict()
def test_generate_outputs_flat_ownage(user_pub, user2_pub, user3_pub):
expected_simple1 = Ed25519Sha256(public_key=b58decode(user_pub))
expected_simple2 = Ed25519Sha256(public_key=b58decode(user2_pub))
expected_simple3 = Ed25519Sha256(public_key=b58decode(user3_pub))
expected = ThresholdSha256(threshold=3)
expected.add_subfulfillment(expected_simple1)
expected.add_subfulfillment(expected_simple2)
expected.add_subfulfillment(expected_simple3)
cond = Output.generate([user_pub, user2_pub, expected_simple3], 1)
assert cond.fulfillment.to_dict() == expected.to_dict()
def test_generate_output_single_owner(user_pub):
expected = Ed25519Sha256(public_key=b58decode(user_pub))
cond = Output.generate([user_pub], 1)
assert cond.fulfillment.to_dict() == expected.to_dict()
def test_generate_output_single_owner_with_output(user_pub):
expected = Ed25519Sha256(public_key=b58decode(user_pub))
cond = Output.generate([expected], 1)
assert cond.fulfillment.to_dict() == expected.to_dict()
def test_generate_output_invalid_parameters(user_pub, user2_pub, user3_pub):
from transactions.common.transaction import Output
from transactions.common.exceptions import AmountError
with raises(ValueError):
Output.generate([], 1)
with raises(TypeError):
Output.generate("not a list", 1)
with raises(ValueError):
Output.generate([[user_pub, [user2_pub, [user3_pub]]]], 1)
with raises(ValueError):
Output.generate([[user_pub]], 1)
with raises(AmountError):
Output.generate([[user_pub]], -1)
def test_invalid_transaction_initialization(asset_definition):
with raises(ValueError):
Transaction(operation="invalid operation", asset=asset_definition)
with raises(TypeError):
Transaction(operation="CREATE", asset="invalid asset")
with raises(TypeError):
Transaction(operation="TRANSFER", asset={})
with raises(TypeError):
Transaction(operation="CREATE", asset=asset_definition, outputs="invalid outputs")
with raises(TypeError):
Transaction(operation="CREATE", asset=asset_definition, outputs=[], inputs="invalid inputs")
with raises(TypeError):
Transaction(
operation="CREATE", asset=asset_definition, outputs=[], inputs=[], metadata={"data": "invalid metadata"}
)
def test_create_default_asset_on_tx_initialization(asset_definition):
expected = {"data": None}
tx = Transaction(Transaction.CREATE, asset=expected)
asset = tx.asset
assert asset == expected
def test_transaction_serialization(user_input, user_output, data):
expected = {
"id": None,
"version": Transaction.VERSION,
# NOTE: This test assumes that Inputs and Outputs can
# successfully be serialized
"inputs": [user_input.to_dict()],
"outputs": [user_output.to_dict()],
"operation": Transaction.CREATE,
"metadata": None,
"asset": {
"data": data,
},
}
tx = Transaction(Transaction.CREATE, {"data": data}, [user_input], [user_output])
tx_dict = tx.to_dict()
assert tx_dict == expected
def test_transaction_deserialization(tri_state_transaction):
from .utils import validate_transaction_model
tx = Transaction.from_dict(tri_state_transaction)
validate_transaction_model(tx)
def test_invalid_input_initialization(user_input, user_pub):
from transactions.common.transaction import Input
with raises(TypeError):
Input(user_input, user_pub)
with raises(TypeError):
Input(user_input, tx_input="somethingthatiswrong")
def test_transaction_link_serialization():
tx_id = "a transaction id"
expected = {
"transaction_id": tx_id,
"output_index": 0,
}
tx_link = TransactionLink(tx_id, 0)
assert tx_link.to_dict() == expected
def test_transaction_link_serialization_with_empty_payload():
expected = None
tx_link = TransactionLink()
assert tx_link.to_dict() == expected
def test_transaction_link_deserialization():
tx_id = "a transaction id"
expected = TransactionLink(tx_id, 0)
tx_link = {
"transaction_id": tx_id,
"output_index": 0,
}
tx_link = TransactionLink.from_dict(tx_link)
assert tx_link == expected
def test_transaction_link_deserialization_with_empty_payload():
expected = TransactionLink()
tx_link = TransactionLink.from_dict(None)
assert tx_link == expected
def test_transaction_link_empty_to_uri():
expected = None
tx_link = TransactionLink().to_uri()
assert expected == tx_link
def test_transaction_link_to_uri():
expected = "path/transactions/abc/outputs/0"
tx_link = TransactionLink("abc", 0).to_uri("path")
assert expected == tx_link
def test_cast_transaction_link_to_boolean():
assert bool(TransactionLink()) is False
assert bool(TransactionLink("a", None)) is False
assert bool(TransactionLink(None, "b")) is False
assert bool(TransactionLink("a", "b")) is True
assert bool(TransactionLink(False, False)) is True
def test_transaction_link_eq():
assert TransactionLink(1, 2) == TransactionLink(1, 2)
assert TransactionLink(2, 2) != TransactionLink(1, 2)
assert TransactionLink(1, 1) != TransactionLink(1, 2)
assert TransactionLink(2, 1) != TransactionLink(1, 2)
def test_add_input_to_tx(user_input, asset_definition):
from .utils import validate_transaction_model
tx = Transaction(Transaction.CREATE, asset_definition, [], [])
tx.add_input(user_input)
assert len(tx.inputs) == 1
validate_transaction_model(tx)
def test_add_input_to_tx_with_invalid_parameters(asset_definition):
tx = Transaction(Transaction.CREATE, asset_definition)
with raises(TypeError):
tx.add_input("somewronginput")
def test_add_output_to_tx(user_output, user_input, asset_definition):
from .utils import validate_transaction_model
tx = Transaction(Transaction.CREATE, asset_definition, [user_input])
tx.add_output(user_output)
assert len(tx.outputs) == 1
validate_transaction_model(tx)
def test_add_output_to_tx_with_invalid_parameters(asset_definition):
tx = Transaction(Transaction.CREATE, asset_definition, [], [])
with raises(TypeError):
tx.add_output("somewronginput")
def test_sign_with_invalid_parameters(utx, user_priv):
with raises(TypeError):
utx.sign(None)
with raises(TypeError):
utx.sign(user_priv)
def test_validate_tx_simple_create_signature(user_input, user_output, user_priv, asset_definition):
from .utils import validate_transaction_model
tx = Transaction(Transaction.CREATE, asset_definition, [user_input], [user_output])
expected = deepcopy(user_output)
tx_dict = tx.to_dict()
tx_dict["inputs"][0]["fulfillment"] = None
serialized_tx = json.dumps(tx_dict, sort_keys=True, separators=(",", ":"), ensure_ascii=True)
message = sha3_256(serialized_tx.encode()).digest()
expected.fulfillment.sign(message, b58decode(user_priv))
tx.sign([user_priv])
assert tx.inputs[0].to_dict()["fulfillment"] == expected.fulfillment.serialize_uri()
assert tx.inputs_valid() is True
validate_transaction_model(tx)
def test_invoke_simple_signature_fulfillment_with_invalid_params(utx, user_input):
from transactions.common.exceptions import KeypairMismatchException
with raises(KeypairMismatchException):
invalid_key_pair = {"wrong_pub_key": "wrong_priv_key"}
utx._sign_simple_signature_fulfillment(user_input, "somemessage", invalid_key_pair)
def test_sign_threshold_with_invalid_params(utx, user_user2_threshold_input, user3_pub, user3_priv):
from transactions.common.exceptions import KeypairMismatchException
with raises(KeypairMismatchException):
utx._sign_threshold_signature_fulfillment(user_user2_threshold_input, "somemessage", {user3_pub: user3_priv})
with raises(KeypairMismatchException):
user_user2_threshold_input.owners_before = [58 * "a"]
utx._sign_threshold_signature_fulfillment(user_user2_threshold_input, "somemessage", None)
def test_validate_input_with_invalid_parameters(utx):
input_conditions = [out.fulfillment.condition_uri for out in utx.outputs]
tx_dict = utx.to_dict()
tx_serialized = Transaction._to_str(tx_dict)
valid = utx._input_valid(utx.inputs[0], tx_serialized, input_conditions[0])
assert not valid
def test_validate_tx_threshold_create_signature(
user_user2_threshold_input,
user_user2_threshold_output,
user_pub,
user2_pub,
user_priv,
user2_priv,
asset_definition,
):
from .utils import validate_transaction_model
tx = Transaction(Transaction.CREATE, asset_definition, [user_user2_threshold_input], [user_user2_threshold_output])
tx_dict = tx.to_dict()
tx_dict["inputs"][0]["fulfillment"] = None
serialized_tx = json.dumps(tx_dict, sort_keys=True, separators=(",", ":"), ensure_ascii=True)
message = sha3_256(serialized_tx.encode()).digest()
expected = deepcopy(user_user2_threshold_output)
expected.fulfillment.subconditions[0]["body"].sign(message, b58decode(user_priv))
expected.fulfillment.subconditions[1]["body"].sign(message, b58decode(user2_priv))
tx.sign([user_priv, user2_priv])
assert tx.inputs[0].to_dict()["fulfillment"] == expected.fulfillment.serialize_uri()
assert tx.inputs_valid() is True
validate_transaction_model(tx)
def test_validate_tx_threshold_duplicated_pk(user_pub, user_priv, asset_definition):
threshold = ThresholdSha256(threshold=2)
threshold.add_subfulfillment(Ed25519Sha256(public_key=b58decode(user_pub)))
threshold.add_subfulfillment(Ed25519Sha256(public_key=b58decode(user_pub)))
threshold_input = Input(threshold, [user_pub, user_pub])
threshold_output = Output(threshold, [user_pub, user_pub])
tx = Transaction(Transaction.CREATE, asset_definition, [threshold_input], [threshold_output])
tx_dict = tx.to_dict()
tx_dict["inputs"][0]["fulfillment"] = None
serialized_tx = json.dumps(tx_dict, sort_keys=True, separators=(",", ":"), ensure_ascii=True)
message = sha3_256(serialized_tx.encode()).digest()
expected = deepcopy(threshold_input)
expected.fulfillment.subconditions[0]["body"].sign(message, b58decode(user_priv))
expected.fulfillment.subconditions[1]["body"].sign(message, b58decode(user_priv))
tx.sign([user_priv, user_priv])
subconditions = tx.inputs[0].fulfillment.subconditions
expected_subconditions = expected.fulfillment.subconditions
assert subconditions[0]["body"].to_dict()["signature"] == expected_subconditions[0]["body"].to_dict()["signature"]
assert subconditions[1]["body"].to_dict()["signature"] == expected_subconditions[1]["body"].to_dict()["signature"]
assert tx.inputs[0].to_dict()["fulfillment"] == expected.fulfillment.serialize_uri()
assert tx.inputs_valid() is True
def test_multiple_input_validation_of_transfer_tx(
user_input, user_output, user_priv, user2_pub, user2_priv, user3_pub, user3_priv, asset_definition
):
from .utils import validate_transaction_model
tx = Transaction(Transaction.CREATE, asset_definition, [user_input], [user_output, deepcopy(user_output)])
tx.sign([user_priv])
inputs = [
Input(cond.fulfillment, cond.public_keys, TransactionLink(tx.id, index))
for index, cond in enumerate(tx.outputs)
]
outputs = [
Output(Ed25519Sha256(public_key=b58decode(user3_pub)), [user3_pub]),
Output(Ed25519Sha256(public_key=b58decode(user3_pub)), [user3_pub]),
]
transfer_tx = Transaction("TRANSFER", {"id": tx.id}, inputs, outputs)
transfer_tx = transfer_tx.sign([user_priv])
assert transfer_tx.inputs_valid(tx.outputs) is True
validate_transaction_model(tx)
def test_validate_inputs_of_transfer_tx_with_invalid_params(
transfer_tx, cond_uri, utx, user2_pub, user_priv, ffill_uri
):
invalid_out = Output(Ed25519Sha256.from_uri(ffill_uri), ["invalid"])
assert transfer_tx.inputs_valid([invalid_out]) is False
invalid_out = utx.outputs[0]
invalid_out.public_key = "invalid"
assert transfer_tx.inputs_valid([invalid_out]) is True
with raises(TypeError):
assert transfer_tx.inputs_valid(None) is False
with raises(AttributeError):
transfer_tx.inputs_valid("not a list")
with raises(ValueError):
transfer_tx.inputs_valid([])
with raises(TypeError):
transfer_tx.operation = "Operation that doesn't exist"
transfer_tx.inputs_valid([utx.outputs[0]])
def test_create_create_transaction_single_io(user_output, user_pub, data):
from .utils import validate_transaction_model
expected = {
"outputs": [user_output.to_dict()],
"metadata": data,
"asset": {
"data": data,
},
"inputs": [{"owners_before": [user_pub], "fulfillment": None, "fulfills": None}],
"operation": "CREATE",
"version": Transaction.VERSION,
}
tx = Create.generate([user_pub], [([user_pub], 1)], metadata=data, asset={"data": data})
tx_dict = tx.to_dict()
tx_dict["inputs"][0]["fulfillment"] = None
tx_dict.pop("id")
assert tx_dict == expected
validate_transaction_model(tx)
def test_validate_single_io_create_transaction(user_pub, user_priv, data, asset_definition):
tx = Create.generate([user_pub], [([user_pub], 1)], metadata=data)
tx = tx.sign([user_priv])
assert tx.inputs_valid() is True
def test_create_create_transaction_multiple_io(user_output, user2_output, user_pub, user2_pub, asset_definition):
# a fulfillment for a create transaction with multiple `owners_before`
# is a fulfillment for an implicit threshold condition with
# weight = len(owners_before)
input = Input.generate([user_pub, user2_pub]).to_dict()
expected = {
"outputs": [user_output.to_dict(), user2_output.to_dict()],
"metadata": "QmaozNR7DZHQK1ZcU9p7QdrshMvXqWK6gpu5rmrkPdT3L4",
"inputs": [input],
"operation": "CREATE",
"version": Transaction.VERSION,
}
tx = Create.generate(
[user_pub, user2_pub],
[([user_pub], 1), ([user2_pub], 1)],
metadata="QmaozNR7DZHQK1ZcU9p7QdrshMvXqWK6gpu5rmrkPdT3L4",
).to_dict()
tx.pop("id")
tx.pop("asset")
assert tx == expected
def test_validate_multiple_io_create_transaction(user_pub, user_priv, user2_pub, user2_priv, asset_definition):
from .utils import validate_transaction_model
tx = Create.generate(
[user_pub, user2_pub],
[([user_pub], 1), ([user2_pub], 1)],
metadata="QmaozNR7DZHQK1ZcU9p7QdrshMvXqWK6gpu5rmrkPdT3L4",
)
tx = tx.sign([user_priv, user2_priv])
assert tx.inputs_valid() is True
validate_transaction_model(tx)
def test_create_create_transaction_threshold(
user_pub, user2_pub, user3_pub, user_user2_threshold_output, user_user2_threshold_input, data
):
expected = {
"outputs": [user_user2_threshold_output.to_dict()],
"metadata": data,
"asset": {
"data": data,
},
"inputs": [
{
"owners_before": [
user_pub,
],
"fulfillment": None,
"fulfills": None,
},
],
"operation": "CREATE",
"version": Transaction.VERSION,
}
tx = Create.generate([user_pub], [([user_pub, user2_pub], 1)], metadata=data, asset={"data": data})
tx_dict = tx.to_dict()
tx_dict.pop("id")
tx_dict["inputs"][0]["fulfillment"] = None
assert tx_dict == expected
def test_validate_threshold_create_transaction(user_pub, user_priv, user2_pub, data, asset_definition):
from .utils import validate_transaction_model
tx = Create.generate([user_pub], [([user_pub, user2_pub], 1)], metadata=data)
tx = tx.sign([user_priv])
assert tx.inputs_valid() is True
validate_transaction_model(tx)
def test_create_create_transaction_with_invalid_parameters(user_pub):
with raises(TypeError):
Create.generate("not a list")
with raises(TypeError):
Create.generate([], "not a list")
with raises(ValueError):
Create.generate([], [user_pub])
with raises(ValueError):
Create.generate([user_pub], [])
with raises(ValueError):
Create.generate([user_pub], [user_pub])
with raises(ValueError):
Create.generate([user_pub], [([user_pub],)])
with raises(TypeError):
Create.generate([user_pub], [([user_pub], 1)], metadata={"data": "not a cid string or none"})
with raises(TypeError):
Create.generate([user_pub], [([user_pub], 1)], asset={"data": "not a dict or none"})
def test_outputs_to_inputs(tx):
inputs = tx.to_inputs([0])
assert len(inputs) == 1
input = inputs.pop()
assert input.owners_before == tx.outputs[0].public_keys
assert input.fulfillment == tx.outputs[0].fulfillment
assert input.fulfills.txid == tx.id
assert input.fulfills.output == 0
def test_create_transfer_transaction_single_io(tx, user_pub, user2_pub, user2_output, user_priv):
from .utils import validate_transaction_model
expected = {
"id": None,
"outputs": [user2_output.to_dict()],
"metadata": None,
"asset": {
"id": tx.id,
},
"inputs": [
{
"owners_before": [user_pub],
"fulfillment": None,
"fulfills": {"transaction_id": tx.id, "output_index": 0},
}
],
"operation": "TRANSFER",
"version": Transaction.VERSION,
}
inputs = tx.to_inputs([0])
transfer_tx = Transfer.generate(inputs, [([user2_pub], 1)], asset_id=tx.id)
transfer_tx = transfer_tx.sign([user_priv])
transfer_tx = transfer_tx.to_dict()
expected_input = deepcopy(inputs[0])
json_serialized_tx = json.dumps(expected, sort_keys=True, separators=(",", ":"), ensure_ascii=True)
message = sha3_256(json_serialized_tx.encode())
message.update(
"{}{}".format(
expected["inputs"][0]["fulfills"]["transaction_id"],
expected["inputs"][0]["fulfills"]["output_index"],
).encode()
)
expected_input.fulfillment.sign(message.digest(), b58decode(user_priv))
expected_ffill = expected_input.fulfillment.serialize_uri()
transfer_ffill = transfer_tx["inputs"][0]["fulfillment"]
assert transfer_ffill == expected_ffill
transfer_tx = Transaction.from_dict(transfer_tx)
assert transfer_tx.inputs_valid([tx.outputs[0]]) is True
validate_transaction_model(transfer_tx)
def test_create_transfer_transaction_multiple_io(
user_pub, user_priv, user2_pub, user2_priv, user3_pub, user2_output, asset_definition
):
tx = Create.generate(
[user_pub], [([user_pub], 1), ([user2_pub], 1)], metadata="QmaozNR7DZHQK1ZcU9p7QdrshMvXqWK6gpu5rmrkPdT3L4"
)
tx = tx.sign([user_priv])
expected = {
"outputs": [user2_output.to_dict(), user2_output.to_dict()],
"metadata": None,
"inputs": [
{
"owners_before": [user_pub],
"fulfillment": None,
"fulfills": {"transaction_id": tx.id, "output_index": 0},
},
{
"owners_before": [user2_pub],
"fulfillment": None,
"fulfills": {"transaction_id": tx.id, "output_index": 1},
},
],
"operation": "TRANSFER",
"version": Transaction.VERSION,
}
transfer_tx = Transfer.generate(tx.to_inputs(), [([user2_pub], 1), ([user2_pub], 1)], asset_id=tx.id)
transfer_tx = transfer_tx.sign([user_priv, user2_priv])
assert len(transfer_tx.inputs) == 2
assert len(transfer_tx.outputs) == 2
assert transfer_tx.inputs_valid(tx.outputs) is True
transfer_tx = transfer_tx.to_dict()
transfer_tx["inputs"][0]["fulfillment"] = None
transfer_tx["inputs"][1]["fulfillment"] = None
transfer_tx.pop("asset")
transfer_tx.pop("id")
assert expected == transfer_tx
def test_create_transfer_with_invalid_parameters(tx, user_pub):
with raises(TypeError):
Transfer.generate({}, [], tx.id)
with raises(ValueError):
Transfer.generate([], [], tx.id)
with raises(TypeError):
Transfer.generate(["fulfillment"], {}, tx.id)
with raises(ValueError):
Transfer.generate(["fulfillment"], [], tx.id)
with raises(ValueError):
Transfer.generate(["fulfillment"], [user_pub], tx.id)
with raises(ValueError):
Transfer.generate(["fulfillment"], [([user_pub],)], tx.id)
with raises(TypeError):
Transfer.generate(["fulfillment"], [([user_pub], 1)], tx.id, metadata={"data": "not a cid string or none"})
with raises(TypeError):
Transfer.generate(["fulfillment"], [([user_pub], 1)], ["not a string"])
def test_cant_add_empty_output():
tx = Transaction(Transaction.CREATE, None)
with raises(TypeError):
tx.add_output(None)
def test_cant_add_empty_input():
tx = Transaction(Transaction.CREATE, None)
with raises(TypeError):
tx.add_input(None)
def test_unfulfilled_transaction_serialized(unfulfilled_transaction):
tx_obj = Transaction.from_dict(unfulfilled_transaction)
expected = json.dumps(unfulfilled_transaction, sort_keys=True, separators=(",", ":"), ensure_ascii=True)
assert tx_obj.serialized == expected
def test_fulfilled_transaction_serialized(fulfilled_transaction):
tx_obj = Transaction.from_dict(fulfilled_transaction)
expected = json.dumps(fulfilled_transaction, sort_keys=True, separators=(",", ":"), ensure_ascii=True)
assert tx_obj.serialized == expected
def test_transaction_hash(fulfilled_transaction):
tx_obj = Transaction.from_dict(fulfilled_transaction)
assert tx_obj._id is None
assert tx_obj.id is None
thing_to_hash = json.dumps(fulfilled_transaction, sort_keys=True, separators=(",", ":"), ensure_ascii=True)
expected_hash_id = sha3_256(thing_to_hash.encode()).hexdigest()
tx_obj._hash()
assert tx_obj._id == expected_hash_id
assert tx_obj.id == expected_hash_id
def test_output_from_dict_invalid_amount(user_output):
from transactions.common.transaction import Output
from transactions.common.exceptions import AmountError
out = user_output.to_dict()
out["amount"] = "a"
with raises(AmountError):
Output.from_dict(out)
def test_unspent_outputs_property(merlin, alice, bob, carol):
tx = Create.generate(
[merlin.public_key],
[([alice.public_key], 1), ([bob.public_key], 2), ([carol.public_key], 3)],
asset={"data": "QmaozNR7DZHQK1ZcU9p7QdrshMvXqWK6gpu5rmrkPdT3L4"},
).sign([merlin.private_key])
unspent_outputs = list(tx.unspent_outputs)
assert len(unspent_outputs) == 3
assert all(utxo.transaction_id == tx.id for utxo in unspent_outputs)
assert all(utxo.asset_id == tx.id for utxo in unspent_outputs)
assert all(utxo.output_index == i for i, utxo in enumerate(unspent_outputs))
unspent_output_0 = unspent_outputs[0]
assert unspent_output_0.amount == 1
assert unspent_output_0.condition_uri == Ed25519Sha256(public_key=b58decode(alice.public_key)).condition_uri
unspent_output_1 = unspent_outputs[1]
assert unspent_output_1.amount == 2
assert unspent_output_1.condition_uri == Ed25519Sha256(public_key=b58decode(bob.public_key)).condition_uri
unspent_output_2 = unspent_outputs[2]
assert unspent_output_2.amount == 3
assert unspent_output_2.condition_uri == Ed25519Sha256(public_key=b58decode(carol.public_key)).condition_uri
def test_spent_outputs_property(signed_transfer_tx):
spent_outputs = list(signed_transfer_tx.spent_outputs)
tx = signed_transfer_tx.to_dict()
assert len(spent_outputs) == 1
spent_output = spent_outputs[0]
assert spent_output["transaction_id"] == tx["inputs"][0]["fulfills"]["transaction_id"]
assert spent_output["output_index"] == tx["inputs"][0]["fulfills"]["output_index"]
# assert spent_output._asdict() == tx['inputs'][0]['fulfills']

View File

@ -1,15 +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
def validate_transaction_model(tx):
from transactions.common.transaction import Transaction
from transactions.common.schema import validate_transaction_schema
tx_dict = tx.to_dict()
# Check that a transaction is valid by re-serializing it
# And calling validate_transaction_schema
validate_transaction_schema(tx_dict)
Transaction.from_dict(tx_dict)