mirror of
https://github.com/planetmint/planetmint.git
synced 2025-11-24 14:35:45 +00:00
removed duplicate transactions test suite
Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>
This commit is contained in:
parent
ccf55d1b8d
commit
ebaa3e1103
@ -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
|
|
||||||
@ -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
|
|
||||||
@ -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)
|
|
||||||
@ -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']
|
|
||||||
@ -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)
|
|
||||||
Loading…
x
Reference in New Issue
Block a user