Throwaway research spike (spike/ branch, outside pub workspace, no prod deps touched). Validates the open Block 2 decisions before committing a funded round: 1. Deterministic one-way secp256k1 (Nostr) key derived from the Ğ1 root seed via HKDF — user still backs up ONE thing. Reproducible + signs (BIP340). 2. OfferTransport abstraction over Nostr NIP-99 (kind 30402); Offer stays inventory/location-agnostic, geohash coarsened on the wire (tested). 3. publish->discover-by-geohash proven end-to-end against an in-process hermetic mini-relay (~34ms round-trip). Findings + risks + recommendation in docs/design/spike-block2-findings.md. Block 1 suite untouched. 14 tests green, analyzer clean.
54 lines
1.6 KiB
Dart
54 lines
1.6 KiB
Dart
/// Self-contained bech32 encode (BIP-173 checksum), enough for NIP-19
|
|
/// `npub`/`nsec`. Written inline to avoid a dependency's version quirks; NIP-19
|
|
/// is plain bech32 (checksum constant 1), not bech32m.
|
|
const _charset = 'qpzry9x8gf2tvdw0s3jn54khce6mua7l';
|
|
|
|
int _polymod(List<int> values) {
|
|
const gen = [0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3];
|
|
var chk = 1;
|
|
for (final v in values) {
|
|
final top = chk >> 25;
|
|
chk = ((chk & 0x1ffffff) << 5) ^ v;
|
|
for (var i = 0; i < 5; i++) {
|
|
if ((top >> i) & 1 == 1) chk ^= gen[i];
|
|
}
|
|
}
|
|
return chk;
|
|
}
|
|
|
|
List<int> _hrpExpand(String hrp) => [
|
|
...hrp.codeUnits.map((c) => c >> 5),
|
|
0,
|
|
...hrp.codeUnits.map((c) => c & 31),
|
|
];
|
|
|
|
List<int> _createChecksum(String hrp, List<int> data) {
|
|
final values = [..._hrpExpand(hrp), ...data, 0, 0, 0, 0, 0, 0];
|
|
final mod = _polymod(values) ^ 1;
|
|
return List.generate(6, (i) => (mod >> (5 * (5 - i))) & 31);
|
|
}
|
|
|
|
/// Converts 8-bit bytes to 5-bit groups (bech32 payload), padding with zeros.
|
|
List<int> _convertBits8to5(List<int> data) {
|
|
var acc = 0;
|
|
var bits = 0;
|
|
final out = <int>[];
|
|
for (final value in data) {
|
|
acc = (acc << 8) | value;
|
|
bits += 8;
|
|
while (bits >= 5) {
|
|
bits -= 5;
|
|
out.add((acc >> bits) & 31);
|
|
}
|
|
}
|
|
if (bits > 0) out.add((acc << (5 - bits)) & 31);
|
|
return out;
|
|
}
|
|
|
|
/// bech32-encodes [data] (raw bytes) under [hrp] (e.g. `npub`).
|
|
String bech32Encode(String hrp, List<int> data) {
|
|
final five = _convertBits8to5(data);
|
|
final checksum = _createChecksum(hrp, five);
|
|
final combined = [...five, ...checksum];
|
|
return '${hrp}1${combined.map((v) => _charset[v]).join()}';
|
|
}
|