bbqr

package module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 22, 2026 License: Unlicense Imports: 9 Imported by: 0

README

bbqr-go

A Go implementation of BBQr ("Better Bitcoin QR"): split binary data into a series of QR-code text payloads and join them back, in any order. Pure standard library, no dependencies.

This is a clean-room port of the public-domain Coinkite reference. It is byte-for-byte compatible for the Hex (H) and Base32 (2) encodings, verified by golden vectors generated directly from the Python reference (see testdata/gen_vectors.py).

Status

Encoding Split (encode) Join (decode)
2 Base32 yes yes
H Hex yes yes
Z Zlib+Base32 not yet yes

Z decode works (raw DEFLATE via the standard library). Z encode is deferred: Go's compress/flate cannot cap the window to the spec's wbits=10 (1 KB), so a Go-produced Z stream could carry back-references a strict 1 KB-window decoder rejects. The main consumer (paper backup of an already-encrypted, incompressible .7z) uses 2 exclusively, so this does not block it.

API

res, err := bbqr.Split(data, bbqr.TypeBinary, bbqr.EncBase32, bbqr.SplitOpts{})
// res.Version, res.Encoding, res.FileType, res.Parts ([]string)

fileType, data, err := bbqr.Join(res.Parts) // any order, dups allowed

hdr, err := bbqr.ParseHeader(res.Parts[0])  // hdr.Encoding/FileType/NumParts/Which

SplitOpts zero values use the reference defaults (MinVersion 5, MaxVersion 40, MinSplit 1, MaxSplit 1295).

Header format

B$        protocol magic (2 chars)
<enc>     '2' Base32 | 'H' Hex | 'Z' Zlib+Base32
<type>    'B' Binary | 'P' PSBT | 'T' Txn | 'U' Unicode | 'J' JSON | 'C' CBOR | 'X' Exec | ...
<NN>      total parts, two base36 digits (1..1295)
<nn>      this part index, two base36 digits (0-based)

Testing

go test ./...                              # unit + golden + Z-decode vectors
# regenerate golden vectors (needs the Python reference installed):
cd testdata && python3 gen_vectors.py > vectors.json

License

Public domain (Unlicense), matching the upstream BBQr reference. See LICENSE.

Documentation

Overview

Package bbqr implements the BBQr ("Better Bitcoin QR") protocol: splitting binary data into a series of QR-code text payloads and joining them back.

This is a clean-room Go port of the public-domain reference implementation by Coinkite (github.com/coinkite/BBQr). It is byte-for-byte compatible with that reference for the Hex ('H') and Base32 ('2') encodings, which is validated by golden vectors generated from the Python reference (see testdata/). The Zlib ('Z') encoding is decode-only for now (Join handles it; Split does not yet emit it - see encode.go).

Each part is an 8-character header followed by the encoded body:

B$        fixed protocol magic (2 chars)
<enc>     one char: '2'=Base32, 'H'=Hex, 'Z'=Zlib+Base32
<type>    one char file type: 'B'=Binary, 'P'=PSBT, ...
<NN>      two base36 digits: total number of parts (1..1295)
<nn>      two base36 digits: index of this part (0-based)

All characters are within the QR "alphanumeric" charset so encoders can use the dense 5.5-bit-per-char mode.

Index

Constants

View Source
const (
	EncBase32 byte = '2'
	EncHex    byte = 'H'
	EncZlib   byte = 'Z'
)

Encoding codes.

View Source
const (
	TypePSBT    byte = 'P'
	TypeTxn     byte = 'T'
	TypeJSON    byte = 'J'
	TypeCBOR    byte = 'C'
	TypeUnicode byte = 'U'
	TypeExec    byte = 'X'
	TypeBinary  byte = 'B'
)

File type codes (FILETYPE_NAMES in the reference).

View Source
const HeaderLen = 8

HeaderLen is the fixed BBQr header length in characters.

Variables

View Source
var ErrEncodeNotImplemented = errors.New("bbqr: requested encoding not implemented on encode side yet (use '2' or 'H')")

ErrEncodeNotImplemented is returned by Split when asked to produce an encoding that is not yet implemented on the encode side (auto/Zlib).

Functions

func Join

func Join(parts []string) (fileType byte, data []byte, err error)

Join reassembles BBQr part strings (in any order, duplicates allowed) into the original bytes and reports the file type. Mirrors the reference join_qrs(): all parts must share the same first 6 header chars, every index in 0..NumParts-1 must be present, and duplicate indices must agree.

Types

type Header struct {
	Encoding byte
	FileType byte
	NumParts int
	Which    int
}

Header holds the parsed fields of a BBQr part header.

func ParseHeader

func ParseHeader(part string) (Header, error)

ParseHeader parses the 8-char header of a single BBQr part string.

type Result

type Result struct {
	Version  int
	Encoding byte
	FileType byte
	Parts    []string
}

Result is the outcome of Split.

func Split

func Split(data []byte, fileType, encoding byte, opts SplitOpts) (Result, error)

Split encodes data into a set of BBQr part strings. fileType must be a known type code; encoding must be '2' (Base32) or 'H' (Hex) for now.

type SplitOpts

type SplitOpts struct {
	MinVersion int
	MaxVersion int
	MinSplit   int
	MaxSplit   int
}

SplitOpts bounds the version/part-count search. Zero fields take the reference defaults: MinVersion=5, MaxVersion=40, MinSplit=1, MaxSplit=1295.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL