Skip to content

Commit

Permalink
Merge branch 'chang' of https://github.com/python-cardano/pycardano i…
Browse files Browse the repository at this point in the history
…nto bugfix/datum
  • Loading branch information
theeldermillenial committed Sep 18, 2024
2 parents 32a11cc + 7727c72 commit 3fd91d0
Show file tree
Hide file tree
Showing 22 changed files with 1,000 additions and 292 deletions.
9 changes: 8 additions & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -68,14 +68,21 @@ jobs:
run: |
cd integration-test && ./run_tests.sh
- name: "Upload coverage to Codecov"
if: ${{ matrix.python-version == '3.11' }}
uses: codecov/codecov-action@v4
with:
fail_ci_if_error: true
token: ${{ secrets.CODECOV_TOKEN }}

- name: Dump docker logs
if: failure()
run: |
cd integration-test && docker compose logs --no-color > integration-test.log
- name: Upload integration-test.log
if: failure()
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v4
with:
name: integration-test.log
path: integration-test/integration-test.log
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ cov_html
docs/build
dist
.mypy_cache
coverage.xml

# IDE
.idea
Expand Down
52 changes: 32 additions & 20 deletions docs/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,35 +1,39 @@
alabaster==0.7.13 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
annotated-types==0.7.0 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
asn1crypto==1.5.1 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
attrs==23.2.0 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
babel==2.15.0 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
black==24.4.2 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
attrs==24.2.0 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
babel==2.16.0 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
black==24.8.0 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
blinker==1.8.2 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
blockfrost-python==0.5.3 ; python_full_version >= "3.8.1" and python_version < "4"
cachetools==5.3.3 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
cbor2==5.6.3 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
certifi==2024.2.2 ; python_full_version >= "3.8.1" and python_version < "4"
cachetools==5.5.0 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
cardano-tools==2.1.0 ; python_full_version >= "3.8.1" and python_version < "4.0"
cbor2==5.6.4 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
certifi==2024.8.30 ; python_full_version >= "3.8.1" and python_version < "4"
certvalidator==0.11.1 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
cffi==1.16.0 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
cffi==1.17.0 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
charset-normalizer==3.3.2 ; python_full_version >= "3.8.1" and python_version < "4"
click==8.1.7 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
colorama==0.4.6 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0" and (sys_platform == "win32" or platform_system == "Windows")
coloredlogs==15.0.1 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
cose==0.9.dev8 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
coverage[toml]==7.5.3 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
cryptography==42.0.7 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
coverage[toml]==7.6.1 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
cryptography==43.0.0 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
decorator==5.1.1 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
docker==7.1.0 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
docutils==0.19 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
ecdsa==0.19.0 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
ecpy==1.2.5 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
exceptiongroup==1.2.1 ; python_full_version >= "3.8.1" and python_version < "3.11"
exceptiongroup==1.2.2 ; python_full_version >= "3.8.1" and python_version < "3.11"
execnet==2.1.1 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
flake8==7.0.0 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
flake8==7.1.1 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
flask==2.3.3 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
frozendict==2.4.4 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
frozenlist==1.4.1 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
idna==3.7 ; python_full_version >= "3.8.1" and python_version < "4"
humanfriendly==10.0 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
idna==3.8 ; python_full_version >= "3.8.1" and python_version < "4"
imagesize==1.4.1 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
importlib-metadata==7.1.0 ; python_full_version >= "3.8.1" and python_version < "3.10"
importlib-metadata==8.4.0 ; python_full_version >= "3.8.1" and python_version < "3.10"
iniconfig==2.0.0 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
isort==5.13.2 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
itsdangerous==2.2.0 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
Expand All @@ -39,21 +43,28 @@ mccabe==0.7.0 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
mnemonic==0.21 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
mypy-extensions==1.0.0 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
mypy==1.4.1 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
ogmios @ git+https://gitlab.com/viperscience/ogmios-python.git@0b713e78839341d92828340a5552ef8dd1c6224b ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
orjson==3.10.7 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
oscrypto==1.3.0 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
packaging==24.0 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
packaging==24.1 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
pathspec==0.12.1 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
pexpect==4.9.0 ; python_full_version >= "3.8.1" and python_version < "4.0"
platformdirs==4.2.2 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
pluggy==1.5.0 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
pprintpp==0.4.0 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
ptyprocess==0.7.0 ; python_full_version >= "3.8.1" and python_version < "4.0"
py==1.11.0 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
pycodestyle==2.11.1 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
pycodestyle==2.12.1 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
pycparser==2.22 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
pydantic-core==2.20.1 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
pydantic==2.8.2 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
pyflakes==3.2.0 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
pygments==2.18.0 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
pynacl==1.5.0 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
pyreadline3==3.4.1 ; sys_platform == "win32" and python_full_version >= "3.8.1" and python_full_version < "4.0.0"
pytest-cov==5.0.0 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
pytest-xdist==3.6.1 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
pytest==8.2.1 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
pytest==8.3.2 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
pytz==2024.1 ; python_full_version >= "3.8.1" and python_version < "3.9"
pywin32==306 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0" and sys_platform == "win32"
requests==2.32.3 ; python_full_version >= "3.8.1" and python_version < "4"
Expand All @@ -74,8 +85,9 @@ sphinxcontrib-serializinghtml==1.1.5 ; python_full_version >= "3.8.1" and python
tomli==2.0.1 ; python_full_version >= "3.8.1" and python_full_version <= "3.11.0a6"
typeguard==4.3.0 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
types-requests==2.31.0.10 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
typing-extensions==4.12.0 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
urllib3==2.2.1 ; python_full_version >= "3.8.1" and python_version < "4"
typing-extensions==4.12.2 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
urllib3==2.2.2 ; python_full_version >= "3.8.1" and python_version < "4"
websocket-client==1.8.0 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
werkzeug==3.0.3 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
zipp==3.19.1 ; python_full_version >= "3.8.1" and python_version < "3.10"
websockets==13.0.1 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
werkzeug==3.0.4 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
zipp==3.20.1 ; python_full_version >= "3.8.1" and python_version < "3.10"
7 changes: 6 additions & 1 deletion docs/source/api/pycardano.backend.base.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,12 @@ Backend
:undoc-members:
:show-inheritance:

.. automodule:: pycardano.backend.ogmios
.. automodule:: pycardano.backend.ogmios_v6
:members:
:undoc-members:
:show-inheritance:

.. automodule:: pycardano.backend.ogmios_v5
:members:
:undoc-members:
:show-inheritance:
2 changes: 1 addition & 1 deletion integration-test/run_tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ export POOL_ID=$(cat "$ROOT"/keys/pool/pool.id)
# Wait for stake pool to start producing blocks
sleep 30

poetry run pytest -m "not (CardanoCLI)" -s -vv -n 4 "$ROOT"/test
poetry run pytest -m "not (CardanoCLI)" -s -vv -n 4 "$ROOT"/test --cov=pycardano --cov-config=../.coveragerc --cov-report=xml:../coverage.xml

# Cleanup
docker compose -f docker-compose-chang.yml down --volumes --remove-orphans
205 changes: 205 additions & 0 deletions integration-test/test/test_zero_empty_asset.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
import pathlib
import tempfile

import pytest
from retry import retry

from pycardano import *

from .base import TEST_RETRIES, TestBase


class TestZeroEmptyAsset(TestBase):
@retry(tries=TEST_RETRIES, backoff=1.5, delay=6, jitter=(0, 4))
@pytest.mark.post_chang
def test_submit_zero_and_empty(self):
address = Address(self.payment_vkey.hash(), network=self.NETWORK)

# Load payment keys or create them if they don't exist
def load_or_create_key_pair(base_dir, base_name):
skey_path = base_dir / f"{base_name}.skey"
vkey_path = base_dir / f"{base_name}.vkey"

if skey_path.exists():
skey = PaymentSigningKey.load(str(skey_path))
vkey = PaymentVerificationKey.from_signing_key(skey)
else:
key_pair = PaymentKeyPair.generate()
key_pair.signing_key.save(str(skey_path))
key_pair.verification_key.save(str(vkey_path))
skey = key_pair.signing_key
vkey = key_pair.verification_key
return skey, vkey

tempdir = tempfile.TemporaryDirectory()
PROJECT_ROOT = tempdir.name

root = pathlib.Path(PROJECT_ROOT)
# Create the directory if it doesn't exist
root.mkdir(parents=True, exist_ok=True)
"""Generate keys"""
key_dir = root / "keys"
key_dir.mkdir(exist_ok=True)

# Generate policy keys, which will be used when minting NFT
policy_skey, policy_vkey = load_or_create_key_pair(key_dir, "policy")

"""Create policy"""
# A policy that requires a signature from the policy key we generated above
pub_key_policy_1 = ScriptPubkey(policy_vkey.hash())

# A policy that requires a signature from the extended payment key
pub_key_policy_2 = ScriptPubkey(self.extended_payment_vkey.hash())

# Combine two policies using ScriptAll policy
policy = ScriptAll([pub_key_policy_1, pub_key_policy_2])

# Calculate policy ID, which is the hash of the policy
policy_id = policy.hash()

"""Define NFT"""
my_nft = MultiAsset.from_primitive(
{
policy_id.payload: {
b"MY_NFT_1": 1, # Name of our NFT1 # Quantity of this NFT
b"MY_NFT_2": 1, # Name of our NFT2 # Quantity of this NFT
}
}
)

native_scripts = [policy]

"""Create metadata"""
# We need to create a metadata for our NFTs, so they could be displayed correctly by blockchain explorer
metadata = {
721: { # 721 refers to the metadata label registered for NFT standard here:
# https://github.com/cardano-foundation/CIPs/blob/master/CIP-0010/registry.json#L14-L17
policy_id.payload.hex(): {
"MY_NFT_1": {
"description": "This is my first NFT thanks to PyCardano",
"name": "PyCardano NFT example token 1",
"id": 1,
"image": "ipfs://QmRhTTbUrPYEw3mJGGhQqQST9k86v1DPBiTTWJGKDJsVFw",
},
"MY_NFT_2": {
"description": "This is my second NFT thanks to PyCardano",
"name": "PyCardano NFT example token 2",
"id": 2,
"image": "ipfs://QmRhTTbUrPYEw3mJGGhQqQST9k86v1DPBiTTWJGKDJsVFw",
},
}
}
}

# Place metadata in AuxiliaryData, the format acceptable by a transaction.
auxiliary_data = AuxiliaryData(AlonzoMetadata(metadata=Metadata(metadata)))

"""Build mint transaction"""

# Create a transaction builder
builder = TransactionBuilder(self.chain_context)

# Add our own address as the input address
builder.add_input_address(address)

# Set nft we want to mint
builder.mint = my_nft

# Set native script
builder.native_scripts = native_scripts

# Set transaction metadata
builder.auxiliary_data = auxiliary_data

# Calculate the minimum amount of lovelace that need to hold the NFT we are going to mint
min_val = min_lovelace_pre_alonzo(Value(0, my_nft), self.chain_context)

# Send the NFT to our own address
nft_output = TransactionOutput(address, Value(min_val, my_nft))
builder.add_output(nft_output)

# Build and sign transaction
signed_tx = builder.build_and_sign(
[self.payment_skey, self.extended_payment_skey, policy_skey], address
)

print("############### Transaction created ###############")
print(signed_tx)
print(signed_tx.to_cbor_hex())

# Submit signed transaction to the network
print("############### Submitting transaction ###############")
self.chain_context.submit_tx(signed_tx)

self.assert_output(address, nft_output)

"""Build transaction with 0 nft"""

# Create a transaction builder
builder = TransactionBuilder(self.chain_context)

# Add our own address as the input address
builder.add_input_address(address)

# Calculate the minimum amount of lovelace that need to hold the NFT we are going to mint
min_val = min_lovelace_pre_alonzo(Value(0), self.chain_context)

# Send the NFT to our own address
nft_output = TransactionOutput(
address,
Value(
min_val,
MultiAsset.from_primitive(
{policy_vkey.hash().payload: {b"MY_NFT_1": 0}}
),
),
)
builder.add_output(nft_output)

# Build and sign transaction
signed_tx = builder.build_and_sign(
[self.payment_skey, self.extended_payment_skey], address
)

print("############### Transaction created ###############")
print(signed_tx)
print(signed_tx.to_cbor_hex())

# Submit signed transaction to the network
print("############### Submitting transaction ###############")
self.chain_context.submit_tx(signed_tx)

self.assert_output(address, nft_output)

"""Build transaction with empty multi-asset"""

# Create a transaction builder
builder = TransactionBuilder(self.chain_context)

# Add our own address as the input address
builder.add_input_address(address)

# Calculate the minimum amount of lovelace that need to hold the NFT we are going to mint
min_val = min_lovelace_pre_alonzo(Value(0), self.chain_context)

# Send the NFT to our own address
nft_output = TransactionOutput(
address,
Value(min_val, MultiAsset.from_primitive({policy_vkey.hash().payload: {}})),
)
builder.add_output(nft_output)

# Build and sign transaction
signed_tx = builder.build_and_sign(
[self.payment_skey, self.extended_payment_skey], address
)

print("############### Transaction created ###############")
print(signed_tx)
print(signed_tx.to_cbor_hex())

# Submit signed transaction to the network
print("############### Submitting transaction ###############")
self.chain_context.submit_tx(signed_tx)

self.assert_output(address, nft_output)
Loading

0 comments on commit 3fd91d0

Please sign in to comment.