taler-typescript-core

Wallet core logic and WebUIs for various components
Log | Files | Refs | Submodules | README | LICENSE

commit 7739f02be1cfe8bb15bcc4df8b39ac0776b00f00
parent 0dd975e825daf88440824d2452f85c95daf72c57
Author: Florian Dold <dold@taler.net>
Date:   Tue, 21 Jul 2026 10:54:06 +0200

util: test segwit address decoding against BIP-173 and BIP-350

Diffstat:
Apackages/taler-util/src/segwit_addr.test.ts | 76++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 76 insertions(+), 0 deletions(-)

diff --git a/packages/taler-util/src/segwit_addr.test.ts b/packages/taler-util/src/segwit_addr.test.ts @@ -0,0 +1,76 @@ +/* + This file is part of GNU Taler + (C) 2026 Taler Systems S.A. + + GNU Taler is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ + +import assert from "node:assert"; +import { test } from "node:test"; +import { BitcoinBech32 } from "./bech32.js"; +import { Result } from "./result.js"; +import { BitcoinSewgit } from "./segwit_addr.js"; + +// Version 0 witness programs use bech32, version 1 and up use bech32m. +// Test vectors are from BIP-173 and BIP-350. +const BECH32_V0 = "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4"; +const BECH32M_V1 = + "bc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqzk5jj0"; +const BECH32M_V16 = "BC1SW50QGDZ25J"; +// Same programs, but checksummed with the other constant. +const V0_WITH_BECH32M = "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kemeawh"; +const V16_WITH_BECH32 = "BC1SW50QA3JX3S"; + +test("segwit address decoding accepts bech32 and bech32m", (t) => { + const v0 = Result.unpack(BitcoinSewgit.decode(BECH32_V0)); + assert.strictEqual(v0.version, 0); + assert.strictEqual(v0.program.length, 20); + + const v1 = Result.unpack(BitcoinSewgit.decode(BECH32M_V1)); + assert.strictEqual(v1.version, 1); + assert.strictEqual(v1.program.length, 32); + + const v16 = Result.unpack(BitcoinSewgit.decode(BECH32M_V16)); + assert.strictEqual(v16.version, 16); +}); + +test("segwit address decoding rejects the wrong checksum constant", (t) => { + assert.strictEqual(BitcoinSewgit.decode(V0_WITH_BECH32M).tag, "error"); + assert.strictEqual(BitcoinSewgit.decode(V16_WITH_BECH32).tag, "error"); +}); + +test("segwit address decoding honours an explicitly given encoding", (t) => { + assert.strictEqual( + BitcoinSewgit.decode(BECH32_V0, BitcoinBech32.Encodings.BECH32).tag, + "ok", + ); + assert.strictEqual( + BitcoinSewgit.decode(BECH32M_V1, BitcoinBech32.Encodings.BECH32M).tag, + "ok", + ); + assert.strictEqual( + BitcoinSewgit.decode(BECH32_V0, BitcoinBech32.Encodings.BECH32M).tag, + "error", + ); + assert.strictEqual( + BitcoinSewgit.decode(BECH32M_V1, BitcoinBech32.Encodings.BECH32).tag, + "error", + ); +}); + +test("bech32 decoding verifies the checksum without an expected encoding", (t) => { + const corrupted = + BECH32_V0.slice(0, -1) + (BECH32_V0.endsWith("4") ? "5" : "4"); + assert.strictEqual(BitcoinBech32.decode(corrupted).tag, "error"); + assert.strictEqual(BitcoinBech32.decode(BECH32_V0).tag, "ok"); + assert.strictEqual(BitcoinBech32.decode(BECH32M_V1).tag, "ok"); +});