From 1c71f6875c55d859db82909ef322532aee9f3531 Mon Sep 17 00:00:00 2001 From: Louis Singer <41042567+louisinger@users.noreply.github.com> Date: Fri, 15 Apr 2022 18:11:13 +0200 Subject: [PATCH] fix serialization of witness version in scriptPubKey (#49) * fix serialization of witness version in scriptPubKey * export blindingDataLike from psbt.ts * build * remove warnings + enrich test * use wip blech32 branch * use FUTURE_SEGWIT_CONSTANT * blech32 from npm (1.1.2) --- package.json | 2 +- src/address.js | 14 +++++------- src/index.d.ts | 2 +- test/integration/taproot.spec.ts | 38 ++++++++++++++++++++++++++++++++ ts_src/address.ts | 16 +++++--------- ts_src/index.ts | 1 + yarn.lock | 8 +++---- 7 files changed, 57 insertions(+), 24 deletions(-) diff --git a/package.json b/package.json index ef476fada..73f03a728 100644 --- a/package.json +++ b/package.json @@ -57,7 +57,7 @@ "bip32": "^2.0.4", "bip66": "^1.1.0", "bitcoin-ops": "^1.4.0", - "blech32": "^1.1.1", + "blech32": "^1.1.2", "bs58check": "^2.0.0", "create-hash": "^1.2.0", "ecpair": "^2.0.1", diff --git a/src/address.js b/src/address.js index 51218d468..2414299cd 100644 --- a/src/address.js +++ b/src/address.js @@ -58,11 +58,6 @@ const FUTURE_SEGWIT_MIN_SIZE = 2; const FUTURE_SEGWIT_MAX_VERSION = 16; const FUTURE_SEGWIT_MIN_VERSION = 1; const FUTURE_SEGWIT_VERSION_DIFF = 0x50; -const FUTURE_SEGWIT_VERSION_WARNING = - 'WARNING: Sending to a future segwit version address can lead to loss of funds. ' + - 'End users MUST be warned carefully in the GUI and asked if they wish to proceed ' + - 'with caution. Wallets should verify the segwit version from the output of fromBech32, ' + - 'then decide when it is safe to use which version of segwit.'; function _toFutureSegwitAddress(output, network) { const data = output.slice(2); if ( @@ -78,7 +73,6 @@ function _toFutureSegwitAddress(output, network) { throw new TypeError('Invalid version for segwit address'); if (output[1] !== data.length) throw new TypeError('Invalid script for segwit address'); - console.warn(FUTURE_SEGWIT_VERSION_WARNING); return toBech32(data, version, network.bech32); } // negative value for confidential types @@ -138,7 +132,12 @@ function fromBlech32(address) { const pubkey = Buffer.from(result.blindingPublicKey, 'hex'); const prg = Buffer.from(result.witness, 'hex'); const data = Buffer.concat([ - Buffer.from([result.witnessVersion, prg.length]), + Buffer.from([ + result.witnessVersion + ? result.witnessVersion + FUTURE_SEGWIT_VERSION_DIFF + : result.witnessVersion, + prg.length, + ]), prg, ]); return { @@ -239,7 +238,6 @@ function toOutputScript(address, network) { decodedBech32.data.length >= FUTURE_SEGWIT_MIN_SIZE && decodedBech32.data.length <= FUTURE_SEGWIT_MAX_SIZE ) { - console.warn(FUTURE_SEGWIT_VERSION_WARNING); return bscript.compile([ decodedBech32.version + FUTURE_SEGWIT_VERSION_DIFF, decodedBech32.data, diff --git a/src/index.d.ts b/src/index.d.ts index adb9f47de..4f6390ef4 100644 --- a/src/index.d.ts +++ b/src/index.d.ts @@ -9,7 +9,7 @@ import * as bip341 from './bip341'; export * from './asset'; export { address, crypto, networks, payments, script, confidential, issuance, bip341, }; export { TaggedHashPrefix } from './crypto'; -export { Psbt, PsbtTxInput, PsbtTxOutput, Signer, SignerAsync, HDSigner, HDSignerAsync, witnessStackToScriptWitness, } from './psbt'; +export { Psbt, PsbtTxInput, PsbtTxOutput, Signer, SignerAsync, HDSigner, HDSignerAsync, witnessStackToScriptWitness, BlindingDataLike, } from './psbt'; export { OPS as opcodes } from './ops'; export { Transaction } from './transaction'; export { Network as NetworkExtended } from './networks'; diff --git a/test/integration/taproot.spec.ts b/test/integration/taproot.spec.ts index 11fc219e4..5c8eebc89 100644 --- a/test/integration/taproot.spec.ts +++ b/test/integration/taproot.spec.ts @@ -23,6 +23,7 @@ import { } from '../../ts_src/bip341'; import { compile, OPS } from '../../ts_src/script'; import { witnessStackToScriptWitness } from '../../ts_src/psbt'; +import * as assert from 'assert'; const bip341 = BIP341Factory(ecc); @@ -37,6 +38,43 @@ describe('liquidjs-lib (transaction with taproot)', () => { 'KwoJAjrautr5EUPVxvnisVgixipMiYGKxykiV8U6e6JtAP9ZURV5', ); + it('should be able to compute confidential address from taproot output script', () => { + const bobScript = compile([bob.publicKey.slice(1), OPS.OP_CHECKSIG]); + + // in this exemple, alice is the internal key (can spend via keypath spend) + // however, the script tree allows bob to spend the coin with a simple p2pkh + const leaves: TaprootLeaf[] = [ + { + scriptHex: bobScript.toString('hex'), + }, + { + scriptHex: + '20b617298552a72ade070667e86ca63b8f5789a9fe8731ef91202a91c9f3459007ac', + }, + ]; + + const hashTree = toHashTree(leaves); + const output = bip341.taprootOutputScript(alice.publicKey, hashTree); + + const unconfidentialAddress = address.fromOutputScript( + output, + networks.regtest, + ); + const confidentialAddress = address.toConfidential( + unconfidentialAddress, + bob.publicKey, + ); + const fromConf = address.fromConfidential(confidentialAddress); + assert.strictEqual(unconfidentialAddress, fromConf.unconfidentialAddress); + + assert.ok(fromConf.blindingKey.equals(bob.publicKey)); + const scriptFromConfidential = address.toOutputScript( + confidentialAddress, + networks.regtest, + ); + assert.ok(scriptFromConfidential.equals(output)); + }); + it('can create (and broadcast via 3PBP) a taproot keyspend Transaction', async () => { const changeAddress = payments.p2pkh({ pubkey: alice.publicKey, diff --git a/ts_src/address.ts b/ts_src/address.ts index b80ded1c3..4fc46aa67 100644 --- a/ts_src/address.ts +++ b/ts_src/address.ts @@ -35,11 +35,6 @@ const FUTURE_SEGWIT_MIN_SIZE: number = 2; const FUTURE_SEGWIT_MAX_VERSION: number = 16; const FUTURE_SEGWIT_MIN_VERSION: number = 1; const FUTURE_SEGWIT_VERSION_DIFF: number = 0x50; -const FUTURE_SEGWIT_VERSION_WARNING: string = - 'WARNING: Sending to a future segwit version address can lead to loss of funds. ' + - 'End users MUST be warned carefully in the GUI and asked if they wish to proceed ' + - 'with caution. Wallets should verify the segwit version from the output of fromBech32, ' + - 'then decide when it is safe to use which version of segwit.'; function _toFutureSegwitAddress(output: Buffer, network: Network): string { const data = output.slice(2); @@ -61,8 +56,6 @@ function _toFutureSegwitAddress(output: Buffer, network: Network): string { if (output[1] !== data.length) throw new TypeError('Invalid script for segwit address'); - console.warn(FUTURE_SEGWIT_VERSION_WARNING); - return toBech32(data, version, network.bech32); } @@ -131,7 +124,12 @@ export function fromBlech32(address: string): Blech32Result { const pubkey = Buffer.from(result.blindingPublicKey, 'hex'); const prg = Buffer.from(result.witness, 'hex'); const data = Buffer.concat([ - Buffer.from([result.witnessVersion, prg.length]), + Buffer.from([ + result.witnessVersion + ? result.witnessVersion + FUTURE_SEGWIT_VERSION_DIFF + : result.witnessVersion, + prg.length, + ]), prg, ]); return { @@ -254,8 +252,6 @@ export function toOutputScript(address: string, network?: Network): Buffer { decodedBech32.data.length >= FUTURE_SEGWIT_MIN_SIZE && decodedBech32.data.length <= FUTURE_SEGWIT_MAX_SIZE ) { - console.warn(FUTURE_SEGWIT_VERSION_WARNING); - return bscript.compile([ decodedBech32.version + FUTURE_SEGWIT_VERSION_DIFF, decodedBech32.data, diff --git a/ts_src/index.ts b/ts_src/index.ts index 287fede1f..de3214649 100644 --- a/ts_src/index.ts +++ b/ts_src/index.ts @@ -28,6 +28,7 @@ export { HDSigner, HDSignerAsync, witnessStackToScriptWitness, + BlindingDataLike, } from './psbt'; export { OPS as opcodes } from './ops'; export { Transaction } from './transaction'; diff --git a/yarn.lock b/yarn.lock index 1d490ac2f..39973645d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -464,10 +464,10 @@ bitcoin-ops@^1.3.0, bitcoin-ops@^1.4.0: resolved "https://registry.yarnpkg.com/bitcoin-ops/-/bitcoin-ops-1.4.1.tgz#e45de620398e22fd4ca6023de43974ff42240278" integrity sha512-pef6gxZFztEhaE9RY9HmWVmiIHqCb2OyS4HPKkpc6CIiiOa3Qmuoylxc5P2EkU3w+5eTSifI9SEZC88idAIGow== -blech32@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/blech32/-/blech32-1.1.1.tgz#b3260d953710c25a2bdac08bfe919d0db1cb3203" - integrity sha512-5EvwgaKzsUMU9En0aAum5Gq48Gp40ODxUZTAPK5gg5BNxjkjDve3Qh/yLEg33wYEaNXL3IFRsnQoky5+bA9tLQ== +blech32@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/blech32/-/blech32-1.1.2.tgz#545459680555e229603241be3b9bf0979f6fbb3a" + integrity sha512-C5qxzoF9KyX88X8Zz18cZ6BOeL0n5/Eg/cDot1frntkArRMwg1djNim5wA6QFWwu0lJ1LN8iiRMN4Lp2kZzdfA== dependencies: long "^4.0.0"