major refactoring inlcuding renaming

This commit is contained in:
Florian Förster 2025-12-16 13:43:48 +01:00
parent 05fa8548b0
commit d155d1afad
16 changed files with 27004 additions and 1771 deletions

99
pdm.lock generated
View File

@ -5,7 +5,7 @@
groups = ["default", "dev", "lint", "nb", "tests"]
strategy = ["inherit_metadata"]
lock_version = "4.5.0"
content_hash = "sha256:c0680aa00cf7474d542ab72daaea39fe9f3c23b3b68f724f49da67b2d74ff588"
content_hash = "sha256:813bab76055ae1081c8e05c3f2d6b429ac1caf20ae5353aa06c90e70a17f75cb"
[[metadata.targets]]
requires_python = ">=3.11"
@ -831,6 +831,56 @@ files = [
{file = "fqdn-1.5.1.tar.gz", hash = "sha256:105ed3677e767fb5ca086a0c1f4bb66ebc3c100be518f0e0d755d9eae164d89f"},
]
[[package]]
name = "greenlet"
version = "3.3.0"
requires_python = ">=3.10"
summary = "Lightweight in-process concurrent programming"
groups = ["default"]
marker = "platform_machine == \"win32\" or platform_machine == \"WIN32\" or platform_machine == \"AMD64\" or platform_machine == \"amd64\" or platform_machine == \"x86_64\" or platform_machine == \"ppc64le\" or platform_machine == \"aarch64\""
files = [
{file = "greenlet-3.3.0-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:e29f3018580e8412d6aaf5641bb7745d38c85228dacf51a73bd4e26ddf2a6a8e"},
{file = "greenlet-3.3.0-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a687205fb22794e838f947e2194c0566d3812966b41c78709554aa883183fb62"},
{file = "greenlet-3.3.0-cp311-cp311-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4243050a88ba61842186cb9e63c7dfa677ec146160b0efd73b855a3d9c7fcf32"},
{file = "greenlet-3.3.0-cp311-cp311-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:670d0f94cd302d81796e37299bcd04b95d62403883b24225c6b5271466612f45"},
{file = "greenlet-3.3.0-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6cb3a8ec3db4a3b0eb8a3c25436c2d49e3505821802074969db017b87bc6a948"},
{file = "greenlet-3.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2de5a0b09eab81fc6a382791b995b1ccf2b172a9fec934747a7a23d2ff291794"},
{file = "greenlet-3.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4449a736606bd30f27f8e1ff4678ee193bc47f6ca810d705981cfffd6ce0d8c5"},
{file = "greenlet-3.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:7652ee180d16d447a683c04e4c5f6441bae7ba7b17ffd9f6b3aff4605e9e6f71"},
{file = "greenlet-3.3.0-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:b01548f6e0b9e9784a2c99c5651e5dc89ffcbe870bc5fb2e5ef864e9cc6b5dcb"},
{file = "greenlet-3.3.0-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:349345b770dc88f81506c6861d22a6ccd422207829d2c854ae2af8025af303e3"},
{file = "greenlet-3.3.0-cp312-cp312-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e8e18ed6995e9e2c0b4ed264d2cf89260ab3ac7e13555b8032b25a74c6d18655"},
{file = "greenlet-3.3.0-cp312-cp312-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c024b1e5696626890038e34f76140ed1daf858e37496d33f2af57f06189e70d7"},
{file = "greenlet-3.3.0-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:047ab3df20ede6a57c35c14bf5200fcf04039d50f908270d3f9a7a82064f543b"},
{file = "greenlet-3.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2d9ad37fc657b1102ec880e637cccf20191581f75c64087a549e66c57e1ceb53"},
{file = "greenlet-3.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:83cd0e36932e0e7f36a64b732a6f60c2fc2df28c351bae79fbaf4f8092fe7614"},
{file = "greenlet-3.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:a7a34b13d43a6b78abf828a6d0e87d3385680eaf830cd60d20d52f249faabf39"},
{file = "greenlet-3.3.0-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:a1e41a81c7e2825822f4e068c48cb2196002362619e2d70b148f20a831c00739"},
{file = "greenlet-3.3.0-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9f515a47d02da4d30caaa85b69474cec77b7929b2e936ff7fb853d42f4bf8808"},
{file = "greenlet-3.3.0-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7d2d9fd66bfadf230b385fdc90426fcd6eb64db54b40c495b72ac0feb5766c54"},
{file = "greenlet-3.3.0-cp313-cp313-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:30a6e28487a790417d036088b3bcb3f3ac7d8babaa7d0139edbaddebf3af9492"},
{file = "greenlet-3.3.0-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:087ea5e004437321508a8d6f20efc4cfec5e3c30118e1417ea96ed1d93950527"},
{file = "greenlet-3.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ab97cf74045343f6c60a39913fa59710e4bd26a536ce7ab2397adf8b27e67c39"},
{file = "greenlet-3.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5375d2e23184629112ca1ea89a53389dddbffcf417dad40125713d88eb5f96e8"},
{file = "greenlet-3.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:9ee1942ea19550094033c35d25d20726e4f1c40d59545815e1128ac58d416d38"},
{file = "greenlet-3.3.0-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:60c2ef0f578afb3c8d92ea07ad327f9a062547137afe91f38408f08aacab667f"},
{file = "greenlet-3.3.0-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0a5d554d0712ba1de0a6c94c640f7aeba3f85b3a6e1f2899c11c2c0428da9365"},
{file = "greenlet-3.3.0-cp314-cp314-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3a898b1e9c5f7307ebbde4102908e6cbfcb9ea16284a3abe15cab996bee8b9b3"},
{file = "greenlet-3.3.0-cp314-cp314-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:dcd2bdbd444ff340e8d6bdf54d2f206ccddbb3ccfdcd3c25bf4afaa7b8f0cf45"},
{file = "greenlet-3.3.0-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5773edda4dc00e173820722711d043799d3adb4f01731f40619e07ea2750b955"},
{file = "greenlet-3.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ac0549373982b36d5fd5d30beb8a7a33ee541ff98d2b502714a09f1169f31b55"},
{file = "greenlet-3.3.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d198d2d977460358c3b3a4dc844f875d1adb33817f0613f663a656f463764ccc"},
{file = "greenlet-3.3.0-cp314-cp314-win_amd64.whl", hash = "sha256:73f51dd0e0bdb596fb0417e475fa3c5e32d4c83638296e560086b8d7da7c4170"},
{file = "greenlet-3.3.0-cp314-cp314t-macosx_11_0_universal2.whl", hash = "sha256:d6ed6f85fae6cdfdb9ce04c9bf7a08d666cfcfb914e7d006f44f840b46741931"},
{file = "greenlet-3.3.0-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d9125050fcf24554e69c4cacb086b87b3b55dc395a8b3ebe6487b045b2614388"},
{file = "greenlet-3.3.0-cp314-cp314t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:87e63ccfa13c0a0f6234ed0add552af24cc67dd886731f2261e46e241608bee3"},
{file = "greenlet-3.3.0-cp314-cp314t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2662433acbca297c9153a4023fe2161c8dcfdcc91f10433171cf7e7d94ba2221"},
{file = "greenlet-3.3.0-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3c6e9b9c1527a78520357de498b0e709fb9e2f49c3a513afd5a249007261911b"},
{file = "greenlet-3.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:286d093f95ec98fdd92fcb955003b8a3d054b4e2cab3e2707a5039e7b50520fd"},
{file = "greenlet-3.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c10513330af5b8ae16f023e8ddbfb486ab355d04467c4679c5cfe4659975dd9"},
{file = "greenlet-3.3.0.tar.gz", hash = "sha256:a82bb225a4e9e4d653dd2fb7b8b2d36e4fb25bc0165422a11e48b88e9e6f78fb"},
]
[[package]]
name = "h11"
version = "0.16.0"
@ -2426,6 +2476,51 @@ files = [
{file = "soupsieve-2.8.tar.gz", hash = "sha256:e2dd4a40a628cb5f28f6d4b0db8800b8f581b65bb380b97de22ba5ca8d72572f"},
]
[[package]]
name = "sqlalchemy"
version = "2.0.45"
requires_python = ">=3.7"
summary = "Database Abstraction Library"
groups = ["default"]
dependencies = [
"greenlet>=1; platform_machine == \"win32\" or platform_machine == \"WIN32\" or platform_machine == \"AMD64\" or platform_machine == \"amd64\" or platform_machine == \"x86_64\" or platform_machine == \"ppc64le\" or platform_machine == \"aarch64\"",
"importlib-metadata; python_version < \"3.8\"",
"typing-extensions>=4.6.0",
]
files = [
{file = "sqlalchemy-2.0.45-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2e90a344c644a4fa871eb01809c32096487928bd2038bf10f3e4515cb688cc56"},
{file = "sqlalchemy-2.0.45-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b8c8b41b97fba5f62349aa285654230296829672fc9939cd7f35aab246d1c08b"},
{file = "sqlalchemy-2.0.45-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:12c694ed6468333a090d2f60950e4250b928f457e4962389553d6ba5fe9951ac"},
{file = "sqlalchemy-2.0.45-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:f7d27a1d977a1cfef38a0e2e1ca86f09c4212666ce34e6ae542f3ed0a33bc606"},
{file = "sqlalchemy-2.0.45-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d62e47f5d8a50099b17e2bfc1b0c7d7ecd8ba6b46b1507b58cc4f05eefc3bb1c"},
{file = "sqlalchemy-2.0.45-cp311-cp311-win32.whl", hash = "sha256:3c5f76216e7b85770d5bb5130ddd11ee89f4d52b11783674a662c7dd57018177"},
{file = "sqlalchemy-2.0.45-cp311-cp311-win_amd64.whl", hash = "sha256:a15b98adb7f277316f2c276c090259129ee4afca783495e212048daf846654b2"},
{file = "sqlalchemy-2.0.45-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b3ee2aac15169fb0d45822983631466d60b762085bc4535cd39e66bea362df5f"},
{file = "sqlalchemy-2.0.45-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba547ac0b361ab4f1608afbc8432db669bd0819b3e12e29fb5fa9529a8bba81d"},
{file = "sqlalchemy-2.0.45-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:215f0528b914e5c75ef2559f69dca86878a3beeb0c1be7279d77f18e8d180ed4"},
{file = "sqlalchemy-2.0.45-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:107029bf4f43d076d4011f1afb74f7c3e2ea029ec82eb23d8527d5e909e97aa6"},
{file = "sqlalchemy-2.0.45-cp312-cp312-win32.whl", hash = "sha256:0c9f6ada57b58420a2c0277ff853abe40b9e9449f8d7d231763c6bc30f5c4953"},
{file = "sqlalchemy-2.0.45-cp312-cp312-win_amd64.whl", hash = "sha256:8defe5737c6d2179c7997242d6473587c3beb52e557f5ef0187277009f73e5e1"},
{file = "sqlalchemy-2.0.45-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fe187fc31a54d7fd90352f34e8c008cf3ad5d064d08fedd3de2e8df83eb4a1cf"},
{file = "sqlalchemy-2.0.45-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:672c45cae53ba88e0dad74b9027dddd09ef6f441e927786b05bec75d949fbb2e"},
{file = "sqlalchemy-2.0.45-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:470daea2c1ce73910f08caf10575676a37159a6d16c4da33d0033546bddebc9b"},
{file = "sqlalchemy-2.0.45-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9c6378449e0940476577047150fd09e242529b761dc887c9808a9a937fe990c8"},
{file = "sqlalchemy-2.0.45-cp313-cp313-win32.whl", hash = "sha256:4b6bec67ca45bc166c8729910bd2a87f1c0407ee955df110d78948f5b5827e8a"},
{file = "sqlalchemy-2.0.45-cp313-cp313-win_amd64.whl", hash = "sha256:afbf47dc4de31fa38fd491f3705cac5307d21d4bb828a4f020ee59af412744ee"},
{file = "sqlalchemy-2.0.45-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:83d7009f40ce619d483d26ac1b757dfe3167b39921379a8bd1b596cf02dab4a6"},
{file = "sqlalchemy-2.0.45-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:d8a2ca754e5415cde2b656c27900b19d50ba076aa05ce66e2207623d3fe41f5a"},
{file = "sqlalchemy-2.0.45-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7f46ec744e7f51275582e6a24326e10c49fbdd3fc99103e01376841213028774"},
{file = "sqlalchemy-2.0.45-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:883c600c345123c033c2f6caca18def08f1f7f4c3ebeb591a63b6fceffc95cce"},
{file = "sqlalchemy-2.0.45-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:2c0b74aa79e2deade948fe8593654c8ef4228c44ba862bb7c9585c8e0db90f33"},
{file = "sqlalchemy-2.0.45-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:8a420169cef179d4c9064365f42d779f1e5895ad26ca0c8b4c0233920973db74"},
{file = "sqlalchemy-2.0.45-cp314-cp314-win32.whl", hash = "sha256:e50dcb81a5dfe4b7b4a4aa8f338116d127cb209559124f3694c70d6cd072b68f"},
{file = "sqlalchemy-2.0.45-cp314-cp314-win_amd64.whl", hash = "sha256:4748601c8ea959e37e03d13dcda4a44837afcd1b21338e637f7c935b8da06177"},
{file = "sqlalchemy-2.0.45-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cd337d3526ec5298f67d6a30bbbe4ed7e5e68862f0bf6dd21d289f8d37b7d60b"},
{file = "sqlalchemy-2.0.45-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9a62b446b7d86a3909abbcd1cd3cc550a832f99c2bc37c5b22e1925438b9367b"},
{file = "sqlalchemy-2.0.45-py3-none-any.whl", hash = "sha256:5225a288e4c8cc2308dbdd874edad6e7d0fd38eac1e9e5f23503425c8eee20d0"},
{file = "sqlalchemy-2.0.45.tar.gz", hash = "sha256:1632a4bda8d2d25703fdad6363058d882541bdaaee0e5e3ddfa0cd3229efce88"},
]
[[package]]
name = "stack-data"
version = "0.6.3"
@ -2519,7 +2614,7 @@ name = "typing-extensions"
version = "4.15.0"
requires_python = ">=3.9"
summary = "Backported and Experimental Type Hints for Python 3.9+"
groups = ["dev", "nb"]
groups = ["default", "dev", "nb"]
files = [
{file = "typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548"},
{file = "typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466"},

View File

@ -1,11 +1,11 @@
[project]
name = "polluck-blockchain"
name = "dopt-pollublock-blockchain"
version = "0.1.0"
description = "blockchain module of the research project >Polluck<"
authors = [
{name = "d-opt GmbH, resp. Florian Foerster", email = "f.foerster@d-opt.com"},
]
dependencies = ["dopt-basics>=0.2.4"]
dependencies = ["dopt-basics>=0.2.4", "sqlalchemy>=2.0.45"]
requires-python = ">=3.11"
readme = "README.md"
license = {text = "LicenseRef-Proprietary"}

View File

@ -27,8 +27,8 @@ openssl_lib = OPENSSL_DIR / "lib"
assert openssl_lib.exists()
ext = Extension(
name="polluck_blockchain.placeholder_native",
sources=["src/polluck_blockchain/placeholder_native.pyx"],
name="dopt_pollublock_blockchain.blockchain",
sources=["src/dopt_pollublock_blockchain/blockchain.pyx"],
include_dirs=[str(openssl_include)],
library_dirs=[str(openssl_lib)],
libraries=["libssl", "libcrypto"],

File diff suppressed because one or more lines are too long

View File

@ -5,40 +5,39 @@
from __future__ import annotations
import datetime
import hashlib
import struct
import time
from pathlib import Path
import cython
import dopt_basics.datetime
import sqlalchemy as sql
from dopt_pollublock_blockchain import db
# from cython.cimports.cpython.ref import Py_INCREF, PyObject
from polluck_blockchain.block cimport Block
from dopt_pollublock_blockchain.block cimport Block
from libcpp.unordered_map cimport unordered_map
from libcpp.vector cimport vector
from libcpp.string cimport string
from libc.stdint cimport uint64_t
from libc.stdlib cimport malloc, free
from libc.string cimport memcpy
from cython.operator import postincrement, dereference
cimport polluck_blockchain.openssl_evp as ossl
# from cython.parallel cimport prange
cimport dopt_pollublock_blockchain.openssl_evp as ossl
ctypedef unsigned long ULong
ctypedef unordered_map[uint64_t, Block*] BcHashmap
cdef const size_t NONCE_OFFSET = <size_t>16
cdef timestamp_to_datetime(uint64_t ts):
return datetime.datetime.fromtimestamp(float(ts), dopt_basics.datetime.TIMEZONE_UTC)
cdef uint64_t current_timestamp_integer():
cdef uint64_t ts
dt = dopt_basics.datetime.current_time_tz(cut_microseconds=True)
ts = <uint64_t>int(dt.timestamp())
return ts
# cdef float_to_bytes(double num):
# return struct.pack(">d", num)
cdef int serialize_uint32(unsigned char* out, unsigned int v) except -1 nogil:
out[0] = (v >> 24) & 0xFF
@ -46,6 +45,7 @@ cdef int serialize_uint32(unsigned char* out, unsigned int v) except -1 nogil:
out[2] = (v >> 8) & 0xFF
out[3] = v & 0xFF
cdef int serialize_uint64(unsigned char* out, unsigned long long v) except -1 nogil:
out[0] = (v >> 56) & 0xFF
out[1] = (v >> 48) & 0xFF
@ -71,6 +71,7 @@ cdef inline bint has_leading_zero_bits(const unsigned char *digest, int num_bits
return True
cdef inline bint has_leading_zero_bytes(const unsigned char *digest, int num_bytes) nogil:
cdef int i, full_bytes = num_bytes // 8
cdef int rem_bits = num_bytes % 8
@ -98,13 +99,13 @@ cdef class PyBlock:
if not from_ptr:
self.BlockC = new Block(
index,
int(datetime.datetime(2025, 12, 1, 12, 0, 0).timestamp()),
current_timestamp_integer(),
nonce,
data.encode("UTF-8"),
previous_hash.encode("UTF-8"),
"".encode("UTF-8"),
)
if self.BlockC == NULL:
if self.BlockC is NULL:
raise MemoryError()
self.ptr_owner = True
@ -133,11 +134,26 @@ cdef class PyBlock:
return py_block
def __repr__(self):
return f"PyBlock({self.index, self.timestamp, self.nonce, self.data})"
return (
f"PyBlock(\n\tIndex:\t\t{self.index}\n\ttimestamp:\t{self.timestamp}\n\tnonce:\t\t{self.nonce}\n\t"
f"Prev Hash:\t{self.prev_hash}\n\tHash:\t\t{self.hash}\n\tData:\t\t{self.data}\n)"
)
def __str__(self):
return self.__repr__()
def serialize_dict(self):
contents = {}
contents["index"] = self.BlockC.index
contents["timestamp"] = self.BlockC.timestamp
contents["nonce"] = self.BlockC.nonce
contents["previous_hash"] = self.prev_hash
contents["hash"] = self.hash
contents["data"] = self.data
return contents
# Python public API
@property
def index(self):
@ -181,7 +197,7 @@ cdef class PyBlock:
try:
digest = perform_hash_c(self.BlockC, &digest_size)
if digest == NULL:
if digest is NULL:
raise MemoryError()
# TODO out: hash assignment in blockchain
self.BlockC.hash = bytes(digest[:digest_size]).hex().encode("UTF-8")
@ -205,7 +221,7 @@ cdef unsigned char* bytes_serialize_c(Block *block, size_t *size) nogil:
)
buf = <unsigned char*>malloc(size[0] * sizeof(unsigned char))
if buf == NULL:
if buf is NULL:
return NULL
serialize_uint64(buf + pos, block.index)
@ -233,10 +249,10 @@ cdef unsigned char* bytes_serialize_c(Block *block, size_t *size) nogil:
cdef unsigned char* SHA256_digest(const void *data, size_t data_size, size_t *digest_size) nogil:
cdef ossl.EVP_MD_CTX *ctx = ossl.EVP_MD_CTX_new()
if ctx == NULL:
if ctx is NULL:
return NULL
cdef const ossl.EVP_MD *algo = ossl.EVP_sha256()
if algo == NULL:
if algo is NULL:
return NULL
cdef:
@ -263,11 +279,11 @@ cdef unsigned char* perform_hash_c(Block *block, size_t *digest_size) nogil:
unsigned char *digest
serialize_res = bytes_serialize_c(block, &serialize_size)
if serialize_res == NULL:
if serialize_res is NULL:
return NULL
digest = SHA256_digest(serialize_res, serialize_size, digest_size)
free(serialize_res)
if digest == NULL:
if digest is NULL:
return NULL
return digest
@ -305,34 +321,116 @@ cdef int mine_block(Block *block, unsigned int difficulty, uint64_t *nonce_solut
cdef class Blockchain:
cdef unsigned int _difficulty
cdef uint64_t _index
cdef BcHashmap *_chain
cdef bint _genesis_done
cdef:
unsigned int _difficulty
uint64_t _index
BcHashmap *_chain_map
vector[Block*] *_chain
bint _genesis_done
bint _loaded
readonly object db_path
readonly object _engine
def __cinit__(self):
def __cinit__(self, *args, **kwargs):
self._difficulty = 26
self._index = <uint64_t>0
self._genesis_done = <bint>0
self._chain = new unordered_map[uint64_t, Block*]()
if self._chain is NULL:
self._genesis_done = False
self._loaded = False
self._chain_map = new unordered_map[uint64_t, Block*]()
self._chain = new vector[Block*]()
if self._chain_map is NULL:
raise MemoryError("Could not allocate hasmap")
if self._chain is NULL:
raise MemoryError("Could not allocate vector")
def __init__(self, *args, **kwargs):
# self.db_path = db_path
pass
def __init__(self, db_path):
self.db_path = Path(db_path).resolve()
if not self.db_path.parent.exists():
raise FileNotFoundError(
"The parent directory of the provided database path does not exist"
)
self._engine = sql.create_engine(f"sqlite:///{str(self.db_path)}")
db.metadata_blockchain.create_all(self._engine)
def __dealloc__(self):
# ownership is typically not transferred from the Blockchain extension class
cdef BcHashmap.iterator it = self._chain.begin()
if self._chain is not NULL:
while it != self._chain.end():
cdef BcHashmap.iterator it = self._chain_map.begin()
if self._chain_map is not NULL:
while it != self._chain_map.end():
del dereference(it).second
postincrement(it)
del self._chain_map
self._chain_map = NULL
if self._chain is not NULL:
del self._chain
self._chain = NULL
cdef Block* get_block_c(self, uint64_t idx) nogil:
if idx > self._index:
return NULL
return self._chain_map[0][idx]
cdef void add_block_from_loading(self, Block *block) nogil:
self._chain[0].push_back(block)
self._chain_map[0][block.index] = block
self._index = block.index
if not self._genesis_done:
self._genesis_done = True
cdef int add_block(self, Block *block) nogil:
cdef:
uint64_t mined_nonce
size_t digest_size
unsigned char *sha256_digest
# mine block
if mine_block(block, self._difficulty, &mined_nonce) != 0:
return 1
block.nonce = mined_nonce
# hash block, add hash to block, add block to blockchain hashmap
sha256_digest = perform_hash_c(block, &digest_size)
with gil:
block.hash = bytes(sha256_digest[:digest_size]).hex().encode("UTF-8")
free(sha256_digest)
self._chain[0].push_back(block)
self._chain_map[0][block.index] = block
if self._genesis_done:
self._index += 1
return 0
cdef string hash_data(self, data):
cdef:
string data_str
unsigned char *data_digest
size_t digest_size
data_str = data.encode("UTF-8")
data_digest = SHA256_digest(data_str.c_str(), data_str.size(), &digest_size)
if data_digest is NULL:
raise RuntimeError("Failed to hash data")
data_str = bytes(data_digest[:digest_size]).hex().encode("UTF-8")
free(data_digest)
return data_str
cdef load_from_batch(self, batch):
cdef Block *block
for entry in batch:
block = new Block(
entry[0],
entry[1],
entry[2],
entry[5].encode("UTF-8"),
entry[3].encode("UTF-8"),
entry[4].encode("UTF-8"),
)
self.add_block_from_loading(block)
# // Python public API
def __len__(self):
return self._index + 1
@ -356,73 +454,55 @@ cdef class Blockchain:
def index(self):
return self._index
def print_key_value_pair(self):
cdef BcHashmap.iterator it = self._chain.begin()
def _print_key_value_pair(self):
cdef BcHashmap.iterator it = self._chain_map.begin()
cdef Block *block
while it != self._chain.end():
while it != self._chain_map.end():
print(dereference(it).first)
block = dereference(it).second
py_block = PyBlock.from_ptr(block)
print(py_block)
postincrement(it)
cdef Block* get_block_c(self, uint64_t idx) nogil:
if idx > self._index:
return NULL
return self._chain[0][idx]
cdef int add_block(self, Block *block) nogil:
def print_blocks(self, max_num):
cdef:
uint64_t mined_nonce
size_t digest_size
unsigned char *sha256_digest
# mine block
if mine_block(block, self._difficulty, &mined_nonce) != 0:
return 1
block.nonce = mined_nonce
# hash block, add hash to block, add block to blockchain hashmap
sha256_digest = perform_hash_c(block, &digest_size)
with gil:
block.hash = bytes(sha256_digest[:digest_size]).hex().encode("UTF-8")
free(sha256_digest)
self._chain[0][block.index] = block
Block *block
int max_nummber = max_num
int idx, num = 0
if self._genesis_done:
self._index += 1
if max_num <= 0:
raise ValueError("Maximum number must be greater than 0")
return 0
for idx in range(self._chain[0].size()):
block = self._chain[0][idx]
py_block = PyBlock.from_ptr(block)
print(py_block)
num += 1
if num == max_nummber:
break
# // Python public API
def get_block(self, idx):
if idx < 0 or idx > self._index:
raise IndexError("Index value is out of bounds")
cdef Block *block = self.get_block_c(idx)
if block == NULL:
if block is NULL:
raise IndexError("Provided index not found")
return PyBlock.from_ptr(block, owner=False)
cdef string hash_data(self, data):
cdef:
string data_str
unsigned char *data_digest
size_t digest_size
data_str = data.encode("UTF-8")
data_digest = SHA256_digest(data_str.c_str(), data_str.size(), &digest_size)
if data_digest == NULL:
raise RuntimeError("Failed to hash data")
data_str = bytes(data_digest[:digest_size]).hex().encode("UTF-8")
free(data_digest)
return data_str
def create_genesis_block(self):
if self._genesis_done:
raise RuntimeError(
("Blockchain already has a genesis block. "
"Either it was created or loaded.")
)
genesis_prev_hash = ("0" * 64).encode("UTF-8")
cdef string data_str = self.hash_data("Genesis Block")
cdef Block *block = new Block(
self._index,
int(datetime.datetime(2025, 12, 1, 12, 0, 0).timestamp()),
current_timestamp_integer(),
0,
data_str,
genesis_prev_hash,
@ -449,12 +529,12 @@ cdef class Blockchain:
data_str = self.hash_data(data)
prev_block = self.get_block_c(self._index)
prev_hash = prev_block.prev_hash
prev_hash = prev_block.hash
new_idx = self._index + 1
cdef Block *block = new Block(
new_idx,
int(datetime.datetime(2025, 12, 1, 12, 0, 0).timestamp()),
current_timestamp_integer(),
0,
data_str,
prev_hash,
@ -464,3 +544,87 @@ cdef class Blockchain:
if res != 0:
raise RuntimeError("Could not mine block. No nonce found")
def validate(self):
cdef:
Block *block
Block *prev_block = NULL
int idx = 0
unsigned char *digest
size_t digest_size
for idx in range(self._chain[0].size()):
block = self._chain[0][idx]
py_bytes = bytes.fromhex(block.hash.decode("UTF-8"))
digest = perform_hash_c(block, &digest_size)
py_bytes_rehashed = bytes(digest[:digest_size])
free(digest)
if py_bytes != py_bytes_rehashed:
print(f"Index {idx}: Hashes to not match. Abort.")
return False
if prev_block is not NULL:
if prev_block.hash != block.prev_hash:
print(
(f"Index {idx}: Hash mismatch. Hash of previous block does not "
"match the saved one in the current block. Abort.")
)
return False
prev_block = block
return True
def get_saving_entries(self, max_idx):
entries = []
cdef:
Block *block
int idx = 0
int _max_idx
if max_idx is None:
_max_idx = -1
else:
_max_idx = max_idx
for idx in range(self._chain[0].size()):
if idx <= _max_idx:
continue
block = self._chain[0][idx]
contents = {}
contents["index"] = block.index
contents["timestamp"] = block.timestamp
contents["nonce"] = block.nonce
contents["previous_hash"] = block.prev_hash.decode("UTF-8")
contents["hash"] = block.hash.decode("UTF-8")
contents["data"] = block.data.decode("UTF-8")
entries.append(contents)
return entries
def save(self):
# get max index from db
# only retrieve indices greater than max value
stmt = sql.select(sql.func.max(db.blocks.c.index))
with self._engine.connect() as conn:
result = conn.execute(stmt)
max_value = result.scalar()
entries = self.get_saving_entries(max_value)
if not entries:
return
with self._engine.begin() as con:
con.execute(sql.insert(db.blocks), entries)
def close_db_connections(self):
self._engine.dispose()
def load(self, batch_size):
if self._loaded:
raise RuntimeError("Blockchain was already loaded")
with self._engine.connect() as con:
res = con.execute(sql.select(db.blocks).order_by(db.blocks.c.index.asc()))
for batch in res.partitions(batch_size):
self.load_from_batch(batch)
self._loaded = True

View File

@ -0,0 +1,16 @@
from __future__ import annotations
import sqlalchemy as sql
metadata_blockchain: sql.MetaData = sql.MetaData()
blocks = sql.Table(
"blocks",
metadata_blockchain,
sql.Column("index", sql.BigInteger, primary_key=True),
sql.Column("timestamp", sql.BigInteger, nullable=False),
sql.Column("nonce", sql.BigInteger, nullable=False),
sql.Column("previous_hash", sql.String(64), nullable=False),
sql.Column("hash", sql.String(64), nullable=False),
sql.Column("data", sql.String(64), nullable=False),
)