mirror of
https://github.com/mineracks/seedhammer-v1-companion.git
synced 2026-06-26 22:01:05 +10:00
Twelve packages lifted from seedhammer/seedhammer @ v1.3.0
(commit 2f071c1d8f23eb7fd39b15fc0acb8874113f801e):
address/ — bitcoin address parsing
backup/ — v1 plate dimensions + UR-coded multi-plate backup
bc/ — Blockchain Commons: ur, fountain, bytewords, urtypes,
xoshiro256 (5 subpkgs)
bip32/ — HD-key derivation
bip39/ — mnemonic seed phrases + 2048-word wordlist
engrave/ — text/QR → MoveTo/LineTo command stream conversion
seedqr/ — SeedQR / CompactSeedQR encoders
image/ — paletted, rgb565, alpha4, ninepatch image formats
nonstandard/ — bitcoin descriptor + script parsing
font/ — bitmap + vector font runtime
font/{comfortaa,poppins,constant,bitmap,vector}/ — actual fonts
driver/mjolnir/ — MarkingWay USB-serial engraver driver
Plus an earlier-aside backup_test.go restored (its deps are now lifted).
Import paths globally rewritten seedhammer.com → mineracks namespace
via single sed pass; verified no orphan refs remain. go.mod adopts
upstream's full dep set plus the replace-directive for the patched
kortschak/qr fork.
go build ./... clean (all 27 packages)
go test ./... clean (12 packages with tests, all passing)
NOT lifted in this commit:
- driver/{wshat,drm,libcamera} (hardware-specific GPIO/LCD/camera —
will be platform-v1/-shaped abstractions instead)
- gui/ (depends on the above; lifts in Phase 2)
- cmd/{controller,...} (Pi binary entrypoints — not needed for the
companion repo)
- zbar/ (QR scanner — needs libcamera)
Next:
- Write the SH1E reference encoder/decoder in engrave/wire/sh1e/
- Lift Gangleri42's cmd/webnfc/ shell + retune to v1 plates
- First buildable composer WASM with a working preview
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
100 lines
2.2 KiB
Go
100 lines
2.2 KiB
Go
package engrave
|
|
|
|
import (
|
|
"image"
|
|
"io"
|
|
"math/rand"
|
|
"reflect"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/kortschak/qr"
|
|
"github.com/mineracks/seedhammer-v1-companion/bip39"
|
|
"github.com/mineracks/seedhammer-v1-companion/font/constant"
|
|
)
|
|
|
|
func TestConstantQR(t *testing.T) {
|
|
rng := rand.New(rand.NewSource(44))
|
|
for i := 0; i < 100; i++ {
|
|
for n := 16; n <= 32; n++ {
|
|
entropy := make([]byte, n)
|
|
if _, err := io.ReadFull(rng, entropy); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
lvl := qr.Q
|
|
cmd, err := constantQR(7, 4, lvl, entropy)
|
|
if err != nil {
|
|
t.Fatalf("entropy: %x: %v", entropy, err)
|
|
}
|
|
qrc, err := qr.Encode(string(entropy), lvl)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
dim := qrc.Size
|
|
want := bitmapForQR(qrc)
|
|
_, _, got := bitmapForQRStatic(dim)
|
|
for _, p := range cmd.plan {
|
|
got.Set(p)
|
|
}
|
|
if !reflect.DeepEqual(got, want) {
|
|
t.Fatalf("entropy: %x: engraving plan doesn't match QR code", entropy)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestConstantString(t *testing.T) {
|
|
s := NewConstantStringer(constant.Font, 1000, bip39.ShortestWord, bip39.LongestWord)
|
|
for i := bip39.Word(0); i < bip39.NumWords; i++ {
|
|
w := strings.ToUpper(bip39.LabelFor(i))
|
|
cmd := s.String(w)
|
|
bounds := image.Rect(0, 0, s.longest*s.dims.X, s.dims.Y)
|
|
moves := measureMoves(cmd)
|
|
if !moves.In(bounds) {
|
|
t.Errorf("%s movement bounds %v are not inside bounds %v", w, moves, bounds)
|
|
}
|
|
}
|
|
}
|
|
|
|
func FuzzConstantQR(f *testing.F) {
|
|
f.Fuzz(func(t *testing.T, entropy []byte) {
|
|
if len(entropy) < 16 {
|
|
return
|
|
}
|
|
if len(entropy) > 32 {
|
|
entropy = entropy[:32]
|
|
}
|
|
if _, err := ConstantQR(1, 3, qr.Q, entropy); err != nil {
|
|
t.Fatalf("entropy: %x: %v", entropy, err)
|
|
}
|
|
if _, err := ConstantQR(1, 3, qr.L, entropy); err != nil {
|
|
t.Fatalf("entropy: %x: %v", entropy, err)
|
|
}
|
|
})
|
|
}
|
|
|
|
func measureMoves(p Plan) image.Rectangle {
|
|
inf := image.Rectangle{Min: image.Pt(1e6, 1e6), Max: image.Pt(-1e6, -1e6)}
|
|
bounds := inf
|
|
p(func(cmd Command) {
|
|
if cmd.Line {
|
|
return
|
|
}
|
|
p := cmd.Coord
|
|
if p.X < bounds.Min.X {
|
|
bounds.Min.X = p.X
|
|
} else if p.X > bounds.Max.X {
|
|
bounds.Max.X = p.X
|
|
}
|
|
if p.Y < bounds.Min.Y {
|
|
bounds.Min.Y = p.Y
|
|
} else if p.Y > bounds.Max.Y {
|
|
bounds.Max.Y = p.Y
|
|
}
|
|
})
|
|
if bounds == inf {
|
|
bounds = image.Rectangle{}
|
|
}
|
|
return bounds
|
|
}
|