Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,6 @@ jobs:
- name: Install dependencies
run: |
poetry install
- name: Ensure pure cbor2 is installed
run: |
make ensure-pure-cbor2
- name: Run unit tests
run: |
poetry run pytest --doctest-modules --ignore=examples --cov=pycardano --cov-config=.coveragerc --cov-report=xml
Expand Down
3 changes: 0 additions & 3 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,6 @@ jobs:
- name: Install dependencies
run: |
poetry install
- name: Ensure pure cbor2 is installed
run: |
make ensure-pure-cbor2
- name: Lint with flake8
run: |
poetry run flake8 pycardano
Expand Down
23 changes: 4 additions & 19 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -23,25 +23,10 @@ export PRINT_HELP_PYSCRIPT

BROWSER := poetry run python -c "$$BROWSER_PYSCRIPT"

ensure-pure-cbor2: ## ensures cbor2 is installed with pure Python implementation
@poetry run python -c "from importlib.metadata import version; \
print(version('cbor2'))" > .cbor2_version
@poetry run python -c "import cbor2, inspect; \
print('Checking cbor2 implementation...'); \
decoder_path = inspect.getfile(cbor2.CBORDecoder); \
using_c_ext = decoder_path.endswith('.so'); \
print(f'Implementation path: {decoder_path}'); \
print(f'Using C extension: {using_c_ext}'); \
exit(1 if using_c_ext else 0)" || \
(echo "Reinstalling cbor2 with pure Python implementation..." && \
poetry run pip uninstall -y cbor2 && \
CBOR2_BUILD_C_EXTENSION=0 poetry run pip install --no-binary cbor2 "cbor2==$$(cat .cbor2_version)" --force-reinstall && \
rm .cbor2_version)

help:
@python -c "$$PRINT_HELP_PYSCRIPT" < $(MAKEFILE_LIST)

cov: ensure-pure-cbor2 ## check code coverage
cov: ## check code coverage
poetry run pytest -n 4 --cov pycardano

cov-html: cov ## check code coverage and generate an html report
Expand Down Expand Up @@ -69,7 +54,7 @@ clean-test: ## remove test and coverage artifacts
rm -fr cov_html/
rm -fr .pytest_cache

test: ensure-pure-cbor2 ## runs tests
test: ## runs tests
poetry run pytest -vv -n 4

test-integration: ## runs integration tests
Expand All @@ -78,7 +63,7 @@ test-integration: ## runs integration tests
test-single: ## runs tests with "single" markers
poetry run pytest -s -vv -m single

qa: ensure-pure-cbor2 ## runs static analyses
qa: ## runs static analyses
poetry run flake8 pycardano
poetry run mypy --install-types --non-interactive pycardano
poetry run black --check .
Expand All @@ -92,6 +77,6 @@ docs: ## build the documentation
poetry run sphinx-build docs/source docs/build/html
$(BROWSER) docs/build/html/index.html

release: clean qa test format ensure-pure-cbor2 ## build dist version and release to pypi
release: clean qa test format ## build dist version and release to pypi
poetry build
poetry publish
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,13 @@ Install the library using [pip](https://pip.pypa.io/en/stable/):

`pip install pycardano`

#### Install cbor2 pure python implementation (Optional)
#### cbor2
[cbor2](https://github.com/agronholm/cbor2/tree/master) is a dependency of pycardano. It is used to encode and decode CBOR data.
It has two implementations: one is pure Python and the other is C, which is installed by default. The C implementation is faster, but it is less flexible than the pure Python implementation.

For some users, the C implementation may not work properly when deserializing cbor data. For example, the order of inputs of a transaction isn't guaranteed to be the same as the order of inputs in the original transaction (details could be found in [this issue](https://github.com/Python-Cardano/pycardano/issues/311)). This would result in a different transaction hash when the transaction is serialized again. For users who encounter this issue, we recommend to use the pure Python implementation of cbor2. You can do so by running [ensure_pure_cbor2.sh](./ensure_pure_cbor2.sh), which inspect the cbor2 installed in the running environment and force install pure python implementation if necessary.
For some users, the C implementation may not work properly when deserializing cbor data. For example, the order of inputs of a transaction isn't guaranteed to be the same as the order of inputs in the original transaction (details could be found in [this issue](https://github.com/Python-Cardano/pycardano/issues/311)). This would result in a different transaction hash when the transaction is serialized again.

To solve this problem, a fork of cbor2 is created at [cbor2pure](https://github.com/cffls/cbor2pure). This fork removes C extension and only uses pure python for cbor decoding. By default, for correctness, pycardano uses cbor2pure in decoding. However, if speed is preferred over accuracy, users can set `CBOR_C_EXTENSION=1` in their environment, and the default C extension would be used instead.

```bash
ensure_pure_cbor2.sh
Expand Down
38 changes: 0 additions & 38 deletions ensure_pure_cbor2.sh

This file was deleted.

1 change: 0 additions & 1 deletion integration-test/run_tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ set -o pipefail
ROOT=$(pwd)

poetry install -C ..
make ensure-pure-cbor2 -f ../Makefile
#poetry run pip install ogmios

##########
Expand Down
17 changes: 16 additions & 1 deletion poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion pycardano/address.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@
from typing import Optional, Type, Union

import base58
import cbor2
from cbor2 import CBORTag
from typing_extensions import override

from pycardano.cbor import cbor2
from pycardano.crypto.bech32 import decode, encode
from pycardano.exception import (
DecodingException,
Expand Down
2 changes: 1 addition & 1 deletion pycardano/backend/blockfrost.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
from fractions import Fraction
from typing import Dict, List, Optional, Union

import cbor2
from blockfrost import ApiError, ApiUrls, BlockFrostApi
from blockfrost.utils import Namespace

Expand All @@ -16,6 +15,7 @@
GenesisParameters,
ProtocolParameters,
)
from pycardano.cbor import cbor2
from pycardano.exception import TransactionFailedException
from pycardano.hash import SCRIPT_HASH_SIZE, DatumHash, ScriptHash
from pycardano.nativescript import NativeScript
Expand Down
2 changes: 1 addition & 1 deletion pycardano/backend/cardano_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
from pathlib import Path
from typing import Dict, List, Optional, Union

import cbor2
import docker
from cachetools import Cache, LRUCache, TTLCache, func
from docker.errors import APIError
Expand All @@ -24,6 +23,7 @@
GenesisParameters,
ProtocolParameters,
)
from pycardano.cbor import cbor2
from pycardano.exception import (
CardanoCliError,
PyCardanoException,
Expand Down
14 changes: 14 additions & 0 deletions pycardano/cbor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
"""
Conditional cbor2 import module.

This module provides a centralized location for importing cbor2,
with support for both the C extension (cbor2) and pure Python (cbor2pure) versions.
Set the environment variable CBOR_C_EXTENSION=1 to use the C extension.
"""

import os

if os.getenv("CBOR_C_EXTENSION", "0") == "1":
import cbor2 # noqa: F401
else:
import cbor2pure as cbor2 # type: ignore # noqa: F401
2 changes: 1 addition & 1 deletion pycardano/plutus.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@
from hashlib import sha256
from typing import Any, List, Optional, Type, Union

import cbor2
from cbor2 import CBORTag
from nacl.encoding import RawEncoder
from nacl.hash import blake2b
from typeguard import typechecked

from pycardano.cbor import cbor2
from pycardano.exception import DeserializeException, InvalidArgumentException
from pycardano.hash import DATUM_HASH_SIZE, SCRIPT_HASH_SIZE, DatumHash, ScriptHash
from pycardano.nativescript import NativeScript
Expand Down
3 changes: 1 addition & 2 deletions pycardano/serialization.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,7 @@
get_type_hints,
)

import cbor2

from pycardano.cbor import cbor2
from pycardano.logging import logger

# Remove the semantic decoder for 258 (CBOR tag for set) as we care about the order of elements
Expand Down
4 changes: 2 additions & 2 deletions pycardano/transaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@
from dataclasses import dataclass, field
from typing import Any, Callable, List, Optional, Type, Union

import cbor2
from cbor2 import CBORTag
from nacl.encoding import RawEncoder
from nacl.hash import blake2b
from pprintpp import pformat

from pycardano.address import Address
from pycardano.cbor import cbor2
from pycardano.certificate import Certificate
from pycardano.exception import InvalidDataException
from pycardano.governance import ProposalProcedure, VotingProcedures
Expand Down Expand Up @@ -105,7 +105,7 @@ def __add__(self, other: Asset) -> Asset:

def __iadd__(self, other: Asset) -> Asset:
new_item = self + other
self.data = new_item.data
self.data = new_item.data # type: ignore[has-type]
return self.normalize()

def __sub__(self, other: Asset) -> Asset:
Expand Down
2 changes: 1 addition & 1 deletion pycardano/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@
import sys
from typing import Dict, List, Optional, Tuple, Union

import cbor2
from nacl.encoding import RawEncoder
from nacl.hash import blake2b

from pycardano.backend.base import ChainContext
from pycardano.cbor import cbor2
from pycardano.hash import SCRIPT_DATA_HASH_SIZE, SCRIPT_HASH_SIZE, ScriptDataHash
from pycardano.plutus import COST_MODELS, CostModels, Datum, RedeemerMap, Redeemers
from pycardano.serialization import NonEmptyOrderedSet, default_encoder
Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ ogmios = ">=1.4.2"
requests = ">=2.32.3"
websockets = ">=13.0"
base58 = ">=2.1.0"
cbor2pure = ">=5.7.2"

[tool.poetry.group.dev.dependencies]
pytest = ">=8.2.0"
Expand Down
2 changes: 1 addition & 1 deletion test/pycardano/test_serialization.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
get_origin,
)

import cbor2
import pytest
from cbor2 import CBORTag
from frozenlist import FrozenList
Expand All @@ -35,6 +34,7 @@
VerificationKey,
VerificationKeyWitness,
)
from pycardano.cbor import cbor2
from pycardano.exception import (
DeserializeException,
InvalidKeyTypeException,
Expand Down
Loading