Support address de/serialization from/to future bech32m outputs
This commit is contained in:
parent
40e73b4898
commit
2f7c83b286
2 changed files with 115 additions and 10 deletions
|
@ -4,9 +4,31 @@ const networks = require('./networks');
|
|||
const payments = require('./payments');
|
||||
const bscript = require('./script');
|
||||
const types = require('./types');
|
||||
const { bech32 } = require('bech32');
|
||||
const { bech32, bech32m } = require('bech32');
|
||||
const bs58check = require('bs58check');
|
||||
const typeforce = require('typeforce');
|
||||
const FUTURE_SEGWIT_MAX_SIZE = 40;
|
||||
const FUTURE_SEGWIT_MIN_SIZE = 2;
|
||||
const FUTURE_SEGWIT_MAX_VERSION = 16;
|
||||
const FUTURE_SEGWIT_MIN_VERSION = 1;
|
||||
const FUTURE_SEGWIT_VERSION_DIFF = 0x50;
|
||||
function _toFutureSegwitAddress(output, network) {
|
||||
const data = output.slice(2);
|
||||
if (
|
||||
data.length < FUTURE_SEGWIT_MIN_SIZE ||
|
||||
data.length > FUTURE_SEGWIT_MAX_SIZE
|
||||
)
|
||||
throw new TypeError('Invalid program length for segwit address');
|
||||
const version = output[0] - FUTURE_SEGWIT_VERSION_DIFF;
|
||||
if (
|
||||
version < FUTURE_SEGWIT_MIN_VERSION ||
|
||||
version > FUTURE_SEGWIT_MAX_VERSION
|
||||
)
|
||||
throw new TypeError('Invalid version for segwit address');
|
||||
if (output[1] !== data.length)
|
||||
throw new TypeError('Invalid script for segwit address');
|
||||
return toBech32(data, version, network.bech32);
|
||||
}
|
||||
function fromBase58Check(address) {
|
||||
const payload = bs58check.decode(address);
|
||||
// TODO: 4.0.0, move to "toOutputScript"
|
||||
|
@ -18,10 +40,22 @@ function fromBase58Check(address) {
|
|||
}
|
||||
exports.fromBase58Check = fromBase58Check;
|
||||
function fromBech32(address) {
|
||||
const result = bech32.decode(address);
|
||||
let result;
|
||||
let version;
|
||||
try {
|
||||
result = bech32.decode(address);
|
||||
} catch (e) {}
|
||||
if (result) {
|
||||
version = result.words[0];
|
||||
if (version !== 0) throw new TypeError(address + ' uses wrong encoding');
|
||||
} else {
|
||||
result = bech32m.decode(address);
|
||||
version = result.words[0];
|
||||
if (version === 0) throw new TypeError(address + ' uses wrong encoding');
|
||||
}
|
||||
const data = bech32.fromWords(result.words.slice(1));
|
||||
return {
|
||||
version: result.words[0],
|
||||
version,
|
||||
prefix: result.prefix,
|
||||
data: Buffer.from(data),
|
||||
};
|
||||
|
@ -38,7 +72,9 @@ exports.toBase58Check = toBase58Check;
|
|||
function toBech32(data, version, prefix) {
|
||||
const words = bech32.toWords(data);
|
||||
words.unshift(version);
|
||||
return bech32.encode(prefix, words);
|
||||
return version === 0
|
||||
? bech32.encode(prefix, words)
|
||||
: bech32m.encode(prefix, words);
|
||||
}
|
||||
exports.toBech32 = toBech32;
|
||||
function fromOutputScript(output, network) {
|
||||
|
@ -56,6 +92,9 @@ function fromOutputScript(output, network) {
|
|||
try {
|
||||
return payments.p2wsh({ output, network }).address;
|
||||
} catch (e) {}
|
||||
try {
|
||||
return _toFutureSegwitAddress(output, network);
|
||||
} catch (e) {}
|
||||
throw new Error(bscript.toASM(output) + ' has no matching Address');
|
||||
}
|
||||
exports.fromOutputScript = fromOutputScript;
|
||||
|
@ -83,7 +122,16 @@ function toOutputScript(address, network) {
|
|||
return payments.p2wpkh({ hash: decodeBech32.data }).output;
|
||||
if (decodeBech32.data.length === 32)
|
||||
return payments.p2wsh({ hash: decodeBech32.data }).output;
|
||||
}
|
||||
} else if (
|
||||
decodeBech32.version >= FUTURE_SEGWIT_MIN_VERSION &&
|
||||
decodeBech32.version <= FUTURE_SEGWIT_MAX_VERSION &&
|
||||
decodeBech32.data.length >= FUTURE_SEGWIT_MIN_SIZE &&
|
||||
decodeBech32.data.length <= FUTURE_SEGWIT_MAX_SIZE
|
||||
)
|
||||
return bscript.compile([
|
||||
decodeBech32.version + FUTURE_SEGWIT_VERSION_DIFF,
|
||||
decodeBech32.data,
|
||||
]);
|
||||
}
|
||||
}
|
||||
throw new Error(address + ' has no matching Script');
|
||||
|
|
|
@ -4,7 +4,7 @@ import * as payments from './payments';
|
|||
import * as bscript from './script';
|
||||
import * as types from './types';
|
||||
|
||||
const { bech32 } = require('bech32');
|
||||
const { bech32, bech32m } = require('bech32');
|
||||
const bs58check = require('bs58check');
|
||||
const typeforce = require('typeforce');
|
||||
|
||||
|
@ -19,6 +19,35 @@ export interface Bech32Result {
|
|||
data: Buffer;
|
||||
}
|
||||
|
||||
const FUTURE_SEGWIT_MAX_SIZE: number = 40;
|
||||
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;
|
||||
|
||||
function _toFutureSegwitAddress(output: Buffer, network: Network): string {
|
||||
const data = output.slice(2);
|
||||
|
||||
if (
|
||||
data.length < FUTURE_SEGWIT_MIN_SIZE ||
|
||||
data.length > FUTURE_SEGWIT_MAX_SIZE
|
||||
)
|
||||
throw new TypeError('Invalid program length for segwit address');
|
||||
|
||||
const version = output[0] - FUTURE_SEGWIT_VERSION_DIFF;
|
||||
|
||||
if (
|
||||
version < FUTURE_SEGWIT_MIN_VERSION ||
|
||||
version > FUTURE_SEGWIT_MAX_VERSION
|
||||
)
|
||||
throw new TypeError('Invalid version for segwit address');
|
||||
|
||||
if (output[1] !== data.length)
|
||||
throw new TypeError('Invalid script for segwit address');
|
||||
|
||||
return toBech32(data, version, network.bech32);
|
||||
}
|
||||
|
||||
export function fromBase58Check(address: string): Base58CheckResult {
|
||||
const payload = bs58check.decode(address);
|
||||
|
||||
|
@ -33,11 +62,25 @@ export function fromBase58Check(address: string): Base58CheckResult {
|
|||
}
|
||||
|
||||
export function fromBech32(address: string): Bech32Result {
|
||||
const result = bech32.decode(address);
|
||||
let result;
|
||||
let version;
|
||||
try {
|
||||
result = bech32.decode(address);
|
||||
} catch (e) {}
|
||||
|
||||
if (result) {
|
||||
version = result.words[0];
|
||||
if (version !== 0) throw new TypeError(address + ' uses wrong encoding');
|
||||
} else {
|
||||
result = bech32m.decode(address);
|
||||
version = result.words[0];
|
||||
if (version === 0) throw new TypeError(address + ' uses wrong encoding');
|
||||
}
|
||||
|
||||
const data = bech32.fromWords(result.words.slice(1));
|
||||
|
||||
return {
|
||||
version: result.words[0],
|
||||
version,
|
||||
prefix: result.prefix,
|
||||
data: Buffer.from(data),
|
||||
};
|
||||
|
@ -61,7 +104,9 @@ export function toBech32(
|
|||
const words = bech32.toWords(data);
|
||||
words.unshift(version);
|
||||
|
||||
return bech32.encode(prefix, words);
|
||||
return version === 0
|
||||
? bech32.encode(prefix, words)
|
||||
: bech32m.encode(prefix, words);
|
||||
}
|
||||
|
||||
export function fromOutputScript(output: Buffer, network?: Network): string {
|
||||
|
@ -80,6 +125,9 @@ export function fromOutputScript(output: Buffer, network?: Network): string {
|
|||
try {
|
||||
return payments.p2wsh({ output, network }).address as string;
|
||||
} catch (e) {}
|
||||
try {
|
||||
return _toFutureSegwitAddress(output, network);
|
||||
} catch (e) {}
|
||||
|
||||
throw new Error(bscript.toASM(output) + ' has no matching Address');
|
||||
}
|
||||
|
@ -111,7 +159,16 @@ export function toOutputScript(address: string, network?: Network): Buffer {
|
|||
return payments.p2wpkh({ hash: decodeBech32.data }).output as Buffer;
|
||||
if (decodeBech32.data.length === 32)
|
||||
return payments.p2wsh({ hash: decodeBech32.data }).output as Buffer;
|
||||
}
|
||||
} else if (
|
||||
decodeBech32.version >= FUTURE_SEGWIT_MIN_VERSION &&
|
||||
decodeBech32.version <= FUTURE_SEGWIT_MAX_VERSION &&
|
||||
decodeBech32.data.length >= FUTURE_SEGWIT_MIN_SIZE &&
|
||||
decodeBech32.data.length <= FUTURE_SEGWIT_MAX_SIZE
|
||||
)
|
||||
return bscript.compile([
|
||||
decodeBech32.version + FUTURE_SEGWIT_VERSION_DIFF,
|
||||
decodeBech32.data,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue