pylibswarm

Python3 wrapper for libswarm-ng
git clone git://git.defalsify.org/pylibswarm.git
Log | Files | Refs | Submodules | README | LICENSE

commit 27b3f29a888d9d00aab1bd651483b099d720d2e5
parent 71fbcb72534670a7068afce8a346753bfc61cfd7
Author: nolash <dev@holbrook.no>
Date:   Thu, 23 Sep 2021 21:15:20 +0200

Add requirements to install

Diffstat:
Mpylibswarm/runnable/bmt.py | 22+++++-----------------
Mpylibswarm/runnable/file.py | 28++++------------------------
Msetup.py | 11+++++++++++
Msrc/python_swarm.c | 133+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------
4 files changed, 144 insertions(+), 50 deletions(-)

diff --git a/pylibswarm/runnable/bmt.py b/pylibswarm/runnable/bmt.py @@ -4,6 +4,10 @@ import sys import logging import select +# local imports +from pylibswarm.arg import stdin_arg + + logging.basicConfig(level=logging.WARNING) logg = logging.getLogger() @@ -17,22 +21,6 @@ argparser.add_argument('data', nargs='?', type=str, help='data input for BMT has largs = argparser.parse_args(sys.argv[1:]) -def stdin_arg(): - """Retreive input arguments from stdin if they exist. - - Method does not block, and expects arguments to be ready on stdin before being called. - - :rtype: str - :returns: Input arguments string - """ - h = select.select([sys.stdin.buffer], [], []) - if len(h[0]) > 0: - v = h[0][0].read() - #return v.rstrip() - return v - return None - - if largs.v: logg.setLevel(logging.DEBUG) @@ -58,7 +46,7 @@ logg.info('hashing {} bytes input with {} length prefix'.format(int(input_data_ def main(): # TODO: why does this segfault just one line before? - probably refcount related import swarm - r = swarm.bmt(input_data, input_data_length, data_length) + r = swarm.bmt_hash(input_data, input_data_length, data_length) if largs.b: sys.stdout.buffer.write(r[:32]) diff --git a/pylibswarm/runnable/file.py b/pylibswarm/runnable/file.py @@ -4,6 +4,10 @@ import sys import argparse import logging +# local imports +from pylibswarm.io import Outputter + + logging.basicConfig(level=logging.WARNING) logg = logging.getLogger() @@ -18,30 +22,6 @@ argparser.add_argument('file', nargs='?', type=str, help='file to hash') largs = argparser.parse_args(sys.argv[1:]) -class Outputter: - - def __init__(self, outdir, prepend_hash=False): - self.outdir = outdir - self.prepend_hash = prepend_hash - os.makedirs(self.outdir, exist_ok=True) - logg.info('outputter set to {}'.format(self.outdir)) - - - def dump(self, hsh, data): - hsh_hex = hsh.hex() - fp = os.path.join(self.outdir, hsh_hex) - f = open(fp, 'wb') - - l = len(data) - if self.prepend_hash: - l += len(hsh) - f.write(hsh) - f.write(data) - f.close() - - logg.debug('wrote {} bytes for chunk file {}'.format(l, hsh_hex)) - - if largs.vv: logg.setLevel(logging.DEBUG) elif largs.v: diff --git a/setup.py b/setup.py @@ -6,6 +6,16 @@ root_dir = os.path.dirname(os.path.realpath(__file__)) default_swarm_dir = os.path.join(root_dir, 'aux', 'libswarm-ng') swarm_dir = os.environ.get('LIBSWARM_DIR', default_swarm_dir) +requirements = [] +f = open('requirements.txt', 'r') +while True: + l = f.readline() + if l == '': + break + requirements.append(l.rstrip()) +f.close() + + def main(): setup( name="swarm", @@ -13,6 +23,7 @@ def main(): description="Swarm tooling", author="Louis Holbrook", author_email="dev@holbrook.no", + install_requires=requirements, ext_modules=[ Extension("swarm", [ "src/python_swarm.c", diff --git a/src/python_swarm.c b/src/python_swarm.c @@ -7,22 +7,36 @@ #include "bmt.h" #include "swarmfile.h" +#include "soc.h" -static void filehash_callback(const char *hash, const char *data, size_t data_length, void *callback_static) { +static void client_callback_do(const unsigned char *hash, const unsigned char *data, size_t data_length, void *callback_static) { PyObject *callback = (PyObject*)callback_static; PyObject_CallFunction(callback, "y#y#", hash, SWARM_WORD_SIZE, data, data_length); } -static bmt_spansize_t filehash_path(filehash_t *fctx, const char *filepath) { +static void soc_sign_callback(char *z, const unsigned char *address, const unsigned char *data, void *callback_static) { + PyObject *callback = (PyObject*)callback_static; + PyObject *r; + unsigned char *signature_bytes; + + + r = PyObject_CallFunction(callback, "y#y#", address, SWARM_ADDRESS_SIZE, data, SWARM_WORD_SIZE); + signature_bytes = PyBytes_AsString(r); + + memcpy(z, signature_bytes, SWARM_SIGNATURE_SIZE); +} + + +static bmt_spansize_t filehash_path(filehash_t *fctx, const unsigned char *filepath) { int fd; int r; int c; size_t l; struct stat st; - char buf[SWARM_BLOCK_SIZE]; + unsigned char buf[SWARM_BLOCK_SIZE]; fd = open(filepath, O_RDONLY); if (fd == -1) { @@ -60,20 +74,25 @@ static bmt_spansize_t filehash_path(filehash_t *fctx, const char *filepath) { } -static PyObject* method_bmt(PyObject *self, PyObject *args) { +static PyObject* method_bmt_hash(PyObject *self, PyObject *args) { bmt_t bctx; - const char *input; + const unsigned char *input; Py_ssize_t input_length; bmt_spansize_t data_length; int r; r = PyArg_ParseTuple(args, "yIL", &input, &input_length, &data_length); if (r != 1) { + PyErr_SetString(PyExc_ValueError, "could not build values"); return NULL; } bmt_init(&bctx, (char*)input, input_length, data_length); - bmt_sum(&bctx); + r = bmt_sum(&bctx); + if (r != 0) { + PyErr_SetString(PyExc_RuntimeError, "bmt hashing failed"); + return NULL; + } return Py_BuildValue("y#", &bctx.buf, SWARM_WORD_SIZE); } @@ -81,29 +100,125 @@ static PyObject* method_bmt(PyObject *self, PyObject *args) { static PyObject* method_filehash_path(PyObject *self, PyObject *args) { filehash_t fctx; - const char *inpath; + const unsigned char *inpath; PyObject *client_callback; int r; r = PyArg_ParseTuple(args, "s|O", &inpath, &client_callback); if (r != 1) { + PyErr_SetString(PyExc_ValueError, "could not build values"); return NULL; } if (client_callback == NULL) { filehash_init(&fctx); } else { - filehash_init_callback(&fctx, filehash_callback, client_callback); + filehash_init_callback(&fctx, client_callback_do, client_callback); } r = filehash_path(&fctx, inpath); + if (r == -1) { + PyErr_Format(PyExc_RuntimeError, "file hashing failed for path %s", inpath); + return NULL; + } return Py_BuildValue("y#", &fctx.buf, SWARM_WORD_SIZE); } +static PyObject* method_soc_identifier(PyObject *self, PyObject *args) { + int r; + const PyBytesObject *topic; + const PyBytesObject *index; + const unsigned char *topic_bytes; + const unsigned char *index_bytes; + char out[128]; + + r = PyArg_ParseTuple(args, "SS", &topic, &index); + if (r != 1) { + PyErr_SetString(PyExc_ValueError, "could not build values"); + return NULL; + } + + topic_bytes = PyBytes_AsString((PyObject*)topic); + index_bytes = PyBytes_AsString((PyObject*)index); + r = soc_identifier(out, topic_bytes, index_bytes); + if (r != 0) { + PyErr_SetString(PyExc_RuntimeError, "could not build identifier"); + return NULL; + } + + return Py_BuildValue("y#", &out, SWARM_WORD_SIZE); +} + + +static PyObject* method_soc_create(PyObject *self, PyObject *args) { + int r; + bmt_t bctx; + const PyBytesObject *identifier; + unsigned char *identifier_bytes; + const PyBytesObject *input; + const PyBytesObject *address; + unsigned char *address_bytes; + size_t input_length; + unsigned char *p; + unsigned char soc_hash[128]; + bmt_spansize_t data_length; + unsigned char soc_serialized[SWARM_DATA_LENGTH_TYPESIZE + SWARM_BLOCK_SIZE]; + soc_chunk_t chunk; + PyObject *client_callback; + PyObject *keystore_callback; + + client_callback = NULL; + + r = PyArg_ParseTuple(args, "SSSILO|O", &identifier, &address, &input, &input_length, &data_length, &keystore_callback, &client_callback); + if (r != 1) { + PyErr_SetString(PyExc_ValueError, "could not build values"); + return NULL; + } + memcpy(&chunk.data.payload_sz, &input_length, sizeof(input_length)); + memcpy(chunk.data.span, &data_length, SWARM_DATA_LENGTH_TYPESIZE); + if (data_length > SWARM_BLOCK_SIZE) { + PyErr_Format(PyExc_ValueError, "data payload size %d exceeds maximum of %d bytes", chunk.data.payload_sz, SWARM_BLOCK_SIZE); + return NULL; + } + chunk.data.payload = PyBytes_AsString((PyObject*)input); + identifier_bytes = PyBytes_AsString((PyObject*)identifier); + address_bytes = PyBytes_AsString((PyObject*)address); + memcpy(chunk.identifier, identifier_bytes, SWARM_SOC_IDENTIFIER_SIZE); + bmt_init(&bctx, (char*)chunk.data.payload, data_length, data_length); + r = bmt_sum(&bctx); + if (r != 0) { + PyErr_SetString(PyExc_RuntimeError, "bmt hashing failed"); + return NULL; + } + memcpy(chunk.data.hash, bctx.buf, SWARM_WORD_SIZE); + r = soc_digest(&chunk, soc_hash); + if (r != 0) { + PyErr_SetString(PyExc_RuntimeError, "soc digest failed"); + return NULL; + } + soc_sign_callback(&chunk.signature, address_bytes, soc_hash, keystore_callback); + + p = soc_serialize(&chunk, soc_serialized, &input_length); + if (p == NULL) { + PyErr_SetString(PyExc_RuntimeError, "soc serialize failed"); + return NULL; + } + + if (client_callback != NULL) { +// client_callback_do(soc_hash, soc_serialized, input_length, client_callback); + } + + return Py_BuildValue("y#y#y#", chunk.signature, SWARM_SIGNATURE_SIZE, soc_hash, SWARM_WORD_SIZE, soc_serialized, input_length); + +} + + static PyMethodDef SwarmMethods[] = { - {"bmt", method_bmt, METH_VARARGS, "Calculate the BMT hash of the given data"}, + {"bmt_hash", method_bmt_hash, METH_VARARGS, "Calculate the BMT hash of the given data"}, {"filehash_path", method_filehash_path, METH_VARARGS, "Calculate the Swarm file hash of the data from the given file path, with optional callback to receive chunks"}, + {"soc_identifier", method_soc_identifier, METH_VARARGS, "Build an SOC identifier from given topic and index"}, + {"soc_create", method_soc_create, METH_VARARGS, "Build an SOC chunk from given data and identifier"}, {NULL, NULL, 0, NULL}, };