Skip to content

Commit

Permalink
refactor: add cffi instructions for python bindings and and ffi to th…
Browse files Browse the repository at this point in the history
…e distribution
  • Loading branch information
mpolitzer committed May 10, 2024
1 parent dcee7bc commit b7b24d1
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 2 deletions.
8 changes: 6 additions & 2 deletions sys-utils/libcmt/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -81,11 +81,12 @@ $(libcmt_SO): $(libcmt_OBJ)
$(TARGET_CC) -shared -o $@ $^

libcmt: $(libcmt_LIB) $(libcmt_SO)
install: $(libcmt_LIB) $(libcmt_SO)
install: $(libcmt_LIB) $(libcmt_SO) build/ffi.h
mkdir -p $(TARGET_DESTDIR)$(TARGET_PREFIX)/lib
cp -f $^ $(TARGET_DESTDIR)$(TARGET_PREFIX)/lib
mkdir -p $(TARGET_DESTDIR)$(TARGET_PREFIX)/include/libcmt/
cp -f src/*.h $(TARGET_DESTDIR)$(TARGET_PREFIX)/include/libcmt/
cp -f build/ffi.h $(TARGET_DESTDIR)$(TARGET_PREFIX)/include/libcmt/
mkdir -p $(TARGET_DESTDIR)$(TARGET_PREFIX)/lib/pkgconfig
sed -e 's|@ARG_PREFIX@|$(TARGET_PREFIX)|g' src/libcmt.pc > $(TARGET_DESTDIR)$(TARGET_PREFIX)/lib/pkgconfig/libcmt.pc

Expand Down Expand Up @@ -121,11 +122,12 @@ $(mock_SO): $(mock_OBJ)

mock: $(mock_LIB) $(mock_SO)

install-mock: $(mock_LIB) $(mock_SO)
install-mock: $(mock_LIB) $(mock_SO) build/ffi.h
mkdir -p $(DESTDIR)$(PREFIX)/lib
cp -f $^ $(DESTDIR)$(PREFIX)/lib
mkdir -p $(DESTDIR)$(PREFIX)/include/libcmt/
cp -f src/*.h $(DESTDIR)$(PREFIX)/include/libcmt/
cp -f build/ffi.h $(DESTDIR)$(PREFIX)/include/libcmt/
mkdir -p $(DESTDIR)$(PREFIX)/lib/pkgconfig
sed -e 's|@ARG_PREFIX@|$(PREFIX)|g' src/libcmt_mock.pc > $(DESTDIR)$(PREFIX)/lib/pkgconfig/libcmt.pc

Expand Down Expand Up @@ -181,6 +183,8 @@ $(tools_OBJDIR)/funsel: tools/funsel.c $(mock_LIB)

tools: $(tools_BINS)

build/ffi.h: src/buf.h src/abi.h src/keccak.h src/merkle.h src/io.h src/rollup.h
cat $^ | sh tools/prepare-ffi.sh > $@
#-------------------------------------------------------------------------------
LINTER_IGNORE_SOURCES=src/io.c
LINTER_IGNORE_HEADERS=
Expand Down
73 changes: 73 additions & 0 deletions sys-utils/libcmt/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,79 @@ cast calldata-decode "Notice(bytes)" 0x`xxd -p -c0 "$1"` | (
This library is intented to be used with programming languages other than C.
They acheive this by different means.

## Python

Python has multiple Foreign Function Interface (FFI) options for interoperability with C.
This example uses CFFI and the `libcmt` mock. Which is assumed to be installed at `./libcmt-0.1.0`.

This document uses the [main mode](https://cffi.readthedocs.io/en/latest/overview.html#main-mode-of-usage) of CFFI and works in two steps: `build`, then `use`.

### Build

The `build` step has the objective of creating a python module for libcmt.
To acheive this we'll use `libcmt/ffi.h` and the script below.
Paths may need adjustments.

```
import os
from cffi import FFI
ffi = FFI()
with open(os.path.join(os.path.dirname(__file__), "../libcmt-0.1.0/usr/include/libcmt/ffi.h")) as f:
ffi.cdef(f.read())
ffi.set_source("pycmt",
"""
#include "libcmt/rollup.h"
""",
include_dirs=["libcmt-0.1.0/usr/include"],
library_dirs=["libcmt-0.1.0/usr/lib"],
libraries=['cmt'])
if __name__ == "__main__":
ffi.compile(verbose=True)
```

### Use

With the module built, we can import it with python and use its functions.
This example uses the raw bindings and just serves to ilustrate the process.
Better yet would be to wrap these functions into a more "pythonic" API.

`LD_LIBRARY_PATH=libcmt-0.1.0/lib/ python`

```
#!/usr/bin/env python
# run this file only after building pycmt!
import os
from pycmt.lib import ffi, lib
r = ffi.new("cmt_rollup_t[1]")
assert(lib.cmt_rollup_init(r) == 0)
address = ffi.new("uint8_t[20]", b"1000000000000")
value = ffi.new("uint8_t[32]", b"0000000000001")
data = b"hello world"
index = ffi.new("uint64_t *")
print(os.strerror(-lib.cmt_rollup_emit_voucher(r,
20, address,
32, value,
len(data), data, index)))
ffi.gc(r, lib.cmt_rollup_fini)
```

Common errors such as the one below indicate that python couldn't find libcmt,
make sure `LD_LIBRARY_PATH` points to the correct directory, or better yet, link
against the static library.

```
>>> import pycmt
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ImportError: libcmt.so: cannot open shared object file: No such file or directory
```

## Go

Go is able to include C headers directly with cgo.
Expand Down
9 changes: 9 additions & 0 deletions sys-utils/libcmt/tools/prepare-ffi.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#!/bin/sh

sed \
-e '/\/\*/,/\*\//d' \
-e '/#if\s/,/#endif/d' \
-e '/#define/d' \
-e '/#endif/d' \
-e '/#ifndef/d' \
-e '/#include/d' \

0 comments on commit b7b24d1

Please sign in to comment.