generated from dopt-python/py311-cython
Include prototyping steps #1
99
pdm.lock
generated
99
pdm.lock
generated
@ -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"},
|
||||
|
||||
@ -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"}
|
||||
|
||||
4
setup.py
4
setup.py
@ -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"],
|
||||
|
||||
21001
src/dopt_pollublock_blockchain/blockchain.cpp
Normal file
21001
src/dopt_pollublock_blockchain/blockchain.cpp
Normal file
File diff suppressed because one or more lines are too long
@ -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
|
||||
16
src/dopt_pollublock_blockchain/db.py
Normal file
16
src/dopt_pollublock_blockchain/db.py
Normal 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),
|
||||
)
|
||||
File diff suppressed because one or more lines are too long
Loading…
x
Reference in New Issue
Block a user