Skip to content

Commit f9873d4

Browse files
committed
build: add pyproject.toml with uv configuration
PEP 621 project metadata with uv as build/run tool. Dependencies (embit, ur, urtypes) sourced from git. Includes poethepoet for remaining manual tasks (docs, i18n, mpy-cross, vulture-whitelist, git-update). Also add a .gitignore and .gitmodules files.
1 parent 65f1665 commit f9873d4

9 files changed

Lines changed: 994 additions & 0 deletions

File tree

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
*.pem
2+
dist/

.gitmodules

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
[submodule "firmware/MaixPy"]
2+
path = firmware/MaixPy
3+
url = https://github.com/selfcustody/MaixPy
4+
[submodule "vendor/embit"]
5+
path = vendor/embit
6+
url = https://github.com/diybitcoinhardware/embit
7+
[submodule "vendor/urtypes"]
8+
path = vendor/urtypes
9+
url = https://github.com/selfcustody/urtypes
10+
[submodule "vendor/foundation-ur-py"]
11+
path = vendor/foundation-ur-py
12+
url = https://github.com/selfcustody/foundation-ur-py

Dockerfile

Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
# The MIT License (MIT)
2+
3+
# Copyright (c) 2021-2023 Krux contributors
4+
5+
# Permission is hereby granted, free of charge, to any person obtaining a copy
6+
# of this software and associated documentation files (the "Software"), to deal
7+
# in the Software without restriction, including without limitation the rights
8+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
# copies of the Software, and to permit persons to whom the Software is
10+
# furnished to do so, subject to the following conditions:
11+
12+
# The above copyright notice and this permission notice shall be included in
13+
# all copies or substantial portions of the Software.
14+
15+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+
# THE SOFTWARE.
22+
23+
# syntax=docker/dockerfile:1
24+
25+
############
26+
# build-base
27+
# install kendryte (k210), cmake and python dependencies
28+
############
29+
FROM gcc:9.5.0-bullseye AS build-base
30+
31+
RUN apt-get update -y && \
32+
apt-get install --no-install-recommends -y -q \
33+
wget \
34+
tar \
35+
zip \
36+
unzip \
37+
build-essential \
38+
libtool \
39+
autoconf \
40+
automake \
41+
autotools-dev \
42+
curl \
43+
libmpc-dev \
44+
libmpfr-dev \
45+
libgmp-dev \
46+
gawk \
47+
bison \
48+
flex \
49+
texinfo \
50+
gperf \
51+
libtool \
52+
patchutils \
53+
bc \
54+
zlib1g-dev \
55+
libexpat-dev \
56+
libisl-dev \
57+
python3 \
58+
python3-pip \
59+
python3-setuptools
60+
61+
RUN mkdir -p /opt && \
62+
GIT_TERMINAL_PROMPT=0 \
63+
git clone --depth 1 --branch v8.2.0-20190409 https://github.com/kendryte/kendryte-gnu-toolchain
64+
65+
RUN cd kendryte-gnu-toolchain && \
66+
sed -i 's|https://github.com/bminor/binutils-gdb.git|https://github.com/riscvarchive/riscv-binutils-gdb.git|' .gitmodules && \
67+
git submodule sync && \
68+
git submodule update \
69+
--init \
70+
--recursive \
71+
--depth 1
72+
73+
74+
75+
RUN cd kendryte-gnu-toolchain && \
76+
export PATH=$PATH:/opt/kendryte-toolchain/bin && \
77+
./configure --prefix=/opt/kendryte-toolchain --with-cmodel=medany --with-arch=rv64imafc --with-abi=lp64f --enable-threads=posix --enable-libatomic && \
78+
make -j8
79+
80+
RUN wget https://github.com/Kitware/CMake/releases/download/v3.21.0/cmake-3.21.0.tar.gz && \
81+
echo "4a42d56449a51f4d3809ab4d3b61fd4a96a469e56266e896ce1009b5768bd2ab cmake-3.21.0.tar.gz" | sha256sum -c && \
82+
tar -xzvf cmake-3.21.0.tar.gz && \
83+
cd cmake-3.21.0 && ./bootstrap && make && make install
84+
85+
RUN apt-get update && apt-get install python3-venv -y
86+
RUN python3 -m venv /kruxenv
87+
RUN /kruxenv/bin/pip install astor
88+
RUN /kruxenv/bin/pip install pyserial==3.4
89+
90+
# Provide memset_s for host tools (mpy-cross) that expect it during link time
91+
RUN set -eux; \
92+
cat > /tmp/memset_s.c <<'EOF'
93+
#include <errno.h>
94+
#include <stddef.h>
95+
96+
typedef size_t rsize_t;
97+
typedef int errno_t;
98+
99+
errno_t memset_s(void *dest, rsize_t destsz, int ch, rsize_t count) {
100+
if (!dest) return EINVAL;
101+
if (count > destsz) return EINVAL;
102+
volatile unsigned char *p = (volatile unsigned char *)dest;
103+
while (count--) *p++ = (unsigned char)ch;
104+
__asm__ __volatile__("" : : : "memory"); /* compiler barrier */
105+
return 0;
106+
}
107+
EOF
108+
RUN set -eux; \
109+
gcc -O2 -c /tmp/memset_s.c -o /tmp/memset_s.o; \
110+
ar rcs /usr/local/lib/libmemset_s.a /tmp/memset_s.o; \
111+
nm -g /usr/local/lib/libmemset_s.a | grep -w memset_s
112+
113+
###############
114+
# build-vendor
115+
# everything except COPY ./src
116+
###############
117+
FROM build-base AS build-vendor
118+
ARG DEVICE="maixpy_m5stickv"
119+
ENV DEVICE_BUILTIN="firmware/MaixPy/projects/${DEVICE}/builtin_py"
120+
RUN mkdir /src
121+
WORKDIR /src
122+
123+
COPY ./vendor vendor
124+
RUN find vendor/urtypes -type d -name '__pycache__' -exec rm -rv {} + -depth
125+
RUN find vendor/foundation-ur-py -type d -name '__pycache__' -exec rm -rv {} + -depth
126+
RUN /kruxenv/bin/pip install vendor/embit
127+
RUN rm -rf vendor/embit/src/embit/util/prebuilt && \
128+
rm -rf vendor/embit/src/embit/liquid && \
129+
rm -f vendor/embit/src/embit/psbtview.py && \
130+
rm -f vendor/embit/src/embit/slip39.py && \
131+
rm -f vendor/embit/src/embit/wordlists/slip39.py && \
132+
rm -f vendor/embit/src/embit/util/ctypes_secp256k1.py && \
133+
rm -f vendor/embit/src/embit/util/py_secp256k1.py && \
134+
rm -f vendor/embit/src/embit/util/py_ripemd160.py && \
135+
find vendor/embit -type d -name '__pycache__' -exec rm -rv {} + -depth
136+
137+
COPY ./firmware firmware
138+
RUN find firmware -type d -name '__pycache__' -exec rm -rv {} + -depth
139+
RUN cp -r vendor/urtypes/src/urtypes "${DEVICE_BUILTIN}"
140+
RUN cp -r vendor/foundation-ur-py/src/ur "${DEVICE_BUILTIN}"
141+
RUN cp -r vendor/embit/src/embit "${DEVICE_BUILTIN}"
142+
143+
################
144+
# build-safeclib
145+
################
146+
FROM build-vendor AS build-safeclib
147+
RUN apt-get update -y && apt-get install --no-install-recommends -y \
148+
git make autoconf automake libtool pkg-config ca-certificates \
149+
&& rm -rf /var/lib/apt/lists/*
150+
WORKDIR /src
151+
RUN git clone --depth 1 https://github.com/rurban/safeclib.git
152+
WORKDIR /src/safeclib
153+
RUN ./build-aux/autogen.sh \
154+
&& ./configure --prefix=/opt/safeclib \
155+
&& make -j"$(nproc)" \
156+
&& make install
157+
158+
159+
###########
160+
# build-mpy
161+
###########
162+
FROM build-safeclib AS build-mpy
163+
RUN apt-get update -y && apt-get install --no-install-recommends -y \
164+
git make python3 ca-certificates \
165+
&& rm -rf /var/lib/apt/lists/*
166+
COPY --from=build-safeclib /opt/safeclib /opt/safeclib
167+
ENV LD_LIBRARY_PATH=/opt/safeclib/lib
168+
169+
WORKDIR /src
170+
COPY firmware/MaixPy/components/micropython/core/mpy-cross/ \
171+
firmware/MaixPy/components/micropython/core/mpy-cross/
172+
173+
# Provide memset_s for host tool only (mpy-cross). No upstream repo edits.
174+
RUN set -eux; \
175+
cat > /tmp/memset_s.c <<'EOF'
176+
#include <errno.h>
177+
#include <stddef.h>
178+
179+
typedef size_t rsize_t;
180+
typedef int errno_t;
181+
182+
errno_t memset_s(void *dest, rsize_t destsz, int ch, rsize_t count) {
183+
if (!dest) return EINVAL;
184+
if (count > destsz) return EINVAL;
185+
volatile unsigned char *p = (volatile unsigned char *)dest;
186+
while (count--) *p++ = (unsigned char)ch;
187+
__asm__ __volatile__("" : : : "memory"); /* compiler barrier */
188+
return 0;
189+
}
190+
EOF
191+
RUN set -eux; \
192+
gcc -O2 -c /tmp/memset_s.c -o /tmp/memset_s.o; \
193+
ar rcs /usr/local/lib/libmemset_s.a /tmp/memset_s.o; \
194+
nm -g /usr/local/lib/libmemset_s.a | grep -w memset_s
195+
196+
WORKDIR /src/firmware/MaixPy/components/micropython/core/mpy-cross
197+
RUN set -eux; \
198+
make V=1 LIB="-lm /usr/local/lib/libmemset_s.a"; \
199+
chmod +x ./mpy-cross
200+
201+
##############
202+
# build kapps
203+
# compilation of kapps inside kapps/ folders
204+
#############
205+
FROM build-mpy AS build-kapp
206+
ARG KAPP="nostr"
207+
WORKDIR /work
208+
COPY src/${KAPP}.py /work/${KAPP}.py
209+
RUN mkdir -p /out \
210+
&& /src/firmware/MaixPy/components/micropython/core/mpy-cross/mpy-cross \
211+
-X heapsize=4194304 -O2 -o "/out/${KAPP}.mpy" "/work/${KAPP}.py"

firmware/MaixPy

Submodule MaixPy added at fa62dab

pyproject.toml

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
# The MIT License (MIT)
2+
3+
# Copyright (c) 2021-2026 Krux contributors
4+
5+
# Permission is hereby granted, free of charge, to any person obtaining a copy
6+
# of this software and associated documentation files (the "Software"), to deal
7+
# in the Software without restriction, including without limitation the rights
8+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
# copies of the Software, and to permit persons to whom the Software is
10+
# furnished to do so, subject to the following conditions:
11+
12+
# The above copyright notice and this permission notice shall be included in
13+
# all copies or substantial portions of the Software.
14+
15+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+
# THE SOFTWARE.
22+
23+
[project]
24+
name = "kapps"
25+
version = "26.03.1"
26+
description = "Krux apps for signing devices"
27+
authors = [{ name = "tadeubas", email = "tadeubas@gmail.com" }]
28+
readme = "README.md"
29+
license = "MIT"
30+
requires-python = ">=3.12.3"
31+
32+
dependencies = [
33+
"embit",
34+
"ur",
35+
"urtypes",
36+
]
37+
38+
[tool.uv.sources]
39+
embit = { git = "https://github.com/diybitcoinhardware/embit.git", branch = "master" }
40+
ur = { git = "https://github.com/selfcustody/foundation-ur-py.git", branch = "master" }
41+
urtypes = { git = "https://github.com/selfcustody/urtypes.git", branch = "main" }
42+
43+
[dependency-groups]
44+
dev = [
45+
"black>=26.3.1,<27",
46+
"pylint>=4.0.5,<5",
47+
"pytest>=9.0.2,<10",
48+
"pytest-cov>=7.0.0,<8",
49+
"pytest-mock>=3.15.1,<4",
50+
"PyQRCode>=1.2.1,<2",
51+
"pycryptodome>=3.23.0,<4",
52+
"vulture>=2.15,<3",
53+
"pre-commit>=4.5.1,<5",
54+
"poethepoet>=0.42.1,<0.43",
55+
]
56+
57+
[tool.hatch.build.targets.wheel]
58+
packages = ["src"]
59+
60+
[build-system]
61+
requires = ["hatchling"]
62+
build-backend = "hatchling.build"
63+
64+
[tool.poe.tasks.vulture]
65+
args = [
66+
{ name = "whitelist", help = "make vulture_whitelist.py"},
67+
]
68+
shell = """
69+
set -euo pipefail
70+
71+
vulture src --make-whitelist > vulture_whitelist.py
72+
"""
73+
74+
[tool.poe.tasks.compile]
75+
args = [
76+
{ name = "name", help = "Basename of src/<name>.py", options = [
77+
"-n",
78+
"--name"
79+
], default = "k_qr" },
80+
]
81+
shell = """
82+
set -euo pipefail
83+
84+
image="kapp-${name}"
85+
container="build-kapp-${name}"
86+
docker build --build-arg KAPP=${name} --target build-kapp -t ${image} .
87+
docker rm -f ${container} 2>/dev/null || true
88+
docker create --name ${container} ${image}
89+
docker cp ${container}:/out/${name}.mpy src/${name}.mpy
90+
docker rm -f ${container}
91+
"""
92+
93+
[tool.poe.tasks.compile-all]
94+
shell = """
95+
set -euo pipefail
96+
for f in src/*.py; do
97+
NAME=$(basename $f)
98+
uv run poe compile --name ${NAME}
99+
done
100+
"""
101+
102+
[tool.poe.tasks.get-pubkey]
103+
args = [
104+
{ name = "pubkey", help = "the openssl public key in PEM format", options =[
105+
"-p",
106+
"--pubkey"
107+
], default = "pubkey.pem" }
108+
]
109+
shell = """
110+
set -euo pipefail
111+
112+
PUBKEY=$(openssl ec -noout -text -inform PEM -in ${pubkey} -pubin | tr -d '\n')
113+
PUBKEY=$(echo $PUBKEY | sed 's/Public-Key: (256 bit)pub://g' )
114+
PUBKEY=$(echo $PUBKEY | sed 's/ASN1 OID: secp256k1//g' )
115+
PUBKEY=$(echo $PUBKEY | sed 's/://g' )
116+
PUBKEY=$(echo $PUBKEY | sed 's/ //g' )
117+
echo $PUBKEY
118+
"""
119+
120+
[tool.poe.tasks.kapp]
121+
args = [
122+
{ name = "name", help = "Basename of kapp/<name>.py", options = [
123+
"-n",
124+
"--name",
125+
], default = "nostr" },
126+
{ name = "prvkey", help = "Path to private key PEM", options = [
127+
"-p",
128+
"--prvkey",
129+
], default = "privkey.pem" },
130+
{ name = "pubkey", help = "Path to public key PEM", options = [
131+
"-P",
132+
"--pubkey",
133+
], default = "pubkey.pem" },
134+
]
135+
help = "Compile a .py to .mpy kapp and sign it to .mpy.sig"
136+
shell = """
137+
set -euo pipefail
138+
139+
# prepare signature
140+
PUBKEY=$(uv run poe get-pubkey)
141+
142+
# compile
143+
uv run poe compile --name ${name}
144+
145+
# sign kapp
146+
while true; do
147+
openssl dgst \
148+
-sign ${prvkey} \
149+
-keyform PEM \
150+
-sha256 \
151+
-out src/${name}.mpy.sig \
152+
-binary src/${name}.mpy
153+
if [ "$(wc -c < "src/${name}.mpy.sig")" -eq 70 ]; then
154+
echo "Signature is exactly 70 bytes. Done!"
155+
break
156+
fi
157+
done
158+
159+
# distribute
160+
mkdir -p dist
161+
mv src/${name}.mpy dist/${name}.mpy
162+
mv src/${name}.mpy.sig dist/${name}.mpy.sig
163+
"""
164+
165+
[tool.poe.tasks.git-update]
166+
shell = "git submodule update --init --recursive"

0 commit comments

Comments
 (0)