more core implementation in Cython

This commit is contained in:
Florian Förster 2025-12-11 16:01:29 +01:00
parent d3d5d04ef2
commit 68356e06a5
2 changed files with 3509 additions and 737 deletions

File diff suppressed because it is too large Load Diff

View File

@ -16,118 +16,54 @@ import dopt_basics.datetime
from polluck_blockchain.block cimport Block
from libcpp.unordered_map cimport unordered_map
from libcpp.string cimport string
from libc.stdint cimport uint64_t
ctypedef unsigned long ULong
# cdef struct PyStringC:
# char* ptr
# Py_ssize_t length
# cdef PyStringC_to_unicode(PyStringC string):
# return string.ptr[: string.length].decode("UTF-8", "strict")
# cdef PyStringC_to_bytes(PyStringC string):
# return <bytes>string.ptr[: string.length]
ctypedef unordered_map[uint64_t, Block*] BcHashmap
cdef timestamp_to_datetime(double ts):
return datetime.datetime.fromtimestamp(ts, dopt_basics.datetime.TIMEZONE_UTC)
# cdef class StringHolder:
# cdef readonly PyStringC c
# cdef object _py_data
# def __cinit__(self, data: str):
# """accepting Python str
# Parameters
# ----------
# data : str
# _description_
# """
# if not isinstance(data, str):
# raise TypeError("Data not 'str'")
# self._py_data = data.encode("UTF-8")
# self.c = PyStringC(self._py_data, len(self._py_data))
# @property
# def py_string(self):
# return PyStringC_to_unicode(self.c)
# @property
# def py_bytes(self):
# return PyStringC_to_bytes(self.c)
# BlockC = cython.struct(
# index=cython.ulonglong,
# timestamp=cython.double,
# data=PyStringC,
# previous_hash=PyStringC,
# nonce=cython.ulonglong,
# )
cdef float_to_bytes(double num):
return struct.pack(">d", num)
# cdef struct BlockC:
# ULong _index
# double _timestamp
# string _data
# string _prev_hash
# ULong _nonce
# string _hash
cdef class BlockPy:
cdef class PyBlock:
cdef:
ULong _index
double _timestamp
string _data
string _prev_hash
ULong _nonce
string _hash
Block* Block
# ULong _index
# double _timestamp
# string _data
# string _prev_hash
# ULong _nonce
# string _hash
Block *BlockC
bint ptr_owner
def __cinit__(self,
index: int,
data: str,
previous_hash: str,
nonce: int,
index,
nonce,
data,
previous_hash,
from_ptr=False,
):
self.Block = new Block(
index,
datetime.datetime(2025, 12, 1, 12, 0, 0).timestamp(),
nonce,
data.encode("UTF-8"),
previous_hash.encode("UTF-8"),
"".encode("UTF-8"),
)
self.ptr_owner = False
if not from_ptr:
self.BlockC = new Block(
index,
datetime.datetime(2025, 12, 1, 12, 0, 0).timestamp(),
nonce,
data.encode("UTF-8"),
previous_hash.encode("UTF-8"),
"".encode("UTF-8"),
)
if self.BlockC == NULL:
raise MemoryError("Allocation of block object failed")
def get_data(self):
return self.Block.data.decode("UTF-8")
self.ptr_owner = True
# def __init__(
# self,
# index: int,
# data: str,
# previous_hash: str,
# nonce: int,
# ):
# self.C = new BlockC(
# index,
# datetime.datetime(2025, 12, 1, 12, 0, 0).timestamp(),
# data.encode("UTF-8"),
# previous_hash.encode("UTF-8"),
# nonce,
# "".encode("UTF-8"),
# )
def __init__(self, *args, **kwargs):
pass
# self._index = index
# # self._timestamp = dopt_basics.datetime.current_time_tz().timestamp()
# self._timestamp = datetime.datetime(2025, 12, 1, 12, 0, 0).timestamp()
@ -136,6 +72,26 @@ cdef class BlockPy:
# self._nonce = nonce
# self._hash = "".encode("UTF-8")
def __dealloc__(self):
if self.BlockC is not NULL and self.ptr_owner is True:
del self.BlockC
self.BlockC = NULL
@staticmethod
cdef PyBlock from_ptr(Block *block, bint owner=False):
cdef PyBlock py_block = PyBlock.__new__(
PyBlock,
block.index,
block.nonce,
block.data,
block.prev_hash,
True,
)
py_block.BlockC = block
py_block.ptr_owner = owner
return py_block
# cpdef _perform_hash(self):
# parts = bytearray()
# parts.extend(self._index.to_bytes(8, "big"))
@ -152,51 +108,124 @@ cdef class BlockPy:
# def compute_hash(self):
# return self._perform_hash().hexdigest()
# # Python public API
# @property
# def index(self):
# return self._index
# Python public API
@property
def index(self):
return self.BlockC.index
# @property
# def timestamp(self):
# return timestamp_to_datetime(self._timestamp)
@property
def timestamp(self):
return timestamp_to_datetime(self.BlockC.timestamp)
# @property
# def data(self):
# return self._data.decode("UTF-8")
@property
def data(self):
return self.BlockC.data.decode("UTF-8")
# @property
# def prev_hash(self):
# return self._prev_hash.decode("UTF-8")
@property
def prev_hash(self):
return self.BlockC.prev_hash.decode("UTF-8")
# @property
# def nonce(self):
# return self._nonce
@property
def nonce(self):
return self.BlockC.nonce
# @nonce.setter
# def nonce(self, value: int):
# self._nonce = value
# @property
# def hash(self):
# return self._hash.decode("UTF-8")
@property
def hash(self):
return self.BlockC.hash.decode("UTF-8")
# @hash.setter
# def hash(self, value: str):
# def hash(self, value):
# if not isinstance(value, str):
# raise TypeError("No string")
# self._hash = value.encode("UTF-8")
# cdef void add_block(BcHashmap *map, Block *block):
# map[block.index] = block
# cdef class Blockchain:
# cdef int _difficulty
# cdef size_t _index
# cdef unordered_map[size_t, int] _chain
# def __cinit__(self):
# self._difficulty = 1
# self._index = <size_t>0
# # self._chain = new unordered_map[size_t, Block]()
cdef class Blockchain:
cdef int _difficulty
cdef uint64_t _index
cdef BcHashmap *_chain
cdef bint _genesis_done
def __cinit__(self):
self._difficulty = 1
self._index = <uint64_t>0
self._genesis_done = <bint>0
self._chain = new unordered_map[uint64_t, Block*]()
if self._chain is NULL:
raise MemoryError("Could not allocate hasmap")
def __init__(self, *args, **kwargs):
pass
def __dealloc__(self):
if self._chain is not NULL:
del self._chain
self._chain = NULL
def __len__(self):
return self._index + 1
@property
def genesis_done(self):
return self._genesis_done
@property
def index(self):
return self._index
# TODO error handling
cdef Block* get_block_c(self, uint64_t idx):
return self._chain[0][idx]
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)
return PyBlock.from_ptr(block, owner=False)
cdef void add_block(self, Block *block):
self._chain[0][block.index] = block
if self._genesis_done:
self._index += 1
def create_genesis_block(self):
cdef Block *block = new Block(
self._index,
datetime.datetime(2025, 12, 1, 12, 0, 0).timestamp(),
0,
"Genesis Block".encode("UTF-8"),
"0".encode("UTF-8"),
"".encode("UTF-8"),
)
block.hash = <string>"dummy hash".encode("UTF-8")
self.add_block(block)
self._genesis_done = <bint>1
def new_block(self, data):
if not self._genesis_done:
raise RuntimeError("Create a genesis block first.")
cdef Block *prev_block = self.get_block_c(self._index)
cdef string prev_hash = prev_block.prev_hash
cdef uint64_t new_idx = self._index + 1
cdef Block *block = new Block(
new_idx,
datetime.datetime(2025, 12, 1, 12, 0, 0).timestamp(),
0,
data.encode("UTF-8"),
prev_hash,
"".encode("UTF-8"),
)
self.add_block(block)
# def __init__(
# self,