Add base64 chunk encoding (BTC_TX64, protocol v1.1) - #11
Conversation
Hex spends 2 characters per transaction byte; base64 spends 1.33. A Meshtastic text message is a fixed-size box, so the same 187-character message carries 126 transaction bytes instead of 85. Under stop-and-wait ARQ every chunk costs a full round trip, so fewer chunks is proportionally less time on air. The repo's own EXAMPLE_RAW_TX goes from 15 chunks to 10. New BTC_TX64| prefix alongside BTC_TX|. Because the prefix includes the pipe, a v1.0 relay ignores base64 chunks rather than misparsing them. Servers accept both encodings; the client defaults to hex so relays can be upgraded before senders. Sessions that mix encodings are rejected instead of reassembled into a silently corrupt transaction. Decoding happens inside TransactionReassembler, so add_chunk still returns hex and the broadcast path is unchanged. The CLI reads the encoding via getattr, so the GUI and existing callers need no changes. Chunk size is 168 base64 chars (a multiple of 4, so chunks stay whole base64 quanta), matching the message length v1.0 already ships rather than pushing to the 233-byte radio cap, since PKC-encrypted direct messages spend part of the payload on the auth tag. Override with BTCMESH_CHUNK_SIZE_B64 to calibrate for a specific radio or region. Also corrects the CHUNK_SIZE comment: 170 hex chars is 85 bytes, not 100.
|
Partial over-the-air result — the "not verified" note above is now half-resolved. Measured on a real link: two Heltec V3 / WSL V3 nodes, region ANZ, LONG_FAST, 0 hops, SNR ~5.75/6.75, firmware 2.7.11, PKC-encrypted direct messages. I sent
So the 187-character chunks this PR produces have real headroom, and PKC does not eat into the payload the way I assumed when picking the size. I am deliberately not raising Still not verified: a full session end to end (chunk → reassemble → |
|
Correction — please disregard the over-the-air table in my previous comment. It does not show what I said it showed. I treated the Checking the rest of the setup after that:
So the two nodes are not in contact, and nothing was delivered. The only claim those probes actually support is that Withdrawing the suggestion to raise the default to 208 chars until someone can produce a real two-node measurement. Nothing in the PR diff itself changed — it was never sized from that data, and the 471 tests are unaffected. The "not verified over the air" caveat in the description remains accurate, and is now simply complete rather than partial. |
|
Retracting my previous comment — the measurement in it is wrong. Apologies for the noise. I read All hop and signal fields are unpopulated, which is what a locally generated packet looks like. Nothing crossed the air. Two further checks confirm it. The receiving node's So the three probes prove only that Withdrawing the suggestion to raise the default to 208 chars. It rested entirely on that bogus result. The diff itself is unaffected — the chunk size was chosen from |
Hex spends 2 characters per transaction byte; base64 spends 1.33. A Meshtastic text message is a fixed-size box (233 bytes), so the same 187-character message BTCMesh already ships carries 126 transaction bytes instead of 85.
That matters more than the ratio suggests, because stop-and-wait spends a full round trip per chunk — chunk out,
BTC_CHUNK_ACKback — and at LONG_FAST each leg is seconds over a multi-hop mesh.EXAMPLE_RAW_TXin this repoCompatibility
New
BTC_TX64|prefix alongsideBTC_TX|. Since the prefix includes the pipe,"BTC_TX64|..."does not satisfystartswith("BTC_TX|")— a v1.0 relay ignores base64 chunks rather than misparsing them as hex.The client still defaults to hex. Receivers should upgrade before senders, so this PR makes relays understand base64 without changing what any existing client puts on air.
--encoding base64opts in; flipping the default is a separate call once relays have caught up. If a v1.1 client does hit a v1.0 relay, the symptom is silence rather than a NACK, so the abort path now prints a one-line hint pointing at--encoding hex.A session whose chunks arrive under both prefixes is rejected rather than reassembled — concatenating hex and base64 fragments would otherwise produce a silently corrupt transaction.
Design notes
TransactionReassembler, soadd_chunkstill returns hex and the broadcast path, ACK/NACK handling and history are untouched.getattr(args, "encoding", ENCODING_HEX), sobtcmesh_gui.pyand every existing caller work unchanged.BTCMESH_CHUNK_SIZE_B64overrides it for calibration against a specific radio or region.CHUNK_SIZEcomment said 170 hex chars is 100 bytes; it's 85.tests/test_btcmesh_cli.py:79already had it right.Tests
25 new tests in
tests/test_base64_chunking.py(round trip, chunk economics, mixed-encoding rejection, corrupt and truncated base64, message length limits, and a no-radio loopback feeding CLI dry-run output straight into the reassembler).All 446 pre-existing tests pass unmodified.
Not verified
This has not been flown over the air. Verification was the test suite plus a loopback of CLI dry-run output through the reassembler; an end-to-end send needs two Meshtastic nodes and I have one. Worth a real send against a Heltec pair before flipping any default.
Also untouched deliberately:
core/transaction_parser.pyis dead code (nothing importsbasic_sanity_check), so a malformed transaction still burns the full ARQ cycle before Bitcoin Core rejects it. Happy to send that as a separate PR if useful.