Browse Source

Merge JoinMarket-Org/joinmarket-clientserver#1809: fix(deps): bencode library

091ce515e9 fix(deps): bencode library (roshii)

Pull request description:

  Migrate from abandoned `bencoder.pyx` to `fastbencode`.
  Update minimum Python version requirement to 3.9 (with 3.8 effectively eol since a year)

  Python 3.13 compatibility will require other dependencies bumping such as `twisted` in https://github.com/JoinMarket-Org/joinmarket-clientserver/pull/1732

  Closes: https://github.com/JoinMarket-Org/joinmarket-clientserver/issues/1805

Top commit has no ACKs.

Tree-SHA512: b498fdead26b0f6b7b97448b3eee0a5b09ec379a63dbfa69b2b1c1d7e02f30e20ab15c52f7c4223f68189e2d244bf49d04dde4dbb2ae2f0cde865d4c5e0dc6da
master
merge-script 2 months ago
parent
commit
94660e7d24
No known key found for this signature in database
GPG Key ID: 33E472FE870C7E5D
  1. 2
      .github/workflows/unittests.yml
  2. 4
      pyproject.toml
  3. 18
      src/jmclient/storage.py
  4. 29
      test/jmclient/test_storage.py

2
.github/workflows/unittests.yml

@ -9,7 +9,7 @@ jobs:
strategy: strategy:
matrix: matrix:
os: [macos-13, ubuntu-latest] os: [macos-13, ubuntu-latest]
python-version: ["3.8", "3.12"] python-version: ["3.9", "3.12"]
bitcoind-version: ["29.2"] bitcoind-version: ["29.2"]
steps: steps:

4
pyproject.toml

@ -7,7 +7,7 @@ name = "joinmarket"
version = "0.9.12dev" version = "0.9.12dev"
description = "Joinmarket client library for Bitcoin coinjoins" description = "Joinmarket client library for Bitcoin coinjoins"
readme = "README.md" readme = "README.md"
requires-python = ">=3.8,<3.13" requires-python = ">=3.9,<3.13"
license = {file = "LICENSE"} license = {file = "LICENSE"}
dependencies = [ dependencies = [
"chromalog==1.0.5", "chromalog==1.0.5",
@ -24,7 +24,7 @@ jmbitcoin = [
jmclient = [ jmclient = [
"argon2_cffi==21.3.0", "argon2_cffi==21.3.0",
"autobahn==20.12.3", "autobahn==20.12.3",
"bencoder.pyx==3.0.1", "fastbencode==0.3.6",
"klein==20.6.0", "klein==20.6.0",
"mnemonic==0.20", "mnemonic==0.20",
"pyjwt==2.4.0", "pyjwt==2.4.0",

18
src/jmclient/storage.py

@ -1,10 +1,14 @@
import atexit
import os import os
import shutil import shutil
import atexit
import bencoder
from hashlib import sha256 from hashlib import sha256
from typing import Any
from argon2 import low_level from argon2 import low_level
from jmbase import aes_cbc_encrypt, aes_cbc_decrypt from fastbencode import bdecode, bencode_utf8
from jmbase import aes_cbc_decrypt, aes_cbc_encrypt
from .support import get_random_bytes from .support import get_random_bytes
@ -223,12 +227,12 @@ class Storage(object):
return self.path return self.path
@staticmethod @staticmethod
def _serialize(data): def _serialize(data: Any) -> bytes:
return bencoder.bencode(data) return bencode_utf8(data)
@staticmethod @staticmethod
def _deserialize(data): def _deserialize(data: bytes) -> Any:
return bencoder.bdecode(data) return bdecode(data)
def _encrypt_file(self, data): def _encrypt_file(self, data):
if not self.is_encrypted(): if not self.is_encrypted():

29
test/jmclient/test_storage.py

@ -1,4 +1,3 @@
from jmclient import storage from jmclient import storage
import pytest import pytest
@ -81,6 +80,7 @@ def test_storage_invalid():
MockStorage(b'garbagefile', __file__, b'password') MockStorage(b'garbagefile', __file__, b'password')
pytest.fail("Non-wallet file, encrypted") pytest.fail("Non-wallet file, encrypted")
def test_storage_readonly(): def test_storage_readonly():
s = MockStorage(None, 'nonexistant', b'password', create=True) s = MockStorage(None, 'nonexistant', b'password', create=True)
s = MockStorage(s.file_data, __file__, b'password', read_only=True) s = MockStorage(s.file_data, __file__, b'password', read_only=True)
@ -136,3 +136,30 @@ def test_storage_lock(tmpdir):
s._create_lock() s._create_lock()
pytest.fail("It should not be possible to re-create a lock") pytest.fail("It should not be possible to re-create a lock")
testdata = {
b"bytes_key": b"bytes_value",
b"int_key": 42,
b"list_key": [b"a", b"b", b"c", 1, 2, 3],
b"dict_key": {b"nested": b"data", b"number": 999},
}
@pytest.mark.parametrize(
"test_data, expected_out",
[
(testdata, testdata),
({b"dict_key": {b"utf": "value"}}, {b"dict_key": {b"utf": b"value"}}),
],
)
def test_bencode_roundtrip_consistency(test_data, expected_out):
s = MockStorage(None, "nonexistent", None, create=True)
serialized1 = s._serialize(test_data)
deserialized1 = s._deserialize(serialized1)
serialized2 = s._serialize(deserialized1)
deserialized2 = s._deserialize(serialized2)
assert serialized1 == serialized2
assert deserialized1 == deserialized2 == expected_out

Loading…
Cancel
Save