junderw
6 years ago
179 changed files with 11350 additions and 4619 deletions
@ -0,0 +1,4 @@ |
|||||
|
{ |
||||
|
"singleQuote": true, |
||||
|
"trailingComma": "all" |
||||
|
} |
@ -1,16 +1,27 @@ |
|||||
sudo: false |
sudo: false |
||||
language: node_js |
language: node_js |
||||
|
services: |
||||
|
- docker |
||||
|
before_install: |
||||
|
- if [ $TEST_SUITE = "integration" ]; then |
||||
|
docker pull junderw/bitcoinjs-regtest-server && |
||||
|
docker run -d -p 127.0.0.1:8080:8080 junderw/bitcoinjs-regtest-server && |
||||
|
docker ps -a; |
||||
|
fi |
||||
node_js: |
node_js: |
||||
|
- "8" |
||||
- "lts/*" |
- "lts/*" |
||||
- "9" |
|
||||
- "10" |
|
||||
matrix: |
matrix: |
||||
include: |
include: |
||||
- node_js: "lts/*" |
- node_js: "lts/*" |
||||
env: TEST_SUITE=standard |
env: TEST_SUITE=format:ci |
||||
|
- node_js: "lts/*" |
||||
|
env: TEST_SUITE=gitdiff:ci |
||||
|
- node_js: "lts/*" |
||||
|
env: TEST_SUITE=lint |
||||
- node_js: "lts/*" |
- node_js: "lts/*" |
||||
env: TEST_SUITE=coverage |
env: TEST_SUITE=coverage |
||||
env: |
env: |
||||
- TEST_SUITE=unit |
- TEST_SUITE=unit |
||||
- TEST_SUITE=integration |
- TEST_SUITE=integration APIURL=http://127.0.0.1:8080/1 |
||||
script: npm run-script $TEST_SUITE |
script: npm run-script $TEST_SUITE |
||||
|
File diff suppressed because it is too large
@ -1,97 +1,91 @@ |
|||||
const Buffer = require('safe-buffer').Buffer |
'use strict'; |
||||
const bech32 = require('bech32') |
Object.defineProperty(exports, '__esModule', { value: true }); |
||||
const bs58check = require('bs58check') |
const networks = require('./networks'); |
||||
const bscript = require('./script') |
const payments = require('./payments'); |
||||
const networks = require('./networks') |
const bscript = require('./script'); |
||||
const typeforce = require('typeforce') |
const types = require('./types'); |
||||
const types = require('./types') |
const bech32 = require('bech32'); |
||||
const payments = require('./payments') |
const bs58check = require('bs58check'); |
||||
|
const typeforce = require('typeforce'); |
||||
function fromBase58Check (address) { |
function fromBase58Check(address) { |
||||
const payload = bs58check.decode(address) |
const payload = bs58check.decode(address); |
||||
|
|
||||
// TODO: 4.0.0, move to "toOutputScript"
|
// TODO: 4.0.0, move to "toOutputScript"
|
||||
if (payload.length < 21) throw new TypeError(address + ' is too short') |
if (payload.length < 21) throw new TypeError(address + ' is too short'); |
||||
if (payload.length > 21) throw new TypeError(address + ' is too long') |
if (payload.length > 21) throw new TypeError(address + ' is too long'); |
||||
|
const version = payload.readUInt8(0); |
||||
const version = payload.readUInt8(0) |
const hash = payload.slice(1); |
||||
const hash = payload.slice(1) |
return { version, hash }; |
||||
|
|
||||
return { version: version, hash: hash } |
|
||||
} |
} |
||||
|
exports.fromBase58Check = fromBase58Check; |
||||
function fromBech32 (address) { |
function fromBech32(address) { |
||||
const result = bech32.decode(address) |
const result = bech32.decode(address); |
||||
const data = bech32.fromWords(result.words.slice(1)) |
const data = bech32.fromWords(result.words.slice(1)); |
||||
|
|
||||
return { |
return { |
||||
version: result.words[0], |
version: result.words[0], |
||||
prefix: result.prefix, |
prefix: result.prefix, |
||||
data: Buffer.from(data) |
data: Buffer.from(data), |
||||
} |
}; |
||||
} |
} |
||||
|
exports.fromBech32 = fromBech32; |
||||
function toBase58Check (hash, version) { |
function toBase58Check(hash, version) { |
||||
typeforce(types.tuple(types.Hash160bit, types.UInt8), arguments) |
typeforce(types.tuple(types.Hash160bit, types.UInt8), arguments); |
||||
|
const payload = Buffer.allocUnsafe(21); |
||||
const payload = Buffer.allocUnsafe(21) |
payload.writeUInt8(version, 0); |
||||
payload.writeUInt8(version, 0) |
hash.copy(payload, 1); |
||||
hash.copy(payload, 1) |
return bs58check.encode(payload); |
||||
|
|
||||
return bs58check.encode(payload) |
|
||||
} |
} |
||||
|
exports.toBase58Check = toBase58Check; |
||||
function toBech32 (data, version, prefix) { |
function toBech32(data, version, prefix) { |
||||
const words = bech32.toWords(data) |
const words = bech32.toWords(data); |
||||
words.unshift(version) |
words.unshift(version); |
||||
|
return bech32.encode(prefix, words); |
||||
return bech32.encode(prefix, words) |
|
||||
} |
} |
||||
|
exports.toBech32 = toBech32; |
||||
function fromOutputScript (output, network) { |
function fromOutputScript(output, network) { |
||||
network = network || networks.bitcoin |
// TODO: Network
|
||||
|
network = network || networks.bitcoin; |
||||
try { return payments.p2pkh({ output, network }).address } catch (e) {} |
try { |
||||
try { return payments.p2sh({ output, network }).address } catch (e) {} |
return payments.p2pkh({ output, network }).address; |
||||
try { return payments.p2wpkh({ output, network }).address } catch (e) {} |
} catch (e) {} |
||||
try { return payments.p2wsh({ output, network }).address } catch (e) {} |
try { |
||||
|
return payments.p2sh({ output, network }).address; |
||||
throw new Error(bscript.toASM(output) + ' has no matching Address') |
} catch (e) {} |
||||
|
try { |
||||
|
return payments.p2wpkh({ output, network }).address; |
||||
|
} catch (e) {} |
||||
|
try { |
||||
|
return payments.p2wsh({ output, network }).address; |
||||
|
} catch (e) {} |
||||
|
throw new Error(bscript.toASM(output) + ' has no matching Address'); |
||||
} |
} |
||||
|
exports.fromOutputScript = fromOutputScript; |
||||
function toOutputScript (address, network) { |
function toOutputScript(address, network) { |
||||
network = network || networks.bitcoin |
network = network || networks.bitcoin; |
||||
|
let decodeBase58; |
||||
let decode |
let decodeBech32; |
||||
try { |
try { |
||||
decode = fromBase58Check(address) |
decodeBase58 = fromBase58Check(address); |
||||
} catch (e) {} |
} catch (e) {} |
||||
|
if (decodeBase58) { |
||||
if (decode) { |
if (decodeBase58.version === network.pubKeyHash) |
||||
if (decode.version === network.pubKeyHash) return payments.p2pkh({ hash: decode.hash }).output |
return payments.p2pkh({ hash: decodeBase58.hash }).output; |
||||
if (decode.version === network.scriptHash) return payments.p2sh({ hash: decode.hash }).output |
if (decodeBase58.version === network.scriptHash) |
||||
|
return payments.p2sh({ hash: decodeBase58.hash }).output; |
||||
} else { |
} else { |
||||
try { |
try { |
||||
decode = fromBech32(address) |
decodeBech32 = fromBech32(address); |
||||
} catch (e) {} |
} catch (e) {} |
||||
|
if (decodeBech32) { |
||||
if (decode) { |
if (decodeBech32.prefix !== network.bech32) |
||||
if (decode.prefix !== network.bech32) throw new Error(address + ' has an invalid prefix') |
throw new Error(address + ' has an invalid prefix'); |
||||
if (decode.version === 0) { |
if (decodeBech32.version === 0) { |
||||
if (decode.data.length === 20) return payments.p2wpkh({ hash: decode.data }).output |
if (decodeBech32.data.length === 20) |
||||
if (decode.data.length === 32) return payments.p2wsh({ hash: decode.data }).output |
return payments.p2wpkh({ hash: decodeBech32.data }).output; |
||||
|
if (decodeBech32.data.length === 32) |
||||
|
return payments.p2wsh({ hash: decodeBech32.data }).output; |
||||
} |
} |
||||
} |
} |
||||
} |
} |
||||
|
throw new Error(address + ' has no matching Script'); |
||||
throw new Error(address + ' has no matching Script') |
|
||||
} |
|
||||
|
|
||||
module.exports = { |
|
||||
fromBase58Check: fromBase58Check, |
|
||||
fromBech32: fromBech32, |
|
||||
fromOutputScript: fromOutputScript, |
|
||||
toBase58Check: toBase58Check, |
|
||||
toBech32: toBech32, |
|
||||
toOutputScript: toOutputScript |
|
||||
} |
} |
||||
|
exports.toOutputScript = toOutputScript; |
||||
|
@ -1,29 +1,40 @@ |
|||||
|
'use strict'; |
||||
|
Object.defineProperty(exports, '__esModule', { value: true }); |
||||
// https://github.com/feross/buffer/blob/master/index.js#L1127
|
// https://github.com/feross/buffer/blob/master/index.js#L1127
|
||||
function verifuint (value, max) { |
function verifuint(value, max) { |
||||
if (typeof value !== 'number') throw new Error('cannot write a non-number as a number') |
if (typeof value !== 'number') |
||||
if (value < 0) throw new Error('specified a negative value for writing an unsigned value') |
throw new Error('cannot write a non-number as a number'); |
||||
if (value > max) throw new Error('RangeError: value out of range') |
if (value < 0) |
||||
if (Math.floor(value) !== value) throw new Error('value has a fractional component') |
throw new Error('specified a negative value for writing an unsigned value'); |
||||
|
if (value > max) throw new Error('RangeError: value out of range'); |
||||
|
if (Math.floor(value) !== value) |
||||
|
throw new Error('value has a fractional component'); |
||||
} |
} |
||||
|
function readUInt64LE(buffer, offset) { |
||||
function readUInt64LE (buffer, offset) { |
const a = buffer.readUInt32LE(offset); |
||||
const a = buffer.readUInt32LE(offset) |
let b = buffer.readUInt32LE(offset + 4); |
||||
let b = buffer.readUInt32LE(offset + 4) |
b *= 0x100000000; |
||||
b *= 0x100000000 |
verifuint(b + a, 0x001fffffffffffff); |
||||
|
return b + a; |
||||
verifuint(b + a, 0x001fffffffffffff) |
|
||||
return b + a |
|
||||
} |
} |
||||
|
exports.readUInt64LE = readUInt64LE; |
||||
function writeUInt64LE (buffer, value, offset) { |
function writeUInt64LE(buffer, value, offset) { |
||||
verifuint(value, 0x001fffffffffffff) |
verifuint(value, 0x001fffffffffffff); |
||||
|
buffer.writeInt32LE(value & -1, offset); |
||||
buffer.writeInt32LE(value & -1, offset) |
buffer.writeUInt32LE(Math.floor(value / 0x100000000), offset + 4); |
||||
buffer.writeUInt32LE(Math.floor(value / 0x100000000), offset + 4) |
return offset + 8; |
||||
return offset + 8 |
|
||||
} |
} |
||||
|
exports.writeUInt64LE = writeUInt64LE; |
||||
module.exports = { |
function reverseBuffer(buffer) { |
||||
readUInt64LE: readUInt64LE, |
if (buffer.length < 1) return buffer; |
||||
writeUInt64LE: writeUInt64LE |
let j = buffer.length - 1; |
||||
|
let tmp = 0; |
||||
|
for (let i = 0; i < buffer.length / 2; i++) { |
||||
|
tmp = buffer[i]; |
||||
|
buffer[i] = buffer[j]; |
||||
|
buffer[j] = tmp; |
||||
|
j--; |
||||
|
} |
||||
|
return buffer; |
||||
} |
} |
||||
|
exports.reverseBuffer = reverseBuffer; |
||||
|
@ -1,29 +1,35 @@ |
|||||
const createHash = require('create-hash') |
'use strict'; |
||||
|
Object.defineProperty(exports, '__esModule', { value: true }); |
||||
function ripemd160 (buffer) { |
const createHash = require('create-hash'); |
||||
return createHash('rmd160').update(buffer).digest() |
function ripemd160(buffer) { |
||||
|
try { |
||||
|
return createHash('rmd160') |
||||
|
.update(buffer) |
||||
|
.digest(); |
||||
|
} catch (err) { |
||||
|
return createHash('ripemd160') |
||||
|
.update(buffer) |
||||
|
.digest(); |
||||
|
} |
||||
} |
} |
||||
|
exports.ripemd160 = ripemd160; |
||||
function sha1 (buffer) { |
function sha1(buffer) { |
||||
return createHash('sha1').update(buffer).digest() |
return createHash('sha1') |
||||
|
.update(buffer) |
||||
|
.digest(); |
||||
} |
} |
||||
|
exports.sha1 = sha1; |
||||
function sha256 (buffer) { |
function sha256(buffer) { |
||||
return createHash('sha256').update(buffer).digest() |
return createHash('sha256') |
||||
|
.update(buffer) |
||||
|
.digest(); |
||||
} |
} |
||||
|
exports.sha256 = sha256; |
||||
function hash160 (buffer) { |
function hash160(buffer) { |
||||
return ripemd160(sha256(buffer)) |
return ripemd160(sha256(buffer)); |
||||
} |
} |
||||
|
exports.hash160 = hash160; |
||||
function hash256 (buffer) { |
function hash256(buffer) { |
||||
return sha256(sha256(buffer)) |
return sha256(sha256(buffer)); |
||||
} |
|
||||
|
|
||||
module.exports = { |
|
||||
hash160: hash160, |
|
||||
hash256: hash256, |
|
||||
ripemd160: ripemd160, |
|
||||
sha1: sha1, |
|
||||
sha256: sha256 |
|
||||
} |
} |
||||
|
exports.hash256 = hash256; |
||||
|
@ -1,106 +1,105 @@ |
|||||
const ecc = require('tiny-secp256k1') |
'use strict'; |
||||
const randomBytes = require('randombytes') |
Object.defineProperty(exports, '__esModule', { value: true }); |
||||
const typeforce = require('typeforce') |
const NETWORKS = require('./networks'); |
||||
const types = require('./types') |
const types = require('./types'); |
||||
const wif = require('wif') |
const ecc = require('tiny-secp256k1'); |
||||
|
const randomBytes = require('randombytes'); |
||||
const NETWORKS = require('./networks') |
const typeforce = require('typeforce'); |
||||
const isOptions = typeforce.maybe(typeforce.compile({ |
const wif = require('wif'); |
||||
|
const isOptions = typeforce.maybe( |
||||
|
typeforce.compile({ |
||||
compressed: types.maybe(types.Boolean), |
compressed: types.maybe(types.Boolean), |
||||
network: types.maybe(types.Network) |
network: types.maybe(types.Network), |
||||
})) |
}), |
||||
|
); |
||||
function ECPair (d, Q, options) { |
class ECPair { |
||||
options = options || {} |
constructor(__D, __Q, options) { |
||||
|
this.__D = __D; |
||||
this.compressed = options.compressed === undefined ? true : options.compressed |
this.__Q = __Q; |
||||
this.network = options.network || NETWORKS.bitcoin |
if (options === undefined) options = {}; |
||||
|
this.compressed = |
||||
this.__d = d || null |
options.compressed === undefined ? true : options.compressed; |
||||
this.__Q = null |
this.network = options.network || NETWORKS.bitcoin; |
||||
if (Q) this.__Q = ecc.pointCompress(Q, this.compressed) |
if (__Q !== undefined) this.__Q = ecc.pointCompress(__Q, this.compressed); |
||||
} |
} |
||||
|
get privateKey() { |
||||
Object.defineProperty(ECPair.prototype, 'privateKey', { |
return this.__D; |
||||
enumerable: false, |
} |
||||
get: function () { return this.__d } |
get publicKey() { |
||||
}) |
if (!this.__Q) this.__Q = ecc.pointFromScalar(this.__D, this.compressed); |
||||
|
return this.__Q; |
||||
Object.defineProperty(ECPair.prototype, 'publicKey', { get: function () { |
} |
||||
if (!this.__Q) this.__Q = ecc.pointFromScalar(this.__d, this.compressed) |
toWIF() { |
||||
return this.__Q |
if (!this.__D) throw new Error('Missing private key'); |
||||
}}) |
return wif.encode(this.network.wif, this.__D, this.compressed); |
||||
|
} |
||||
ECPair.prototype.toWIF = function () { |
sign(hash, lowR = false) { |
||||
if (!this.__d) throw new Error('Missing private key') |
if (!this.__D) throw new Error('Missing private key'); |
||||
return wif.encode(this.network.wif, this.__d, this.compressed) |
if (lowR === false) { |
||||
} |
return ecc.sign(hash, this.__D); |
||||
|
} else { |
||||
ECPair.prototype.sign = function (hash) { |
let sig = ecc.sign(hash, this.__D); |
||||
if (!this.__d) throw new Error('Missing private key') |
const extraData = Buffer.alloc(32, 0); |
||||
return ecc.sign(hash, this.__d) |
let counter = 0; |
||||
} |
// if first try is lowR, skip the loop
|
||||
|
// for second try and on, add extra entropy counting up
|
||||
ECPair.prototype.verify = function (hash, signature) { |
while (sig[0] > 0x7f) { |
||||
return ecc.verify(hash, this.publicKey, signature) |
counter++; |
||||
|
extraData.writeUIntLE(counter, 0, 6); |
||||
|
sig = ecc.signWithEntropy(hash, this.__D, extraData); |
||||
|
} |
||||
|
return sig; |
||||
|
} |
||||
|
} |
||||
|
verify(hash, signature) { |
||||
|
return ecc.verify(hash, this.publicKey, signature); |
||||
|
} |
||||
} |
} |
||||
|
function fromPrivateKey(buffer, options) { |
||||
function fromPrivateKey (buffer, options) { |
typeforce(types.Buffer256bit, buffer); |
||||
typeforce(types.Buffer256bit, buffer) |
if (!ecc.isPrivate(buffer)) |
||||
if (!ecc.isPrivate(buffer)) throw new TypeError('Private key not in range [1, n)') |
throw new TypeError('Private key not in range [1, n)'); |
||||
typeforce(isOptions, options) |
typeforce(isOptions, options); |
||||
|
return new ECPair(buffer, undefined, options); |
||||
return new ECPair(buffer, null, options) |
|
||||
} |
} |
||||
|
exports.fromPrivateKey = fromPrivateKey; |
||||
function fromPublicKey (buffer, options) { |
function fromPublicKey(buffer, options) { |
||||
typeforce(ecc.isPoint, buffer) |
typeforce(ecc.isPoint, buffer); |
||||
typeforce(isOptions, options) |
typeforce(isOptions, options); |
||||
return new ECPair(null, buffer, options) |
return new ECPair(undefined, buffer, options); |
||||
} |
} |
||||
|
exports.fromPublicKey = fromPublicKey; |
||||
function fromWIF (string, network) { |
function fromWIF(wifString, network) { |
||||
const decoded = wif.decode(string) |
const decoded = wif.decode(wifString); |
||||
const version = decoded.version |
const version = decoded.version; |
||||
|
|
||||
// list of networks?
|
// list of networks?
|
||||
if (types.Array(network)) { |
if (types.Array(network)) { |
||||
network = network.filter(function (x) { |
network = network |
||||
return version === x.wif |
.filter(x => { |
||||
}).pop() |
return version === x.wif; |
||||
|
}) |
||||
if (!network) throw new Error('Unknown network version') |
.pop(); |
||||
|
if (!network) throw new Error('Unknown network version'); |
||||
// otherwise, assume a network object (or default to bitcoin)
|
// otherwise, assume a network object (or default to bitcoin)
|
||||
} else { |
} else { |
||||
network = network || NETWORKS.bitcoin |
network = network || NETWORKS.bitcoin; |
||||
|
if (version !== network.wif) throw new Error('Invalid network version'); |
||||
if (version !== network.wif) throw new Error('Invalid network version') |
|
||||
} |
} |
||||
|
|
||||
return fromPrivateKey(decoded.privateKey, { |
return fromPrivateKey(decoded.privateKey, { |
||||
compressed: decoded.compressed, |
compressed: decoded.compressed, |
||||
network: network |
network: network, |
||||
}) |
}); |
||||
} |
} |
||||
|
exports.fromWIF = fromWIF; |
||||
function makeRandom (options) { |
function makeRandom(options) { |
||||
typeforce(isOptions, options) |
typeforce(isOptions, options); |
||||
options = options || {} |
if (options === undefined) options = {}; |
||||
const rng = options.rng || randomBytes |
const rng = options.rng || randomBytes; |
||||
|
let d; |
||||
let d |
|
||||
do { |
do { |
||||
d = rng(32) |
d = rng(32); |
||||
typeforce(types.Buffer256bit, d) |
typeforce(types.Buffer256bit, d); |
||||
} while (!ecc.isPrivate(d)) |
} while (!ecc.isPrivate(d)); |
||||
|
return fromPrivateKey(d, options); |
||||
return fromPrivateKey(d, options) |
|
||||
} |
|
||||
|
|
||||
module.exports = { |
|
||||
makeRandom, |
|
||||
fromPrivateKey, |
|
||||
fromPublicKey, |
|
||||
fromWIF |
|
||||
} |
} |
||||
|
exports.makeRandom = makeRandom; |
||||
|
@ -1,16 +1,24 @@ |
|||||
const script = require('./script') |
'use strict'; |
||||
|
Object.defineProperty(exports, '__esModule', { value: true }); |
||||
module.exports = { |
const bip32 = require('bip32'); |
||||
Block: require('./block'), |
exports.bip32 = bip32; |
||||
ECPair: require('./ecpair'), |
const address = require('./address'); |
||||
Transaction: require('./transaction'), |
exports.address = address; |
||||
TransactionBuilder: require('./transaction_builder'), |
const crypto = require('./crypto'); |
||||
|
exports.crypto = crypto; |
||||
address: require('./address'), |
const ECPair = require('./ecpair'); |
||||
bip32: require('bip32'), |
exports.ECPair = ECPair; |
||||
crypto: require('./crypto'), |
const networks = require('./networks'); |
||||
networks: require('./networks'), |
exports.networks = networks; |
||||
opcodes: require('bitcoin-ops'), |
const payments = require('./payments'); |
||||
payments: require('./payments'), |
exports.payments = payments; |
||||
script: script |
const script = require('./script'); |
||||
} |
exports.script = script; |
||||
|
var block_1 = require('./block'); |
||||
|
exports.Block = block_1.Block; |
||||
|
var script_1 = require('./script'); |
||||
|
exports.opcodes = script_1.OPS; |
||||
|
var transaction_1 = require('./transaction'); |
||||
|
exports.Transaction = transaction_1.Transaction; |
||||
|
var transaction_builder_1 = require('./transaction_builder'); |
||||
|
exports.TransactionBuilder = transaction_builder_1.TransactionBuilder; |
||||
|
@ -1,27 +1,35 @@ |
|||||
// https://en.bitcoin.it/wiki/List_of_address_prefixes
|
'use strict'; |
||||
// Dogecoin BIP32 is a proposed standard: https://bitcointalk.org/index.php?topic=409731
|
Object.defineProperty(exports, '__esModule', { value: true }); |
||||
|
exports.bitcoin = { |
||||
module.exports = { |
|
||||
bitcoin: { |
|
||||
messagePrefix: '\x18Bitcoin Signed Message:\n', |
messagePrefix: '\x18Bitcoin Signed Message:\n', |
||||
bech32: 'bc', |
bech32: 'bc', |
||||
bip32: { |
bip32: { |
||||
public: 0x0488b21e, |
public: 0x0488b21e, |
||||
private: 0x0488ade4 |
private: 0x0488ade4, |
||||
}, |
}, |
||||
pubKeyHash: 0x00, |
pubKeyHash: 0x00, |
||||
scriptHash: 0x05, |
scriptHash: 0x05, |
||||
wif: 0x80 |
wif: 0x80, |
||||
|
}; |
||||
|
exports.regtest = { |
||||
|
messagePrefix: '\x18Bitcoin Signed Message:\n', |
||||
|
bech32: 'bcrt', |
||||
|
bip32: { |
||||
|
public: 0x043587cf, |
||||
|
private: 0x04358394, |
||||
}, |
}, |
||||
testnet: { |
pubKeyHash: 0x6f, |
||||
|
scriptHash: 0xc4, |
||||
|
wif: 0xef, |
||||
|
}; |
||||
|
exports.testnet = { |
||||
messagePrefix: '\x18Bitcoin Signed Message:\n', |
messagePrefix: '\x18Bitcoin Signed Message:\n', |
||||
bech32: 'tb', |
bech32: 'tb', |
||||
bip32: { |
bip32: { |
||||
public: 0x043587cf, |
public: 0x043587cf, |
||||
private: 0x04358394 |
private: 0x04358394, |
||||
}, |
}, |
||||
pubKeyHash: 0x6f, |
pubKeyHash: 0x6f, |
||||
scriptHash: 0xc4, |
scriptHash: 0xc4, |
||||
wif: 0xef |
wif: 0xef, |
||||
} |
}; |
||||
} |
|
||||
|
@ -1,56 +1,49 @@ |
|||||
const lazy = require('./lazy') |
'use strict'; |
||||
const typef = require('typeforce') |
Object.defineProperty(exports, '__esModule', { value: true }); |
||||
const OPS = require('bitcoin-ops') |
const networks_1 = require('../networks'); |
||||
|
const bscript = require('../script'); |
||||
const bscript = require('../script') |
const lazy = require('./lazy'); |
||||
const BITCOIN_NETWORK = require('../networks').bitcoin |
const typef = require('typeforce'); |
||||
|
const OPS = bscript.OPS; |
||||
function stacksEqual (a, b) { |
function stacksEqual(a, b) { |
||||
if (a.length !== b.length) return false |
if (a.length !== b.length) return false; |
||||
|
return a.every((x, i) => { |
||||
return a.every(function (x, i) { |
return x.equals(b[i]); |
||||
return x.equals(b[i]) |
}); |
||||
}) |
|
||||
} |
} |
||||
|
|
||||
// output: OP_RETURN ...
|
// output: OP_RETURN ...
|
||||
function p2data (a, opts) { |
function p2data(a, opts) { |
||||
if ( |
if (!a.data && !a.output) throw new TypeError('Not enough data'); |
||||
!a.data && |
opts = Object.assign({ validate: true }, opts || {}); |
||||
!a.output |
typef( |
||||
) throw new TypeError('Not enough data') |
{ |
||||
opts = opts || { validate: true } |
|
||||
|
|
||||
typef({ |
|
||||
network: typef.maybe(typef.Object), |
network: typef.maybe(typef.Object), |
||||
output: typef.maybe(typef.Buffer), |
output: typef.maybe(typef.Buffer), |
||||
data: typef.maybe(typef.arrayOf(typef.Buffer)) |
data: typef.maybe(typef.arrayOf(typef.Buffer)), |
||||
}, a) |
}, |
||||
|
a, |
||||
const network = a.network || BITCOIN_NETWORK |
); |
||||
const o = { network } |
const network = a.network || networks_1.bitcoin; |
||||
|
const o = { network }; |
||||
lazy.prop(o, 'output', function () { |
lazy.prop(o, 'output', () => { |
||||
if (!a.data) return |
if (!a.data) return; |
||||
return bscript.compile([OPS.OP_RETURN].concat(a.data)) |
return bscript.compile([OPS.OP_RETURN].concat(a.data)); |
||||
}) |
}); |
||||
lazy.prop(o, 'data', function () { |
lazy.prop(o, 'data', () => { |
||||
if (!a.output) return |
if (!a.output) return; |
||||
return bscript.decompile(a.output).slice(1) |
return bscript.decompile(a.output).slice(1); |
||||
}) |
}); |
||||
|
|
||||
// extended validation
|
// extended validation
|
||||
if (opts.validate) { |
if (opts.validate) { |
||||
if (a.output) { |
if (a.output) { |
||||
const chunks = bscript.decompile(a.output) |
const chunks = bscript.decompile(a.output); |
||||
if (chunks[0] !== OPS.OP_RETURN) throw new TypeError('Output is invalid') |
if (chunks[0] !== OPS.OP_RETURN) throw new TypeError('Output is invalid'); |
||||
if (!chunks.slice(1).every(typef.Buffer)) throw new TypeError('Output is invalid') |
if (!chunks.slice(1).every(typef.Buffer)) |
||||
|
throw new TypeError('Output is invalid'); |
||||
if (a.data && !stacksEqual(a.data, o.data)) throw new TypeError('Data mismatch') |
if (a.data && !stacksEqual(a.data, o.data)) |
||||
|
throw new TypeError('Data mismatch'); |
||||
} |
} |
||||
} |
} |
||||
|
return Object.assign(o, a); |
||||
return Object.assign(o, a) |
|
||||
} |
} |
||||
|
exports.p2data = p2data; |
||||
module.exports = p2data |
|
||||
|
@ -1,12 +1,18 @@ |
|||||
const embed = require('./embed') |
'use strict'; |
||||
const p2ms = require('./p2ms') |
Object.defineProperty(exports, '__esModule', { value: true }); |
||||
const p2pk = require('./p2pk') |
const embed_1 = require('./embed'); |
||||
const p2pkh = require('./p2pkh') |
exports.embed = embed_1.p2data; |
||||
const p2sh = require('./p2sh') |
const p2ms_1 = require('./p2ms'); |
||||
const p2wpkh = require('./p2wpkh') |
exports.p2ms = p2ms_1.p2ms; |
||||
const p2wsh = require('./p2wsh') |
const p2pk_1 = require('./p2pk'); |
||||
|
exports.p2pk = p2pk_1.p2pk; |
||||
module.exports = { embed, p2ms, p2pk, p2pkh, p2sh, p2wpkh, p2wsh } |
const p2pkh_1 = require('./p2pkh'); |
||||
|
exports.p2pkh = p2pkh_1.p2pkh; |
||||
|
const p2sh_1 = require('./p2sh'); |
||||
|
exports.p2sh = p2sh_1.p2sh; |
||||
|
const p2wpkh_1 = require('./p2wpkh'); |
||||
|
exports.p2wpkh = p2wpkh_1.p2wpkh; |
||||
|
const p2wsh_1 = require('./p2wsh'); |
||||
|
exports.p2wsh = p2wsh_1.p2wsh; |
||||
// TODO
|
// TODO
|
||||
// witness commitment
|
// witness commitment
|
||||
|
@ -1,30 +1,31 @@ |
|||||
function prop (object, name, f) { |
'use strict'; |
||||
|
Object.defineProperty(exports, '__esModule', { value: true }); |
||||
|
function prop(object, name, f) { |
||||
Object.defineProperty(object, name, { |
Object.defineProperty(object, name, { |
||||
configurable: true, |
configurable: true, |
||||
enumerable: true, |
enumerable: true, |
||||
get: function () { |
get() { |
||||
let value = f.call(this) |
const _value = f.call(this); |
||||
this[name] = value |
this[name] = _value; |
||||
return value |
return _value; |
||||
}, |
}, |
||||
set: function (value) { |
set(_value) { |
||||
Object.defineProperty(this, name, { |
Object.defineProperty(this, name, { |
||||
configurable: true, |
configurable: true, |
||||
enumerable: true, |
enumerable: true, |
||||
value: value, |
value: _value, |
||||
writable: true |
writable: true, |
||||
}) |
}); |
||||
} |
}, |
||||
}) |
}); |
||||
} |
} |
||||
|
exports.prop = prop; |
||||
function value (f) { |
function value(f) { |
||||
let value |
let _value; |
||||
return function () { |
return () => { |
||||
if (value !== undefined) return value |
if (_value !== undefined) return _value; |
||||
value = f() |
_value = f(); |
||||
return value |
return _value; |
||||
} |
}; |
||||
} |
} |
||||
|
exports.value = value; |
||||
module.exports = { prop, value } |
|
||||
|
@ -1,140 +1,141 @@ |
|||||
const lazy = require('./lazy') |
'use strict'; |
||||
const typef = require('typeforce') |
Object.defineProperty(exports, '__esModule', { value: true }); |
||||
const OPS = require('bitcoin-ops') |
const networks_1 = require('../networks'); |
||||
const ecc = require('tiny-secp256k1') |
const bscript = require('../script'); |
||||
|
const lazy = require('./lazy'); |
||||
const bscript = require('../script') |
const OPS = bscript.OPS; |
||||
const BITCOIN_NETWORK = require('../networks').bitcoin |
const typef = require('typeforce'); |
||||
const OP_INT_BASE = OPS.OP_RESERVED // OP_1 - 1
|
const ecc = require('tiny-secp256k1'); |
||||
|
const OP_INT_BASE = OPS.OP_RESERVED; // OP_1 - 1
|
||||
function stacksEqual (a, b) { |
function stacksEqual(a, b) { |
||||
if (a.length !== b.length) return false |
if (a.length !== b.length) return false; |
||||
|
return a.every((x, i) => { |
||||
return a.every(function (x, i) { |
return x.equals(b[i]); |
||||
return x.equals(b[i]) |
}); |
||||
}) |
|
||||
} |
} |
||||
|
|
||||
// input: OP_0 [signatures ...]
|
// input: OP_0 [signatures ...]
|
||||
// output: m [pubKeys ...] n OP_CHECKMULTISIG
|
// output: m [pubKeys ...] n OP_CHECKMULTISIG
|
||||
function p2ms (a, opts) { |
function p2ms(a, opts) { |
||||
if ( |
if ( |
||||
!a.input && |
!a.input && |
||||
!a.output && |
!a.output && |
||||
!(a.pubkeys && a.m !== undefined) && |
!(a.pubkeys && a.m !== undefined) && |
||||
!a.signatures |
!a.signatures |
||||
) throw new TypeError('Not enough data') |
) |
||||
opts = opts || { validate: true } |
throw new TypeError('Not enough data'); |
||||
|
opts = Object.assign({ validate: true }, opts || {}); |
||||
function isAcceptableSignature (x) { |
function isAcceptableSignature(x) { |
||||
return bscript.isCanonicalScriptSignature(x) || (opts.allowIncomplete && (x === OPS.OP_0)) |
return ( |
||||
|
bscript.isCanonicalScriptSignature(x) || |
||||
|
(opts.allowIncomplete && x === OPS.OP_0) !== undefined |
||||
|
); |
||||
} |
} |
||||
|
typef( |
||||
typef({ |
{ |
||||
network: typef.maybe(typef.Object), |
network: typef.maybe(typef.Object), |
||||
m: typef.maybe(typef.Number), |
m: typef.maybe(typef.Number), |
||||
n: typef.maybe(typef.Number), |
n: typef.maybe(typef.Number), |
||||
output: typef.maybe(typef.Buffer), |
output: typef.maybe(typef.Buffer), |
||||
pubkeys: typef.maybe(typef.arrayOf(ecc.isPoint)), |
pubkeys: typef.maybe(typef.arrayOf(ecc.isPoint)), |
||||
|
|
||||
signatures: typef.maybe(typef.arrayOf(isAcceptableSignature)), |
signatures: typef.maybe(typef.arrayOf(isAcceptableSignature)), |
||||
input: typef.maybe(typef.Buffer) |
input: typef.maybe(typef.Buffer), |
||||
}, a) |
}, |
||||
|
a, |
||||
const network = a.network || BITCOIN_NETWORK |
); |
||||
const o = { network } |
const network = a.network || networks_1.bitcoin; |
||||
|
const o = { network }; |
||||
let chunks |
let chunks = []; |
||||
let decoded = false |
let decoded = false; |
||||
function decode (output) { |
function decode(output) { |
||||
if (decoded) return |
if (decoded) return; |
||||
decoded = true |
decoded = true; |
||||
chunks = bscript.decompile(output) |
chunks = bscript.decompile(output); |
||||
o.m = chunks[0] - OP_INT_BASE |
o.m = chunks[0] - OP_INT_BASE; |
||||
o.n = chunks[chunks.length - 2] - OP_INT_BASE |
o.n = chunks[chunks.length - 2] - OP_INT_BASE; |
||||
o.pubkeys = chunks.slice(1, -2) |
o.pubkeys = chunks.slice(1, -2); |
||||
} |
} |
||||
|
lazy.prop(o, 'output', () => { |
||||
lazy.prop(o, 'output', function () { |
if (!a.m) return; |
||||
if (!a.m) return |
if (!o.n) return; |
||||
if (!o.n) return |
if (!a.pubkeys) return; |
||||
if (!a.pubkeys) return |
return bscript.compile( |
||||
return bscript.compile([].concat( |
[].concat( |
||||
OP_INT_BASE + a.m, |
OP_INT_BASE + a.m, |
||||
a.pubkeys, |
a.pubkeys, |
||||
OP_INT_BASE + o.n, |
OP_INT_BASE + o.n, |
||||
OPS.OP_CHECKMULTISIG |
OPS.OP_CHECKMULTISIG, |
||||
)) |
), |
||||
}) |
); |
||||
lazy.prop(o, 'm', function () { |
}); |
||||
if (!o.output) return |
lazy.prop(o, 'm', () => { |
||||
decode(o.output) |
if (!o.output) return; |
||||
return o.m |
decode(o.output); |
||||
}) |
return o.m; |
||||
lazy.prop(o, 'n', function () { |
}); |
||||
if (!o.pubkeys) return |
lazy.prop(o, 'n', () => { |
||||
return o.pubkeys.length |
if (!o.pubkeys) return; |
||||
}) |
return o.pubkeys.length; |
||||
lazy.prop(o, 'pubkeys', function () { |
}); |
||||
if (!a.output) return |
lazy.prop(o, 'pubkeys', () => { |
||||
decode(a.output) |
if (!a.output) return; |
||||
return o.pubkeys |
decode(a.output); |
||||
}) |
return o.pubkeys; |
||||
lazy.prop(o, 'signatures', function () { |
}); |
||||
if (!a.input) return |
lazy.prop(o, 'signatures', () => { |
||||
return bscript.decompile(a.input).slice(1) |
if (!a.input) return; |
||||
}) |
return bscript.decompile(a.input).slice(1); |
||||
lazy.prop(o, 'input', function () { |
}); |
||||
if (!a.signatures) return |
lazy.prop(o, 'input', () => { |
||||
return bscript.compile([OPS.OP_0].concat(a.signatures)) |
if (!a.signatures) return; |
||||
}) |
return bscript.compile([OPS.OP_0].concat(a.signatures)); |
||||
lazy.prop(o, 'witness', function () { |
}); |
||||
if (!o.input) return |
lazy.prop(o, 'witness', () => { |
||||
return [] |
if (!o.input) return; |
||||
}) |
return []; |
||||
|
}); |
||||
// extended validation
|
// extended validation
|
||||
if (opts.validate) { |
if (opts.validate) { |
||||
if (a.output) { |
if (a.output) { |
||||
decode(a.output) |
decode(a.output); |
||||
if (!typef.Number(chunks[0])) throw new TypeError('Output is invalid') |
if (!typef.Number(chunks[0])) throw new TypeError('Output is invalid'); |
||||
if (!typef.Number(chunks[chunks.length - 2])) throw new TypeError('Output is invalid') |
if (!typef.Number(chunks[chunks.length - 2])) |
||||
if (chunks[chunks.length - 1] !== OPS.OP_CHECKMULTISIG) throw new TypeError('Output is invalid') |
throw new TypeError('Output is invalid'); |
||||
|
if (chunks[chunks.length - 1] !== OPS.OP_CHECKMULTISIG) |
||||
if ( |
throw new TypeError('Output is invalid'); |
||||
o.m <= 0 || |
if (o.m <= 0 || o.n > 16 || o.m > o.n || o.n !== chunks.length - 3) |
||||
o.n > 16 || |
throw new TypeError('Output is invalid'); |
||||
o.m > o.n || |
if (!o.pubkeys.every(x => ecc.isPoint(x))) |
||||
o.n !== chunks.length - 3) throw new TypeError('Output is invalid') |
throw new TypeError('Output is invalid'); |
||||
if (!o.pubkeys.every(x => ecc.isPoint(x))) throw new TypeError('Output is invalid') |
if (a.m !== undefined && a.m !== o.m) throw new TypeError('m mismatch'); |
||||
|
if (a.n !== undefined && a.n !== o.n) throw new TypeError('n mismatch'); |
||||
if (a.m !== undefined && a.m !== o.m) throw new TypeError('m mismatch') |
if (a.pubkeys && !stacksEqual(a.pubkeys, o.pubkeys)) |
||||
if (a.n !== undefined && a.n !== o.n) throw new TypeError('n mismatch') |
throw new TypeError('Pubkeys mismatch'); |
||||
if (a.pubkeys && !stacksEqual(a.pubkeys, o.pubkeys)) throw new TypeError('Pubkeys mismatch') |
|
||||
} |
} |
||||
|
|
||||
if (a.pubkeys) { |
if (a.pubkeys) { |
||||
if (a.n !== undefined && a.n !== a.pubkeys.length) throw new TypeError('Pubkey count mismatch') |
if (a.n !== undefined && a.n !== a.pubkeys.length) |
||||
o.n = a.pubkeys.length |
throw new TypeError('Pubkey count mismatch'); |
||||
|
o.n = a.pubkeys.length; |
||||
if (o.n < o.m) throw new TypeError('Pubkey count cannot be less than m') |
if (o.n < o.m) throw new TypeError('Pubkey count cannot be less than m'); |
||||
} |
} |
||||
|
|
||||
if (a.signatures) { |
if (a.signatures) { |
||||
if (a.signatures.length < o.m) throw new TypeError('Not enough signatures provided') |
if (a.signatures.length < o.m) |
||||
if (a.signatures.length > o.m) throw new TypeError('Too many signatures provided') |
throw new TypeError('Not enough signatures provided'); |
||||
|
if (a.signatures.length > o.m) |
||||
|
throw new TypeError('Too many signatures provided'); |
||||
} |
} |
||||
|
|
||||
if (a.input) { |
if (a.input) { |
||||
if (a.input[0] !== OPS.OP_0) throw new TypeError('Input is invalid') |
if (a.input[0] !== OPS.OP_0) throw new TypeError('Input is invalid'); |
||||
if (o.signatures.length === 0 || !o.signatures.every(isAcceptableSignature)) throw new TypeError('Input has invalid signature(s)') |
if ( |
||||
|
o.signatures.length === 0 || |
||||
if (a.signatures && !stacksEqual(a.signatures.equals(o.signatures))) throw new TypeError('Signature mismatch') |
!o.signatures.every(isAcceptableSignature) |
||||
if (a.m !== undefined && a.m !== a.signatures.length) throw new TypeError('Signature count mismatch') |
) |
||||
|
throw new TypeError('Input has invalid signature(s)'); |
||||
|
if (a.signatures && !stacksEqual(a.signatures, o.signatures)) |
||||
|
throw new TypeError('Signature mismatch'); |
||||
|
if (a.m !== undefined && a.m !== a.signatures.length) |
||||
|
throw new TypeError('Signature count mismatch'); |
||||
} |
} |
||||
} |
} |
||||
|
return Object.assign(o, a); |
||||
return Object.assign(o, a) |
|
||||
} |
} |
||||
|
exports.p2ms = p2ms; |
||||
module.exports = p2ms |
|
||||
|
@ -1,83 +1,72 @@ |
|||||
let lazy = require('./lazy') |
'use strict'; |
||||
let typef = require('typeforce') |
Object.defineProperty(exports, '__esModule', { value: true }); |
||||
let OPS = require('bitcoin-ops') |
const networks_1 = require('../networks'); |
||||
let ecc = require('tiny-secp256k1') |
const bscript = require('../script'); |
||||
|
const lazy = require('./lazy'); |
||||
let bscript = require('../script') |
const typef = require('typeforce'); |
||||
let BITCOIN_NETWORK = require('../networks').bitcoin |
const OPS = bscript.OPS; |
||||
|
const ecc = require('tiny-secp256k1'); |
||||
// input: {signature}
|
// input: {signature}
|
||||
// output: {pubKey} OP_CHECKSIG
|
// output: {pubKey} OP_CHECKSIG
|
||||
function p2pk (a, opts) { |
function p2pk(a, opts) { |
||||
if ( |
if (!a.input && !a.output && !a.pubkey && !a.input && !a.signature) |
||||
!a.input && |
throw new TypeError('Not enough data'); |
||||
!a.output && |
opts = Object.assign({ validate: true }, opts || {}); |
||||
!a.pubkey && |
typef( |
||||
!a.input && |
{ |
||||
!a.signature |
|
||||
) throw new TypeError('Not enough data') |
|
||||
opts = opts || { validate: true } |
|
||||
|
|
||||
typef({ |
|
||||
network: typef.maybe(typef.Object), |
network: typef.maybe(typef.Object), |
||||
output: typef.maybe(typef.Buffer), |
output: typef.maybe(typef.Buffer), |
||||
pubkey: typef.maybe(ecc.isPoint), |
pubkey: typef.maybe(ecc.isPoint), |
||||
|
|
||||
signature: typef.maybe(bscript.isCanonicalScriptSignature), |
signature: typef.maybe(bscript.isCanonicalScriptSignature), |
||||
input: typef.maybe(typef.Buffer) |
input: typef.maybe(typef.Buffer), |
||||
}, a) |
}, |
||||
|
a, |
||||
let _chunks = lazy.value(function () { return bscript.decompile(a.input) }) |
); |
||||
|
const _chunks = lazy.value(() => { |
||||
let network = a.network || BITCOIN_NETWORK |
return bscript.decompile(a.input); |
||||
let o = { network } |
}); |
||||
|
const network = a.network || networks_1.bitcoin; |
||||
lazy.prop(o, 'output', function () { |
const o = { network }; |
||||
if (!a.pubkey) return |
lazy.prop(o, 'output', () => { |
||||
return bscript.compile([ |
if (!a.pubkey) return; |
||||
a.pubkey, |
return bscript.compile([a.pubkey, OPS.OP_CHECKSIG]); |
||||
OPS.OP_CHECKSIG |
}); |
||||
]) |
lazy.prop(o, 'pubkey', () => { |
||||
}) |
if (!a.output) return; |
||||
lazy.prop(o, 'pubkey', function () { |
return a.output.slice(1, -1); |
||||
if (!a.output) return |
}); |
||||
return a.output.slice(1, -1) |
lazy.prop(o, 'signature', () => { |
||||
}) |
if (!a.input) return; |
||||
lazy.prop(o, 'signature', function () { |
return _chunks()[0]; |
||||
if (!a.input) return |
}); |
||||
return _chunks()[0] |
lazy.prop(o, 'input', () => { |
||||
}) |
if (!a.signature) return; |
||||
lazy.prop(o, 'input', function () { |
return bscript.compile([a.signature]); |
||||
if (!a.signature) return |
}); |
||||
return bscript.compile([a.signature]) |
lazy.prop(o, 'witness', () => { |
||||
}) |
if (!o.input) return; |
||||
lazy.prop(o, 'witness', function () { |
return []; |
||||
if (!o.input) return |
}); |
||||
return [] |
|
||||
}) |
|
||||
|
|
||||
// extended validation
|
// extended validation
|
||||
if (opts.validate) { |
if (opts.validate) { |
||||
if (a.pubkey && a.output) { |
|
||||
if (!a.pubkey.equals(o.pubkey)) throw new TypeError('Pubkey mismatch') |
|
||||
} |
|
||||
|
|
||||
if (a.output) { |
if (a.output) { |
||||
if (a.output[a.output.length - 1] !== OPS.OP_CHECKSIG) throw new TypeError('Output is invalid') |
if (a.output[a.output.length - 1] !== OPS.OP_CHECKSIG) |
||||
if (!ecc.isPoint(o.pubkey)) throw new TypeError('Output pubkey is invalid') |
throw new TypeError('Output is invalid'); |
||||
|
if (!ecc.isPoint(o.pubkey)) |
||||
|
throw new TypeError('Output pubkey is invalid'); |
||||
|
if (a.pubkey && !a.pubkey.equals(o.pubkey)) |
||||
|
throw new TypeError('Pubkey mismatch'); |
||||
} |
} |
||||
|
|
||||
if (a.signature) { |
if (a.signature) { |
||||
if (a.input && !a.input.equals(o.input)) throw new TypeError('Input mismatch') |
if (a.input && !a.input.equals(o.input)) |
||||
|
throw new TypeError('Signature mismatch'); |
||||
} |
} |
||||
|
|
||||
if (a.input) { |
if (a.input) { |
||||
if (_chunks().length !== 1) throw new TypeError('Input is invalid') |
if (_chunks().length !== 1) throw new TypeError('Input is invalid'); |
||||
if (!bscript.isCanonicalScriptSignature(_chunks()[0])) throw new TypeError('Input has invalid signature') |
if (!bscript.isCanonicalScriptSignature(o.signature)) |
||||
|
throw new TypeError('Input has invalid signature'); |
||||
} |
} |
||||
} |
} |
||||
|
return Object.assign(o, a); |
||||
return Object.assign(o, a) |
|
||||
} |
} |
||||
|
exports.p2pk = p2pk; |
||||
module.exports = p2pk |
|
||||
|
@ -1,187 +1,178 @@ |
|||||
const lazy = require('./lazy') |
'use strict'; |
||||
const typef = require('typeforce') |
Object.defineProperty(exports, '__esModule', { value: true }); |
||||
const OPS = require('bitcoin-ops') |
const bcrypto = require('../crypto'); |
||||
|
const networks_1 = require('../networks'); |
||||
const bcrypto = require('../crypto') |
const bscript = require('../script'); |
||||
const bscript = require('../script') |
const lazy = require('./lazy'); |
||||
const BITCOIN_NETWORK = require('../networks').bitcoin |
const typef = require('typeforce'); |
||||
const bs58check = require('bs58check') |
const OPS = bscript.OPS; |
||||
|
const bs58check = require('bs58check'); |
||||
function stacksEqual (a, b) { |
function stacksEqual(a, b) { |
||||
if (a.length !== b.length) return false |
if (a.length !== b.length) return false; |
||||
|
return a.every((x, i) => { |
||||
return a.every(function (x, i) { |
return x.equals(b[i]); |
||||
return x.equals(b[i]) |
}); |
||||
}) |
|
||||
} |
} |
||||
|
|
||||
// input: [redeemScriptSig ...] {redeemScript}
|
// input: [redeemScriptSig ...] {redeemScript}
|
||||
// witness: <?>
|
// witness: <?>
|
||||
// output: OP_HASH160 {hash160(redeemScript)} OP_EQUAL
|
// output: OP_HASH160 {hash160(redeemScript)} OP_EQUAL
|
||||
function p2sh (a, opts) { |
function p2sh(a, opts) { |
||||
if ( |
if (!a.address && !a.hash && !a.output && !a.redeem && !a.input) |
||||
!a.address && |
throw new TypeError('Not enough data'); |
||||
!a.hash && |
opts = Object.assign({ validate: true }, opts || {}); |
||||
!a.output && |
typef( |
||||
!a.redeem && |
{ |
||||
!a.input |
|
||||
) throw new TypeError('Not enough data') |
|
||||
opts = opts || { validate: true } |
|
||||
|
|
||||
typef({ |
|
||||
network: typef.maybe(typef.Object), |
network: typef.maybe(typef.Object), |
||||
|
|
||||
address: typef.maybe(typef.String), |
address: typef.maybe(typef.String), |
||||
hash: typef.maybe(typef.BufferN(20)), |
hash: typef.maybe(typef.BufferN(20)), |
||||
output: typef.maybe(typef.BufferN(23)), |
output: typef.maybe(typef.BufferN(23)), |
||||
|
|
||||
redeem: typef.maybe({ |
redeem: typef.maybe({ |
||||
network: typef.maybe(typef.Object), |
network: typef.maybe(typef.Object), |
||||
output: typef.maybe(typef.Buffer), |
output: typef.maybe(typef.Buffer), |
||||
input: typef.maybe(typef.Buffer), |
input: typef.maybe(typef.Buffer), |
||||
witness: typef.maybe(typef.arrayOf(typef.Buffer)) |
witness: typef.maybe(typef.arrayOf(typef.Buffer)), |
||||
}), |
}), |
||||
input: typef.maybe(typef.Buffer), |
input: typef.maybe(typef.Buffer), |
||||
witness: typef.maybe(typef.arrayOf(typef.Buffer)) |
witness: typef.maybe(typef.arrayOf(typef.Buffer)), |
||||
}, a) |
}, |
||||
|
a, |
||||
const network = a.network || BITCOIN_NETWORK |
); |
||||
const o = { network } |
let network = a.network; |
||||
|
if (!network) { |
||||
const _address = lazy.value(function () { |
network = (a.redeem && a.redeem.network) || networks_1.bitcoin; |
||||
const payload = bs58check.decode(a.address) |
} |
||||
const version = payload.readUInt8(0) |
const o = { network }; |
||||
const hash = payload.slice(1) |
const _address = lazy.value(() => { |
||||
return { version, hash } |
const payload = bs58check.decode(a.address); |
||||
}) |
const version = payload.readUInt8(0); |
||||
const _chunks = lazy.value(function () { return bscript.decompile(a.input) }) |
const hash = payload.slice(1); |
||||
const _redeem = lazy.value(function () { |
return { version, hash }; |
||||
const chunks = _chunks() |
}); |
||||
|
const _chunks = lazy.value(() => { |
||||
|
return bscript.decompile(a.input); |
||||
|
}); |
||||
|
const _redeem = lazy.value(() => { |
||||
|
const chunks = _chunks(); |
||||
return { |
return { |
||||
network: network, |
network, |
||||
output: chunks[chunks.length - 1], |
output: chunks[chunks.length - 1], |
||||
input: bscript.compile(chunks.slice(0, -1)), |
input: bscript.compile(chunks.slice(0, -1)), |
||||
witness: a.witness || [] |
witness: a.witness || [], |
||||
} |
}; |
||||
}) |
}); |
||||
|
|
||||
// output dependents
|
// output dependents
|
||||
lazy.prop(o, 'address', function () { |
lazy.prop(o, 'address', () => { |
||||
if (!o.hash) return |
if (!o.hash) return; |
||||
|
const payload = Buffer.allocUnsafe(21); |
||||
const payload = Buffer.allocUnsafe(21) |
payload.writeUInt8(o.network.scriptHash, 0); |
||||
payload.writeUInt8(network.scriptHash, 0) |
o.hash.copy(payload, 1); |
||||
o.hash.copy(payload, 1) |
return bs58check.encode(payload); |
||||
return bs58check.encode(payload) |
}); |
||||
}) |
lazy.prop(o, 'hash', () => { |
||||
lazy.prop(o, 'hash', function () { |
|
||||
// in order of least effort
|
// in order of least effort
|
||||
if (a.output) return a.output.slice(2, 22) |
if (a.output) return a.output.slice(2, 22); |
||||
if (a.address) return _address().hash |
if (a.address) return _address().hash; |
||||
if (o.redeem && o.redeem.output) return bcrypto.hash160(o.redeem.output) |
if (o.redeem && o.redeem.output) return bcrypto.hash160(o.redeem.output); |
||||
}) |
}); |
||||
lazy.prop(o, 'output', function () { |
lazy.prop(o, 'output', () => { |
||||
if (!o.hash) return |
if (!o.hash) return; |
||||
return bscript.compile([ |
return bscript.compile([OPS.OP_HASH160, o.hash, OPS.OP_EQUAL]); |
||||
OPS.OP_HASH160, |
}); |
||||
o.hash, |
|
||||
OPS.OP_EQUAL |
|
||||
]) |
|
||||
}) |
|
||||
|
|
||||
// input dependents
|
// input dependents
|
||||
lazy.prop(o, 'redeem', function () { |
lazy.prop(o, 'redeem', () => { |
||||
if (!a.input) return |
if (!a.input) return; |
||||
return _redeem() |
return _redeem(); |
||||
}) |
}); |
||||
lazy.prop(o, 'input', function () { |
lazy.prop(o, 'input', () => { |
||||
if (!a.redeem || !a.redeem.input || !a.redeem.output) return |
if (!a.redeem || !a.redeem.input || !a.redeem.output) return; |
||||
return bscript.compile([].concat( |
return bscript.compile( |
||||
bscript.decompile(a.redeem.input), |
[].concat(bscript.decompile(a.redeem.input), a.redeem.output), |
||||
a.redeem.output |
); |
||||
)) |
}); |
||||
}) |
lazy.prop(o, 'witness', () => { |
||||
lazy.prop(o, 'witness', function () { |
if (o.redeem && o.redeem.witness) return o.redeem.witness; |
||||
if (o.redeem && o.redeem.witness) return o.redeem.witness |
if (o.input) return []; |
||||
if (o.input) return [] |
}); |
||||
}) |
|
||||
|
|
||||
if (opts.validate) { |
if (opts.validate) { |
||||
let hash |
let hash = Buffer.from([]); |
||||
if (a.address) { |
if (a.address) { |
||||
if (_address().version !== network.scriptHash) throw new TypeError('Invalid version or Network mismatch') |
if (_address().version !== network.scriptHash) |
||||
if (_address().hash.length !== 20) throw new TypeError('Invalid address') |
throw new TypeError('Invalid version or Network mismatch'); |
||||
else hash = _address().hash |
if (_address().hash.length !== 20) throw new TypeError('Invalid address'); |
||||
|
hash = _address().hash; |
||||
} |
} |
||||
|
|
||||
if (a.hash) { |
if (a.hash) { |
||||
if (hash && !hash.equals(a.hash)) throw new TypeError('Hash mismatch') |
if (hash.length > 0 && !hash.equals(a.hash)) |
||||
else hash = a.hash |
throw new TypeError('Hash mismatch'); |
||||
|
else hash = a.hash; |
||||
} |
} |
||||
|
|
||||
if (a.output) { |
if (a.output) { |
||||
if ( |
if ( |
||||
a.output.length !== 23 || |
a.output.length !== 23 || |
||||
a.output[0] !== OPS.OP_HASH160 || |
a.output[0] !== OPS.OP_HASH160 || |
||||
a.output[1] !== 0x14 || |
a.output[1] !== 0x14 || |
||||
a.output[22] !== OPS.OP_EQUAL) throw new TypeError('Output is invalid') |
a.output[22] !== OPS.OP_EQUAL |
||||
const hash2 = a.output.slice(2, 22) |
) |
||||
if (hash && !hash.equals(hash2)) throw new TypeError('Hash mismatch') |
throw new TypeError('Output is invalid'); |
||||
else hash = hash2 |
const hash2 = a.output.slice(2, 22); |
||||
|
if (hash.length > 0 && !hash.equals(hash2)) |
||||
|
throw new TypeError('Hash mismatch'); |
||||
|
else hash = hash2; |
||||
} |
} |
||||
|
|
||||
// inlined to prevent 'no-inner-declarations' failing
|
// inlined to prevent 'no-inner-declarations' failing
|
||||
const checkRedeem = function (redeem) { |
const checkRedeem = redeem => { |
||||
// is the redeem output empty/invalid?
|
// is the redeem output empty/invalid?
|
||||
if (redeem.output) { |
if (redeem.output) { |
||||
const decompile = bscript.decompile(redeem.output) |
const decompile = bscript.decompile(redeem.output); |
||||
if (!decompile || decompile.length < 1) throw new TypeError('Redeem.output too short') |
if (!decompile || decompile.length < 1) |
||||
|
throw new TypeError('Redeem.output too short'); |
||||
// match hash against other sources
|
// match hash against other sources
|
||||
const hash2 = bcrypto.hash160(redeem.output) |
const hash2 = bcrypto.hash160(redeem.output); |
||||
if (hash && !hash.equals(hash2)) throw new TypeError('Hash mismatch') |
if (hash.length > 0 && !hash.equals(hash2)) |
||||
else hash = hash2 |
throw new TypeError('Hash mismatch'); |
||||
|
else hash = hash2; |
||||
} |
} |
||||
|
|
||||
if (redeem.input) { |
if (redeem.input) { |
||||
const hasInput = redeem.input.length > 0 |
const hasInput = redeem.input.length > 0; |
||||
const hasWitness = redeem.witness && redeem.witness.length > 0 |
const hasWitness = redeem.witness && redeem.witness.length > 0; |
||||
if (!hasInput && !hasWitness) throw new TypeError('Empty input') |
if (!hasInput && !hasWitness) throw new TypeError('Empty input'); |
||||
if (hasInput && hasWitness) throw new TypeError('Input and witness provided') |
if (hasInput && hasWitness) |
||||
|
throw new TypeError('Input and witness provided'); |
||||
if (hasInput) { |
if (hasInput) { |
||||
const richunks = bscript.decompile(redeem.input) |
const richunks = bscript.decompile(redeem.input); |
||||
if (!bscript.isPushOnly(richunks)) throw new TypeError('Non push-only scriptSig') |
if (!bscript.isPushOnly(richunks)) |
||||
|
throw new TypeError('Non push-only scriptSig'); |
||||
} |
} |
||||
} |
} |
||||
} |
}; |
||||
|
|
||||
if (a.input) { |
if (a.input) { |
||||
const chunks = _chunks() |
const chunks = _chunks(); |
||||
if (!chunks || chunks.length < 1) throw new TypeError('Input too short') |
if (!chunks || chunks.length < 1) throw new TypeError('Input too short'); |
||||
if (!Buffer.isBuffer(_redeem().output)) throw new TypeError('Input is invalid') |
if (!Buffer.isBuffer(_redeem().output)) |
||||
|
throw new TypeError('Input is invalid'); |
||||
checkRedeem(_redeem()) |
checkRedeem(_redeem()); |
||||
} |
} |
||||
|
|
||||
if (a.redeem) { |
if (a.redeem) { |
||||
if (a.redeem.network && a.redeem.network !== network) throw new TypeError('Network mismatch') |
if (a.redeem.network && a.redeem.network !== network) |
||||
if (o.redeem) { |
throw new TypeError('Network mismatch'); |
||||
if (a.redeem.output && !a.redeem.output.equals(o.redeem.output)) throw new TypeError('Redeem.output mismatch') |
if (a.input) { |
||||
if (a.redeem.input && !a.redeem.input.equals(o.redeem.input)) throw new TypeError('Redeem.input mismatch') |
const redeem = _redeem(); |
||||
|
if (a.redeem.output && !a.redeem.output.equals(redeem.output)) |
||||
|
throw new TypeError('Redeem.output mismatch'); |
||||
|
if (a.redeem.input && !a.redeem.input.equals(redeem.input)) |
||||
|
throw new TypeError('Redeem.input mismatch'); |
||||
} |
} |
||||
|
checkRedeem(a.redeem); |
||||
checkRedeem(a.redeem) |
|
||||
} |
} |
||||
|
|
||||
if (a.witness) { |
if (a.witness) { |
||||
if ( |
if ( |
||||
a.redeem && |
a.redeem && |
||||
a.redeem.witness && |
a.redeem.witness && |
||||
!stacksEqual(a.redeem.witness, a.witness)) throw new TypeError('Witness and redeem.witness mismatch') |
!stacksEqual(a.redeem.witness, a.witness) |
||||
|
) |
||||
|
throw new TypeError('Witness and redeem.witness mismatch'); |
||||
} |
} |
||||
} |
} |
||||
|
return Object.assign(o, a); |
||||
return Object.assign(o, a) |
|
||||
} |
} |
||||
|
exports.p2sh = p2sh; |
||||
module.exports = p2sh |
|
||||
|
@ -1,40 +0,0 @@ |
|||||
{ |
|
||||
"name": "bitcoinjs-playground", |
|
||||
"version": "1.0.0", |
|
||||
"description": "Go nuts!", |
|
||||
"main": "_testnet.js", |
|
||||
"scripts": { |
|
||||
"test": "echo \"Error: no test specified\" && exit 1" |
|
||||
}, |
|
||||
"repository": { |
|
||||
"type": "git", |
|
||||
"url": "git+https://github.com/bitcoinjs/bitcoinjs-playground.git" |
|
||||
}, |
|
||||
"author": "", |
|
||||
"license": "ISC", |
|
||||
"bugs": { |
|
||||
"url": "https://github.com/bitcoinjs/bitcoinjs-playground/issues" |
|
||||
}, |
|
||||
"homepage": "https://github.com/bitcoinjs/bitcoinjs-playground#readme", |
|
||||
"dependencies": { |
|
||||
"async": "^2.5.0", |
|
||||
"bech32": "^1.1.3", |
|
||||
"bip21": "^2.0.1", |
|
||||
"bip32-utils": "^0.11.1", |
|
||||
"bip38": "^2.0.2", |
|
||||
"bip39": "^2.5.0", |
|
||||
"bip69": "^2.1.1", |
|
||||
"bitcoin-ops": "^1.4.1", |
|
||||
"bitcoinjs-lib": "^3.3.2", |
|
||||
"bs58": "^4.0.1", |
|
||||
"bs58check": "^2.1.1", |
|
||||
"cb-http-client": "^0.2.3", |
|
||||
"coinselect": "^3.1.11", |
|
||||
"dhttp": "^2.4.2", |
|
||||
"merkle-lib": "^2.0.10", |
|
||||
"mocha": "^5.0.5", |
|
||||
"tape": "^4.9.0", |
|
||||
"typeforce": "^1.11.4", |
|
||||
"utxo": "^2.0.4" |
|
||||
} |
|
||||
} |
|
@ -1,205 +1,177 @@ |
|||||
const Buffer = require('safe-buffer').Buffer |
'use strict'; |
||||
const bip66 = require('bip66') |
Object.defineProperty(exports, '__esModule', { value: true }); |
||||
const ecc = require('tiny-secp256k1') |
const scriptNumber = require('./script_number'); |
||||
const pushdata = require('pushdata-bitcoin') |
const scriptSignature = require('./script_signature'); |
||||
const typeforce = require('typeforce') |
const types = require('./types'); |
||||
const types = require('./types') |
const bip66 = require('bip66'); |
||||
const scriptNumber = require('./script_number') |
const ecc = require('tiny-secp256k1'); |
||||
|
const pushdata = require('pushdata-bitcoin'); |
||||
const OPS = require('bitcoin-ops') |
const typeforce = require('typeforce'); |
||||
const REVERSE_OPS = require('bitcoin-ops/map') |
exports.OPS = require('bitcoin-ops'); |
||||
const OP_INT_BASE = OPS.OP_RESERVED // OP_1 - 1
|
const REVERSE_OPS = require('bitcoin-ops/map'); |
||||
|
const OP_INT_BASE = exports.OPS.OP_RESERVED; // OP_1 - 1
|
||||
function isOPInt (value) { |
function isOPInt(value) { |
||||
return types.Number(value) && |
return ( |
||||
((value === OPS.OP_0) || |
types.Number(value) && |
||||
(value >= OPS.OP_1 && value <= OPS.OP_16) || |
(value === exports.OPS.OP_0 || |
||||
(value === OPS.OP_1NEGATE)) |
(value >= exports.OPS.OP_1 && value <= exports.OPS.OP_16) || |
||||
|
value === exports.OPS.OP_1NEGATE) |
||||
|
); |
||||
} |
} |
||||
|
function isPushOnlyChunk(value) { |
||||
function isPushOnlyChunk (value) { |
return types.Buffer(value) || isOPInt(value); |
||||
return types.Buffer(value) || isOPInt(value) |
|
||||
} |
} |
||||
|
function isPushOnly(value) { |
||||
function isPushOnly (value) { |
return types.Array(value) && value.every(isPushOnlyChunk); |
||||
return types.Array(value) && value.every(isPushOnlyChunk) |
|
||||
} |
} |
||||
|
exports.isPushOnly = isPushOnly; |
||||
function asMinimalOP (buffer) { |
function asMinimalOP(buffer) { |
||||
if (buffer.length === 0) return OPS.OP_0 |
if (buffer.length === 0) return exports.OPS.OP_0; |
||||
if (buffer.length !== 1) return |
if (buffer.length !== 1) return; |
||||
if (buffer[0] >= 1 && buffer[0] <= 16) return OP_INT_BASE + buffer[0] |
if (buffer[0] >= 1 && buffer[0] <= 16) return OP_INT_BASE + buffer[0]; |
||||
if (buffer[0] === 0x81) return OPS.OP_1NEGATE |
if (buffer[0] === 0x81) return exports.OPS.OP_1NEGATE; |
||||
} |
} |
||||
|
function chunksIsBuffer(buf) { |
||||
function compile (chunks) { |
return Buffer.isBuffer(buf); |
||||
|
} |
||||
|
function chunksIsArray(buf) { |
||||
|
return types.Array(buf); |
||||
|
} |
||||
|
function singleChunkIsBuffer(buf) { |
||||
|
return Buffer.isBuffer(buf); |
||||
|
} |
||||
|
function compile(chunks) { |
||||
// TODO: remove me
|
// TODO: remove me
|
||||
if (Buffer.isBuffer(chunks)) return chunks |
if (chunksIsBuffer(chunks)) return chunks; |
||||
|
typeforce(types.Array, chunks); |
||||
typeforce(types.Array, chunks) |
const bufferSize = chunks.reduce((accum, chunk) => { |
||||
|
|
||||
const bufferSize = chunks.reduce(function (accum, chunk) { |
|
||||
// data chunk
|
// data chunk
|
||||
if (Buffer.isBuffer(chunk)) { |
if (singleChunkIsBuffer(chunk)) { |
||||
// adhere to BIP62.3, minimal push policy
|
// adhere to BIP62.3, minimal push policy
|
||||
if (chunk.length === 1 && asMinimalOP(chunk) !== undefined) { |
if (chunk.length === 1 && asMinimalOP(chunk) !== undefined) { |
||||
return accum + 1 |
return accum + 1; |
||||
} |
} |
||||
|
return accum + pushdata.encodingLength(chunk.length) + chunk.length; |
||||
return accum + pushdata.encodingLength(chunk.length) + chunk.length |
|
||||
} |
} |
||||
|
|
||||
// opcode
|
// opcode
|
||||
return accum + 1 |
return accum + 1; |
||||
}, 0.0) |
}, 0.0); |
||||
|
const buffer = Buffer.allocUnsafe(bufferSize); |
||||
const buffer = Buffer.allocUnsafe(bufferSize) |
let offset = 0; |
||||
let offset = 0 |
chunks.forEach(chunk => { |
||||
|
|
||||
chunks.forEach(function (chunk) { |
|
||||
// data chunk
|
// data chunk
|
||||
if (Buffer.isBuffer(chunk)) { |
if (singleChunkIsBuffer(chunk)) { |
||||
// adhere to BIP62.3, minimal push policy
|
// adhere to BIP62.3, minimal push policy
|
||||
const opcode = asMinimalOP(chunk) |
const opcode = asMinimalOP(chunk); |
||||
if (opcode !== undefined) { |
if (opcode !== undefined) { |
||||
buffer.writeUInt8(opcode, offset) |
buffer.writeUInt8(opcode, offset); |
||||
offset += 1 |
offset += 1; |
||||
return |
return; |
||||
} |
} |
||||
|
offset += pushdata.encode(buffer, chunk.length, offset); |
||||
offset += pushdata.encode(buffer, chunk.length, offset) |
chunk.copy(buffer, offset); |
||||
chunk.copy(buffer, offset) |
offset += chunk.length; |
||||
offset += chunk.length |
|
||||
|
|
||||
// opcode
|
// opcode
|
||||
} else { |
} else { |
||||
buffer.writeUInt8(chunk, offset) |
buffer.writeUInt8(chunk, offset); |
||||
offset += 1 |
offset += 1; |
||||
} |
} |
||||
}) |
}); |
||||
|
if (offset !== buffer.length) throw new Error('Could not decode chunks'); |
||||
if (offset !== buffer.length) throw new Error('Could not decode chunks') |
return buffer; |
||||
return buffer |
|
||||
} |
} |
||||
|
exports.compile = compile; |
||||
function decompile (buffer) { |
function decompile(buffer) { |
||||
// TODO: remove me
|
// TODO: remove me
|
||||
if (types.Array(buffer)) return buffer |
if (chunksIsArray(buffer)) return buffer; |
||||
|
typeforce(types.Buffer, buffer); |
||||
typeforce(types.Buffer, buffer) |
const chunks = []; |
||||
|
let i = 0; |
||||
const chunks = [] |
|
||||
let i = 0 |
|
||||
|
|
||||
while (i < buffer.length) { |
while (i < buffer.length) { |
||||
const opcode = buffer[i] |
const opcode = buffer[i]; |
||||
|
|
||||
// data chunk
|
// data chunk
|
||||
if ((opcode > OPS.OP_0) && (opcode <= OPS.OP_PUSHDATA4)) { |
if (opcode > exports.OPS.OP_0 && opcode <= exports.OPS.OP_PUSHDATA4) { |
||||
const d = pushdata.decode(buffer, i) |
const d = pushdata.decode(buffer, i); |
||||
|
|
||||
// did reading a pushDataInt fail?
|
// did reading a pushDataInt fail?
|
||||
if (d === null) return null |
if (d === null) return null; |
||||
i += d.size |
i += d.size; |
||||
|
|
||||
// attempt to read too much data?
|
// attempt to read too much data?
|
||||
if (i + d.number > buffer.length) return null |
if (i + d.number > buffer.length) return null; |
||||
|
const data = buffer.slice(i, i + d.number); |
||||
const data = buffer.slice(i, i + d.number) |
i += d.number; |
||||
i += d.number |
|
||||
|
|
||||
// decompile minimally
|
// decompile minimally
|
||||
const op = asMinimalOP(data) |
const op = asMinimalOP(data); |
||||
if (op !== undefined) { |
if (op !== undefined) { |
||||
chunks.push(op) |
chunks.push(op); |
||||
} else { |
} else { |
||||
chunks.push(data) |
chunks.push(data); |
||||
} |
} |
||||
|
|
||||
// opcode
|
// opcode
|
||||
} else { |
} else { |
||||
chunks.push(opcode) |
chunks.push(opcode); |
||||
|
i += 1; |
||||
i += 1 |
|
||||
} |
} |
||||
} |
} |
||||
|
return chunks; |
||||
return chunks |
|
||||
} |
} |
||||
|
exports.decompile = decompile; |
||||
function toASM (chunks) { |
function toASM(chunks) { |
||||
if (Buffer.isBuffer(chunks)) { |
if (chunksIsBuffer(chunks)) { |
||||
chunks = decompile(chunks) |
chunks = decompile(chunks); |
||||
} |
} |
||||
|
return chunks |
||||
return chunks.map(function (chunk) { |
.map(chunk => { |
||||
// data?
|
// data?
|
||||
if (Buffer.isBuffer(chunk)) { |
if (singleChunkIsBuffer(chunk)) { |
||||
const op = asMinimalOP(chunk) |
const op = asMinimalOP(chunk); |
||||
if (op === undefined) return chunk.toString('hex') |
if (op === undefined) return chunk.toString('hex'); |
||||
chunk = op |
chunk = op; |
||||
} |
} |
||||
|
|
||||
// opcode!
|
// opcode!
|
||||
return REVERSE_OPS[chunk] |
return REVERSE_OPS[chunk]; |
||||
}).join(' ') |
}) |
||||
|
.join(' '); |
||||
} |
} |
||||
|
exports.toASM = toASM; |
||||
function fromASM (asm) { |
function fromASM(asm) { |
||||
typeforce(types.String, asm) |
typeforce(types.String, asm); |
||||
|
return compile( |
||||
return compile(asm.split(' ').map(function (chunkStr) { |
asm.split(' ').map(chunkStr => { |
||||
// opcode?
|
// opcode?
|
||||
if (OPS[chunkStr] !== undefined) return OPS[chunkStr] |
if (exports.OPS[chunkStr] !== undefined) return exports.OPS[chunkStr]; |
||||
typeforce(types.Hex, chunkStr) |
typeforce(types.Hex, chunkStr); |
||||
|
|
||||
// data!
|
// data!
|
||||
return Buffer.from(chunkStr, 'hex') |
return Buffer.from(chunkStr, 'hex'); |
||||
})) |
}), |
||||
|
); |
||||
} |
} |
||||
|
exports.fromASM = fromASM; |
||||
function toStack (chunks) { |
function toStack(chunks) { |
||||
chunks = decompile(chunks) |
chunks = decompile(chunks); |
||||
typeforce(isPushOnly, chunks) |
typeforce(isPushOnly, chunks); |
||||
|
return chunks.map(op => { |
||||
return chunks.map(function (op) { |
if (singleChunkIsBuffer(op)) return op; |
||||
if (Buffer.isBuffer(op)) return op |
if (op === exports.OPS.OP_0) return Buffer.allocUnsafe(0); |
||||
if (op === OPS.OP_0) return Buffer.allocUnsafe(0) |
return scriptNumber.encode(op - OP_INT_BASE); |
||||
|
}); |
||||
return scriptNumber.encode(op - OP_INT_BASE) |
|
||||
}) |
|
||||
} |
} |
||||
|
exports.toStack = toStack; |
||||
function isCanonicalPubKey (buffer) { |
function isCanonicalPubKey(buffer) { |
||||
return ecc.isPoint(buffer) |
return ecc.isPoint(buffer); |
||||
} |
} |
||||
|
exports.isCanonicalPubKey = isCanonicalPubKey; |
||||
function isDefinedHashType (hashType) { |
function isDefinedHashType(hashType) { |
||||
const hashTypeMod = hashType & ~0x80 |
const hashTypeMod = hashType & ~0x80; |
||||
|
|
||||
// return hashTypeMod > SIGHASH_ALL && hashTypeMod < SIGHASH_SINGLE
|
// return hashTypeMod > SIGHASH_ALL && hashTypeMod < SIGHASH_SINGLE
|
||||
return hashTypeMod > 0x00 && hashTypeMod < 0x04 |
return hashTypeMod > 0x00 && hashTypeMod < 0x04; |
||||
} |
|
||||
|
|
||||
function isCanonicalScriptSignature (buffer) { |
|
||||
if (!Buffer.isBuffer(buffer)) return false |
|
||||
if (!isDefinedHashType(buffer[buffer.length - 1])) return false |
|
||||
|
|
||||
return bip66.check(buffer.slice(0, -1)) |
|
||||
} |
} |
||||
|
exports.isDefinedHashType = isDefinedHashType; |
||||
module.exports = { |
function isCanonicalScriptSignature(buffer) { |
||||
compile: compile, |
if (!Buffer.isBuffer(buffer)) return false; |
||||
decompile: decompile, |
if (!isDefinedHashType(buffer[buffer.length - 1])) return false; |
||||
fromASM: fromASM, |
return bip66.check(buffer.slice(0, -1)); |
||||
toASM: toASM, |
|
||||
toStack: toStack, |
|
||||
|
|
||||
number: require('./script_number'), |
|
||||
signature: require('./script_signature'), |
|
||||
|
|
||||
isCanonicalPubKey: isCanonicalPubKey, |
|
||||
isCanonicalScriptSignature: isCanonicalScriptSignature, |
|
||||
isPushOnly: isPushOnly, |
|
||||
isDefinedHashType: isDefinedHashType |
|
||||
} |
} |
||||
|
exports.isCanonicalScriptSignature = isCanonicalScriptSignature; |
||||
|
// tslint:disable-next-line variable-name
|
||||
|
exports.number = scriptNumber; |
||||
|
exports.signature = scriptSignature; |
||||
|
@ -1,67 +1,61 @@ |
|||||
const Buffer = require('safe-buffer').Buffer |
'use strict'; |
||||
|
Object.defineProperty(exports, '__esModule', { value: true }); |
||||
function decode (buffer, maxLength, minimal) { |
function decode(buffer, maxLength, minimal) { |
||||
maxLength = maxLength || 4 |
maxLength = maxLength || 4; |
||||
minimal = minimal === undefined ? true : minimal |
minimal = minimal === undefined ? true : minimal; |
||||
|
const length = buffer.length; |
||||
const length = buffer.length |
if (length === 0) return 0; |
||||
if (length === 0) return 0 |
if (length > maxLength) throw new TypeError('Script number overflow'); |
||||
if (length > maxLength) throw new TypeError('Script number overflow') |
|
||||
if (minimal) { |
if (minimal) { |
||||
if ((buffer[length - 1] & 0x7f) === 0) { |
if ((buffer[length - 1] & 0x7f) === 0) { |
||||
if (length <= 1 || (buffer[length - 2] & 0x80) === 0) throw new Error('Non-minimally encoded script number') |
if (length <= 1 || (buffer[length - 2] & 0x80) === 0) |
||||
|
throw new Error('Non-minimally encoded script number'); |
||||
} |
} |
||||
} |
} |
||||
|
|
||||
// 40-bit
|
// 40-bit
|
||||
if (length === 5) { |
if (length === 5) { |
||||
const a = buffer.readUInt32LE(0) |
const a = buffer.readUInt32LE(0); |
||||
const b = buffer.readUInt8(4) |
const b = buffer.readUInt8(4); |
||||
|
if (b & 0x80) return -((b & ~0x80) * 0x100000000 + a); |
||||
if (b & 0x80) return -(((b & ~0x80) * 0x100000000) + a) |
return b * 0x100000000 + a; |
||||
return (b * 0x100000000) + a |
|
||||
} |
} |
||||
|
|
||||
// 32-bit / 24-bit / 16-bit / 8-bit
|
// 32-bit / 24-bit / 16-bit / 8-bit
|
||||
let result = 0 |
let result = 0; |
||||
for (var i = 0; i < length; ++i) { |
for (let i = 0; i < length; ++i) { |
||||
result |= buffer[i] << (8 * i) |
result |= buffer[i] << (8 * i); |
||||
} |
} |
||||
|
if (buffer[length - 1] & 0x80) |
||||
if (buffer[length - 1] & 0x80) return -(result & ~(0x80 << (8 * (length - 1)))) |
return -(result & ~(0x80 << (8 * (length - 1)))); |
||||
return result |
return result; |
||||
} |
} |
||||
|
exports.decode = decode; |
||||
function scriptNumSize (i) { |
function scriptNumSize(i) { |
||||
return i > 0x7fffffff ? 5 |
return i > 0x7fffffff |
||||
: i > 0x7fffff ? 4 |
? 5 |
||||
: i > 0x7fff ? 3 |
: i > 0x7fffff |
||||
: i > 0x7f ? 2 |
? 4 |
||||
: i > 0x00 ? 1 |
: i > 0x7fff |
||||
: 0 |
? 3 |
||||
|
: i > 0x7f |
||||
|
? 2 |
||||
|
: i > 0x00 |
||||
|
? 1 |
||||
|
: 0; |
||||
} |
} |
||||
|
function encode(_number) { |
||||
function encode (number) { |
let value = Math.abs(_number); |
||||
let value = Math.abs(number) |
const size = scriptNumSize(value); |
||||
const size = scriptNumSize(value) |
const buffer = Buffer.allocUnsafe(size); |
||||
const buffer = Buffer.allocUnsafe(size) |
const negative = _number < 0; |
||||
const negative = number < 0 |
for (let i = 0; i < size; ++i) { |
||||
|
buffer.writeUInt8(value & 0xff, i); |
||||
for (var i = 0; i < size; ++i) { |
value >>= 8; |
||||
buffer.writeUInt8(value & 0xff, i) |
|
||||
value >>= 8 |
|
||||
} |
} |
||||
|
|
||||
if (buffer[size - 1] & 0x80) { |
if (buffer[size - 1] & 0x80) { |
||||
buffer.writeUInt8(negative ? 0x80 : 0x00, size - 1) |
buffer.writeUInt8(negative ? 0x80 : 0x00, size - 1); |
||||
} else if (negative) { |
} else if (negative) { |
||||
buffer[size - 1] |= 0x80 |
buffer[size - 1] |= 0x80; |
||||
} |
} |
||||
|
return buffer; |
||||
return buffer |
|
||||
} |
|
||||
|
|
||||
module.exports = { |
|
||||
decode: decode, |
|
||||
encode: encode |
|
||||
} |
} |
||||
|
exports.encode = encode; |
||||
|
@ -1,64 +1,52 @@ |
|||||
const bip66 = require('bip66') |
'use strict'; |
||||
const Buffer = require('safe-buffer').Buffer |
Object.defineProperty(exports, '__esModule', { value: true }); |
||||
const typeforce = require('typeforce') |
const types = require('./types'); |
||||
const types = require('./types') |
const bip66 = require('bip66'); |
||||
|
const typeforce = require('typeforce'); |
||||
const ZERO = Buffer.alloc(1, 0) |
const ZERO = Buffer.alloc(1, 0); |
||||
function toDER (x) { |
function toDER(x) { |
||||
let i = 0 |
let i = 0; |
||||
while (x[i] === 0) ++i |
while (x[i] === 0) ++i; |
||||
if (i === x.length) return ZERO |
if (i === x.length) return ZERO; |
||||
x = x.slice(i) |
x = x.slice(i); |
||||
if (x[0] & 0x80) return Buffer.concat([ZERO, x], 1 + x.length) |
if (x[0] & 0x80) return Buffer.concat([ZERO, x], 1 + x.length); |
||||
return x |
return x; |
||||
} |
} |
||||
|
function fromDER(x) { |
||||
function fromDER (x) { |
if (x[0] === 0x00) x = x.slice(1); |
||||
if (x[0] === 0x00) x = x.slice(1) |
const buffer = Buffer.alloc(32, 0); |
||||
const buffer = Buffer.alloc(32, 0) |
const bstart = Math.max(0, 32 - x.length); |
||||
const bstart = Math.max(0, 32 - x.length) |
x.copy(buffer, bstart); |
||||
x.copy(buffer, bstart) |
return buffer; |
||||
return buffer |
|
||||
} |
} |
||||
|
|
||||
// BIP62: 1 byte hashType flag (only 0x01, 0x02, 0x03, 0x81, 0x82 and 0x83 are allowed)
|
// BIP62: 1 byte hashType flag (only 0x01, 0x02, 0x03, 0x81, 0x82 and 0x83 are allowed)
|
||||
function decode (buffer) { |
function decode(buffer) { |
||||
const hashType = buffer.readUInt8(buffer.length - 1) |
const hashType = buffer.readUInt8(buffer.length - 1); |
||||
const hashTypeMod = hashType & ~0x80 |
const hashTypeMod = hashType & ~0x80; |
||||
if (hashTypeMod <= 0 || hashTypeMod >= 4) throw new Error('Invalid hashType ' + hashType) |
if (hashTypeMod <= 0 || hashTypeMod >= 4) |
||||
|
throw new Error('Invalid hashType ' + hashType); |
||||
const decode = bip66.decode(buffer.slice(0, -1)) |
const decoded = bip66.decode(buffer.slice(0, -1)); |
||||
const r = fromDER(decode.r) |
const r = fromDER(decoded.r); |
||||
const s = fromDER(decode.s) |
const s = fromDER(decoded.s); |
||||
|
const signature = Buffer.concat([r, s], 64); |
||||
return { |
return { signature, hashType }; |
||||
signature: Buffer.concat([r, s], 64), |
|
||||
hashType: hashType |
|
||||
} |
|
||||
} |
} |
||||
|
exports.decode = decode; |
||||
function encode (signature, hashType) { |
function encode(signature, hashType) { |
||||
typeforce({ |
typeforce( |
||||
|
{ |
||||
signature: types.BufferN(64), |
signature: types.BufferN(64), |
||||
hashType: types.UInt8 |
hashType: types.UInt8, |
||||
}, { signature, hashType }) |
}, |
||||
|
{ signature, hashType }, |
||||
const hashTypeMod = hashType & ~0x80 |
); |
||||
if (hashTypeMod <= 0 || hashTypeMod >= 4) throw new Error('Invalid hashType ' + hashType) |
const hashTypeMod = hashType & ~0x80; |
||||
|
if (hashTypeMod <= 0 || hashTypeMod >= 4) |
||||
const hashTypeBuffer = Buffer.allocUnsafe(1) |
throw new Error('Invalid hashType ' + hashType); |
||||
hashTypeBuffer.writeUInt8(hashType, 0) |
const hashTypeBuffer = Buffer.allocUnsafe(1); |
||||
|
hashTypeBuffer.writeUInt8(hashType, 0); |
||||
const r = toDER(signature.slice(0, 32)) |
const r = toDER(signature.slice(0, 32)); |
||||
const s = toDER(signature.slice(32, 64)) |
const s = toDER(signature.slice(32, 64)); |
||||
|
return Buffer.concat([bip66.encode(r, s), hashTypeBuffer]); |
||||
return Buffer.concat([ |
|
||||
bip66.encode(r, s), |
|
||||
hashTypeBuffer |
|
||||
]) |
|
||||
} |
|
||||
|
|
||||
module.exports = { |
|
||||
decode: decode, |
|
||||
encode: encode |
|
||||
} |
} |
||||
|
exports.encode = encode; |
||||
|
@ -1,4 +1,6 @@ |
|||||
module.exports = { |
'use strict'; |
||||
input: require('./input'), |
Object.defineProperty(exports, '__esModule', { value: true }); |
||||
output: require('./output') |
const input = require('./input'); |
||||
} |
exports.input = input; |
||||
|
const output = require('./output'); |
||||
|
exports.output = output; |
||||
|
@ -1,23 +1,23 @@ |
|||||
|
'use strict'; |
||||
// OP_0 [signatures ...]
|
// OP_0 [signatures ...]
|
||||
|
Object.defineProperty(exports, '__esModule', { value: true }); |
||||
const bscript = require('../../script') |
const bscript = require('../../script'); |
||||
const OPS = require('bitcoin-ops') |
const script_1 = require('../../script'); |
||||
|
function partialSignature(value) { |
||||
function partialSignature (value) { |
return ( |
||||
return value === OPS.OP_0 || bscript.isCanonicalScriptSignature(value) |
value === script_1.OPS.OP_0 || bscript.isCanonicalScriptSignature(value) |
||||
|
); |
||||
} |
} |
||||
|
function check(script, allowIncomplete) { |
||||
function check (script, allowIncomplete) { |
const chunks = bscript.decompile(script); |
||||
const chunks = bscript.decompile(script) |
if (chunks.length < 2) return false; |
||||
if (chunks.length < 2) return false |
if (chunks[0] !== script_1.OPS.OP_0) return false; |
||||
if (chunks[0] !== OPS.OP_0) return false |
|
||||
|
|
||||
if (allowIncomplete) { |
if (allowIncomplete) { |
||||
return chunks.slice(1).every(partialSignature) |
return chunks.slice(1).every(partialSignature); |
||||
} |
} |
||||
|
return chunks.slice(1).every(bscript.isCanonicalScriptSignature); |
||||
return chunks.slice(1).every(bscript.isCanonicalScriptSignature) |
|
||||
} |
} |
||||
check.toJSON = function () { return 'multisig input' } |
exports.check = check; |
||||
|
check.toJSON = () => { |
||||
module.exports = { check } |
return 'multisig input'; |
||||
|
}; |
||||
|
@ -1,29 +1,27 @@ |
|||||
|
'use strict'; |
||||
// m [pubKeys ...] n OP_CHECKMULTISIG
|
// m [pubKeys ...] n OP_CHECKMULTISIG
|
||||
|
Object.defineProperty(exports, '__esModule', { value: true }); |
||||
const bscript = require('../../script') |
const bscript = require('../../script'); |
||||
const types = require('../../types') |
const script_1 = require('../../script'); |
||||
const OPS = require('bitcoin-ops') |
const types = require('../../types'); |
||||
const OP_INT_BASE = OPS.OP_RESERVED // OP_1 - 1
|
const OP_INT_BASE = script_1.OPS.OP_RESERVED; // OP_1 - 1
|
||||
|
function check(script, allowIncomplete) { |
||||
function check (script, allowIncomplete) { |
const chunks = bscript.decompile(script); |
||||
const chunks = bscript.decompile(script) |
if (chunks.length < 4) return false; |
||||
|
if (chunks[chunks.length - 1] !== script_1.OPS.OP_CHECKMULTISIG) return false; |
||||
if (chunks.length < 4) return false |
if (!types.Number(chunks[0])) return false; |
||||
if (chunks[chunks.length - 1] !== OPS.OP_CHECKMULTISIG) return false |
if (!types.Number(chunks[chunks.length - 2])) return false; |
||||
if (!types.Number(chunks[0])) return false |
const m = chunks[0] - OP_INT_BASE; |
||||
if (!types.Number(chunks[chunks.length - 2])) return false |
const n = chunks[chunks.length - 2] - OP_INT_BASE; |
||||
const m = chunks[0] - OP_INT_BASE |
if (m <= 0) return false; |
||||
const n = chunks[chunks.length - 2] - OP_INT_BASE |
if (n > 16) return false; |
||||
|
if (m > n) return false; |
||||
if (m <= 0) return false |
if (n !== chunks.length - 3) return false; |
||||
if (n > 16) return false |
if (allowIncomplete) return true; |
||||
if (m > n) return false |
const keys = chunks.slice(1, -2); |
||||
if (n !== chunks.length - 3) return false |
return keys.every(bscript.isCanonicalPubKey); |
||||
if (allowIncomplete) return true |
|
||||
|
|
||||
const keys = chunks.slice(1, -2) |
|
||||
return keys.every(bscript.isCanonicalPubKey) |
|
||||
} |
} |
||||
check.toJSON = function () { return 'multi-sig output' } |
exports.check = check; |
||||
|
check.toJSON = () => { |
||||
module.exports = { check } |
return 'multi-sig output'; |
||||
|
}; |
||||
|
@ -1,14 +1,15 @@ |
|||||
|
'use strict'; |
||||
|
Object.defineProperty(exports, '__esModule', { value: true }); |
||||
// OP_RETURN {data}
|
// OP_RETURN {data}
|
||||
|
const bscript = require('../script'); |
||||
const bscript = require('../script') |
const OPS = bscript.OPS; |
||||
const OPS = require('bitcoin-ops') |
function check(script) { |
||||
|
const buffer = bscript.compile(script); |
||||
function check (script) { |
return buffer.length > 1 && buffer[0] === OPS.OP_RETURN; |
||||
const buffer = bscript.compile(script) |
|
||||
|
|
||||
return buffer.length > 1 && |
|
||||
buffer[0] === OPS.OP_RETURN |
|
||||
} |
} |
||||
check.toJSON = function () { return 'null data output' } |
exports.check = check; |
||||
|
check.toJSON = () => { |
||||
module.exports = { output: { check: check } } |
return 'null data output'; |
||||
|
}; |
||||
|
const output = { check }; |
||||
|
exports.output = output; |
||||
|
@ -1,4 +1,6 @@ |
|||||
module.exports = { |
'use strict'; |
||||
input: require('./input'), |
Object.defineProperty(exports, '__esModule', { value: true }); |
||||
output: require('./output') |
const input = require('./input'); |
||||
} |
exports.input = input; |
||||
|
const output = require('./output'); |
||||
|
exports.output = output; |
||||
|
@ -1,15 +1,12 @@ |
|||||
|
'use strict'; |
||||
// {signature}
|
// {signature}
|
||||
|
Object.defineProperty(exports, '__esModule', { value: true }); |
||||
const bscript = require('../../script') |
const bscript = require('../../script'); |
||||
|
function check(script) { |
||||
function check (script) { |
const chunks = bscript.decompile(script); |
||||
const chunks = bscript.decompile(script) |
return chunks.length === 1 && bscript.isCanonicalScriptSignature(chunks[0]); |
||||
|
|
||||
return chunks.length === 1 && |
|
||||
bscript.isCanonicalScriptSignature(chunks[0]) |
|
||||
} |
|
||||
check.toJSON = function () { return 'pubKey input' } |
|
||||
|
|
||||
module.exports = { |
|
||||
check: check |
|
||||
} |
} |
||||
|
exports.check = check; |
||||
|
check.toJSON = () => { |
||||
|
return 'pubKey input'; |
||||
|
}; |
||||
|
@ -1,15 +1,17 @@ |
|||||
|
'use strict'; |
||||
// {pubKey} OP_CHECKSIG
|
// {pubKey} OP_CHECKSIG
|
||||
|
Object.defineProperty(exports, '__esModule', { value: true }); |
||||
const bscript = require('../../script') |
const bscript = require('../../script'); |
||||
const OPS = require('bitcoin-ops') |
const script_1 = require('../../script'); |
||||
|
function check(script) { |
||||
function check (script) { |
const chunks = bscript.decompile(script); |
||||
const chunks = bscript.decompile(script) |
return ( |
||||
|
chunks.length === 2 && |
||||
return chunks.length === 2 && |
|
||||
bscript.isCanonicalPubKey(chunks[0]) && |
bscript.isCanonicalPubKey(chunks[0]) && |
||||
chunks[1] === OPS.OP_CHECKSIG |
chunks[1] === script_1.OPS.OP_CHECKSIG |
||||
|
); |
||||
} |
} |
||||
check.toJSON = function () { return 'pubKey output' } |
exports.check = check; |
||||
|
check.toJSON = () => { |
||||
module.exports = { check } |
return 'pubKey output'; |
||||
|
}; |
||||
|
@ -1,4 +1,6 @@ |
|||||
module.exports = { |
'use strict'; |
||||
input: require('./input'), |
Object.defineProperty(exports, '__esModule', { value: true }); |
||||
output: require('./output') |
const input = require('./input'); |
||||
} |
exports.input = input; |
||||
|
const output = require('./output'); |
||||
|
exports.output = output; |
||||
|
@ -1,14 +1,16 @@ |
|||||
|
'use strict'; |
||||
// {signature} {pubKey}
|
// {signature} {pubKey}
|
||||
|
Object.defineProperty(exports, '__esModule', { value: true }); |
||||
const bscript = require('../../script') |
const bscript = require('../../script'); |
||||
|
function check(script) { |
||||
function check (script) { |
const chunks = bscript.decompile(script); |
||||
const chunks = bscript.decompile(script) |
return ( |
||||
|
chunks.length === 2 && |
||||
return chunks.length === 2 && |
|
||||
bscript.isCanonicalScriptSignature(chunks[0]) && |
bscript.isCanonicalScriptSignature(chunks[0]) && |
||||
bscript.isCanonicalPubKey(chunks[1]) |
bscript.isCanonicalPubKey(chunks[1]) |
||||
|
); |
||||
} |
} |
||||
check.toJSON = function () { return 'pubKeyHash input' } |
exports.check = check; |
||||
|
check.toJSON = () => { |
||||
module.exports = { check } |
return 'pubKeyHash input'; |
||||
|
}; |
||||
|
@ -1,18 +1,20 @@ |
|||||
|
'use strict'; |
||||
// OP_DUP OP_HASH160 {pubKeyHash} OP_EQUALVERIFY OP_CHECKSIG
|
// OP_DUP OP_HASH160 {pubKeyHash} OP_EQUALVERIFY OP_CHECKSIG
|
||||
|
Object.defineProperty(exports, '__esModule', { value: true }); |
||||
const bscript = require('../../script') |
const bscript = require('../../script'); |
||||
const OPS = require('bitcoin-ops') |
const script_1 = require('../../script'); |
||||
|
function check(script) { |
||||
function check (script) { |
const buffer = bscript.compile(script); |
||||
const buffer = bscript.compile(script) |
return ( |
||||
|
buffer.length === 25 && |
||||
return buffer.length === 25 && |
buffer[0] === script_1.OPS.OP_DUP && |
||||
buffer[0] === OPS.OP_DUP && |
buffer[1] === script_1.OPS.OP_HASH160 && |
||||
buffer[1] === OPS.OP_HASH160 && |
|
||||
buffer[2] === 0x14 && |
buffer[2] === 0x14 && |
||||
buffer[23] === OPS.OP_EQUALVERIFY && |
buffer[23] === script_1.OPS.OP_EQUALVERIFY && |
||||
buffer[24] === OPS.OP_CHECKSIG |
buffer[24] === script_1.OPS.OP_CHECKSIG |
||||
|
); |
||||
} |
} |
||||
check.toJSON = function () { return 'pubKeyHash output' } |
exports.check = check; |
||||
|
check.toJSON = () => { |
||||
module.exports = { check } |
return 'pubKeyHash output'; |
||||
|
}; |
||||
|
@ -1,4 +1,6 @@ |
|||||
module.exports = { |
'use strict'; |
||||
input: require('./input'), |
Object.defineProperty(exports, '__esModule', { value: true }); |
||||
output: require('./output') |
const input = require('./input'); |
||||
} |
exports.input = input; |
||||
|
const output = require('./output'); |
||||
|
exports.output = output; |
||||
|
@ -1,48 +1,50 @@ |
|||||
|
'use strict'; |
||||
// <scriptSig> {serialized scriptPubKey script}
|
// <scriptSig> {serialized scriptPubKey script}
|
||||
|
Object.defineProperty(exports, '__esModule', { value: true }); |
||||
const Buffer = require('safe-buffer').Buffer |
const bscript = require('../../script'); |
||||
const bscript = require('../../script') |
const p2ms = require('../multisig'); |
||||
|
const p2pk = require('../pubkey'); |
||||
const p2ms = require('../multisig/') |
const p2pkh = require('../pubkeyhash'); |
||||
const p2pk = require('../pubkey/') |
const p2wpkho = require('../witnesspubkeyhash/output'); |
||||
const p2pkh = require('../pubkeyhash/') |
const p2wsho = require('../witnessscripthash/output'); |
||||
const p2wpkho = require('../witnesspubkeyhash/output') |
function check(script, allowIncomplete) { |
||||
const p2wsho = require('../witnessscripthash/output') |
const chunks = bscript.decompile(script); |
||||
|
if (chunks.length < 1) return false; |
||||
function check (script, allowIncomplete) { |
const lastChunk = chunks[chunks.length - 1]; |
||||
const chunks = bscript.decompile(script) |
if (!Buffer.isBuffer(lastChunk)) return false; |
||||
if (chunks.length < 1) return false |
const scriptSigChunks = bscript.decompile( |
||||
|
bscript.compile(chunks.slice(0, -1)), |
||||
const lastChunk = chunks[chunks.length - 1] |
); |
||||
if (!Buffer.isBuffer(lastChunk)) return false |
const redeemScriptChunks = bscript.decompile(lastChunk); |
||||
|
|
||||
const scriptSigChunks = bscript.decompile(bscript.compile(chunks.slice(0, -1))) |
|
||||
const redeemScriptChunks = bscript.decompile(lastChunk) |
|
||||
|
|
||||
// is redeemScript a valid script?
|
// is redeemScript a valid script?
|
||||
if (!redeemScriptChunks) return false |
if (!redeemScriptChunks) return false; |
||||
|
|
||||
// is redeemScriptSig push only?
|
// is redeemScriptSig push only?
|
||||
if (!bscript.isPushOnly(scriptSigChunks)) return false |
if (!bscript.isPushOnly(scriptSigChunks)) return false; |
||||
|
|
||||
// is witness?
|
// is witness?
|
||||
if (chunks.length === 1) { |
if (chunks.length === 1) { |
||||
return p2wsho.check(redeemScriptChunks) || |
return ( |
||||
p2wpkho.check(redeemScriptChunks) |
p2wsho.check(redeemScriptChunks) || p2wpkho.check(redeemScriptChunks) |
||||
|
); |
||||
} |
} |
||||
|
|
||||
// match types
|
// match types
|
||||
if (p2pkh.input.check(scriptSigChunks) && |
if ( |
||||
p2pkh.output.check(redeemScriptChunks)) return true |
p2pkh.input.check(scriptSigChunks) && |
||||
|
p2pkh.output.check(redeemScriptChunks) |
||||
if (p2ms.input.check(scriptSigChunks, allowIncomplete) && |
) |
||||
p2ms.output.check(redeemScriptChunks)) return true |
return true; |
||||
|
if ( |
||||
if (p2pk.input.check(scriptSigChunks) && |
p2ms.input.check(scriptSigChunks, allowIncomplete) && |
||||
p2pk.output.check(redeemScriptChunks)) return true |
p2ms.output.check(redeemScriptChunks) |
||||
|
) |
||||
return false |
return true; |
||||
|
if ( |
||||
|
p2pk.input.check(scriptSigChunks) && |
||||
|
p2pk.output.check(redeemScriptChunks) |
||||
|
) |
||||
|
return true; |
||||
|
return false; |
||||
} |
} |
||||
check.toJSON = function () { return 'scriptHash input' } |
exports.check = check; |
||||
|
check.toJSON = () => { |
||||
module.exports = { check } |
return 'scriptHash input'; |
||||
|
}; |
||||
|
@ -1,16 +1,18 @@ |
|||||
|
'use strict'; |
||||
// OP_HASH160 {scriptHash} OP_EQUAL
|
// OP_HASH160 {scriptHash} OP_EQUAL
|
||||
|
Object.defineProperty(exports, '__esModule', { value: true }); |
||||
const bscript = require('../../script') |
const bscript = require('../../script'); |
||||
const OPS = require('bitcoin-ops') |
const script_1 = require('../../script'); |
||||
|
function check(script) { |
||||
function check (script) { |
const buffer = bscript.compile(script); |
||||
const buffer = bscript.compile(script) |
return ( |
||||
|
buffer.length === 23 && |
||||
return buffer.length === 23 && |
buffer[0] === script_1.OPS.OP_HASH160 && |
||||
buffer[0] === OPS.OP_HASH160 && |
|
||||
buffer[1] === 0x14 && |
buffer[1] === 0x14 && |
||||
buffer[22] === OPS.OP_EQUAL |
buffer[22] === script_1.OPS.OP_EQUAL |
||||
|
); |
||||
} |
} |
||||
check.toJSON = function () { return 'scriptHash output' } |
exports.check = check; |
||||
|
check.toJSON = () => { |
||||
module.exports = { check } |
return 'scriptHash output'; |
||||
|
}; |
||||
|
@ -1,3 +1,4 @@ |
|||||
module.exports = { |
'use strict'; |
||||
output: require('./output') |
Object.defineProperty(exports, '__esModule', { value: true }); |
||||
} |
const output = require('./output'); |
||||
|
exports.output = output; |
||||
|
@ -1,42 +1,34 @@ |
|||||
|
'use strict'; |
||||
// OP_RETURN {aa21a9ed} {commitment}
|
// OP_RETURN {aa21a9ed} {commitment}
|
||||
|
Object.defineProperty(exports, '__esModule', { value: true }); |
||||
const Buffer = require('safe-buffer').Buffer |
const bscript = require('../../script'); |
||||
const bscript = require('../../script') |
const script_1 = require('../../script'); |
||||
const types = require('../../types') |
const types = require('../../types'); |
||||
const typeforce = require('typeforce') |
const typeforce = require('typeforce'); |
||||
const OPS = require('bitcoin-ops') |
const HEADER = Buffer.from('aa21a9ed', 'hex'); |
||||
|
function check(script) { |
||||
const HEADER = Buffer.from('aa21a9ed', 'hex') |
const buffer = bscript.compile(script); |
||||
|
return ( |
||||
function check (script) { |
buffer.length > 37 && |
||||
const buffer = bscript.compile(script) |
buffer[0] === script_1.OPS.OP_RETURN && |
||||
|
|
||||
return buffer.length > 37 && |
|
||||
buffer[0] === OPS.OP_RETURN && |
|
||||
buffer[1] === 0x24 && |
buffer[1] === 0x24 && |
||||
buffer.slice(2, 6).equals(HEADER) |
buffer.slice(2, 6).equals(HEADER) |
||||
|
); |
||||
} |
} |
||||
|
exports.check = check; |
||||
check.toJSON = function () { return 'Witness commitment output' } |
check.toJSON = () => { |
||||
|
return 'Witness commitment output'; |
||||
function encode (commitment) { |
}; |
||||
typeforce(types.Hash256bit, commitment) |
function encode(commitment) { |
||||
|
typeforce(types.Hash256bit, commitment); |
||||
const buffer = Buffer.allocUnsafe(36) |
const buffer = Buffer.allocUnsafe(36); |
||||
HEADER.copy(buffer, 0) |
HEADER.copy(buffer, 0); |
||||
commitment.copy(buffer, 4) |
commitment.copy(buffer, 4); |
||||
|
return bscript.compile([script_1.OPS.OP_RETURN, buffer]); |
||||
return bscript.compile([OPS.OP_RETURN, buffer]) |
|
||||
} |
} |
||||
|
exports.encode = encode; |
||||
function decode (buffer) { |
function decode(buffer) { |
||||
typeforce(check, buffer) |
typeforce(check, buffer); |
||||
|
return bscript.decompile(buffer)[1].slice(4, 36); |
||||
return bscript.decompile(buffer)[1].slice(4, 36) |
|
||||
} |
|
||||
|
|
||||
module.exports = { |
|
||||
check: check, |
|
||||
decode: decode, |
|
||||
encode: encode |
|
||||
} |
} |
||||
|
exports.decode = decode; |
||||
|
@ -1,4 +1,6 @@ |
|||||
module.exports = { |
'use strict'; |
||||
input: require('./input'), |
Object.defineProperty(exports, '__esModule', { value: true }); |
||||
output: require('./output') |
const input = require('./input'); |
||||
} |
exports.input = input; |
||||
|
const output = require('./output'); |
||||
|
exports.output = output; |
||||
|
@ -1,18 +1,19 @@ |
|||||
|
'use strict'; |
||||
// {signature} {pubKey}
|
// {signature} {pubKey}
|
||||
|
Object.defineProperty(exports, '__esModule', { value: true }); |
||||
const bscript = require('../../script') |
const bscript = require('../../script'); |
||||
|
function isCompressedCanonicalPubKey(pubKey) { |
||||
function isCompressedCanonicalPubKey (pubKey) { |
return bscript.isCanonicalPubKey(pubKey) && pubKey.length === 33; |
||||
return bscript.isCanonicalPubKey(pubKey) && pubKey.length === 33 |
|
||||
} |
} |
||||
|
function check(script) { |
||||
function check (script) { |
const chunks = bscript.decompile(script); |
||||
const chunks = bscript.decompile(script) |
return ( |
||||
|
chunks.length === 2 && |
||||
return chunks.length === 2 && |
|
||||
bscript.isCanonicalScriptSignature(chunks[0]) && |
bscript.isCanonicalScriptSignature(chunks[0]) && |
||||
isCompressedCanonicalPubKey(chunks[1]) |
isCompressedCanonicalPubKey(chunks[1]) |
||||
|
); |
||||
} |
} |
||||
check.toJSON = function () { return 'witnessPubKeyHash input' } |
exports.check = check; |
||||
|
check.toJSON = () => { |
||||
module.exports = { check } |
return 'witnessPubKeyHash input'; |
||||
|
}; |
||||
|
@ -1,17 +1,17 @@ |
|||||
|
'use strict'; |
||||
// OP_0 {pubKeyHash}
|
// OP_0 {pubKeyHash}
|
||||
|
Object.defineProperty(exports, '__esModule', { value: true }); |
||||
const bscript = require('../../script') |
const bscript = require('../../script'); |
||||
const OPS = require('bitcoin-ops') |
const script_1 = require('../../script'); |
||||
|
function check(script) { |
||||
function check (script) { |
const buffer = bscript.compile(script); |
||||
const buffer = bscript.compile(script) |
return ( |
||||
|
buffer.length === 22 && |
||||
return buffer.length === 22 && |
buffer[0] === script_1.OPS.OP_0 && |
||||
buffer[0] === OPS.OP_0 && |
|
||||
buffer[1] === 0x14 |
buffer[1] === 0x14 |
||||
|
); |
||||
} |
} |
||||
check.toJSON = function () { return 'Witness pubKeyHash output' } |
exports.check = check; |
||||
|
check.toJSON = () => { |
||||
module.exports = { |
return 'Witness pubKeyHash output'; |
||||
check |
}; |
||||
} |
|
||||
|
@ -1,4 +1,6 @@ |
|||||
module.exports = { |
'use strict'; |
||||
input: require('./input'), |
Object.defineProperty(exports, '__esModule', { value: true }); |
||||
output: require('./output') |
const input = require('./input'); |
||||
} |
exports.input = input; |
||||
|
const output = require('./output'); |
||||
|
exports.output = output; |
||||
|
@ -1,39 +1,39 @@ |
|||||
|
'use strict'; |
||||
// <scriptSig> {serialized scriptPubKey script}
|
// <scriptSig> {serialized scriptPubKey script}
|
||||
|
Object.defineProperty(exports, '__esModule', { value: true }); |
||||
const bscript = require('../../script') |
const bscript = require('../../script'); |
||||
const types = require('../../types') |
const typeforce = require('typeforce'); |
||||
const typeforce = require('typeforce') |
const p2ms = require('../multisig'); |
||||
|
const p2pk = require('../pubkey'); |
||||
const p2ms = require('../multisig/') |
const p2pkh = require('../pubkeyhash'); |
||||
const p2pk = require('../pubkey/') |
function check(chunks, allowIncomplete) { |
||||
const p2pkh = require('../pubkeyhash/') |
typeforce(typeforce.Array, chunks); |
||||
|
if (chunks.length < 1) return false; |
||||
function check (chunks, allowIncomplete) { |
const witnessScript = chunks[chunks.length - 1]; |
||||
typeforce(types.Array, chunks) |
if (!Buffer.isBuffer(witnessScript)) return false; |
||||
if (chunks.length < 1) return false |
const witnessScriptChunks = bscript.decompile(witnessScript); |
||||
|
|
||||
const witnessScript = chunks[chunks.length - 1] |
|
||||
if (!Buffer.isBuffer(witnessScript)) return false |
|
||||
|
|
||||
const witnessScriptChunks = bscript.decompile(witnessScript) |
|
||||
|
|
||||
// is witnessScript a valid script?
|
// is witnessScript a valid script?
|
||||
if (!witnessScriptChunks || witnessScriptChunks.length === 0) return false |
if (!witnessScriptChunks || witnessScriptChunks.length === 0) return false; |
||||
|
const witnessRawScriptSig = bscript.compile(chunks.slice(0, -1)); |
||||
const witnessRawScriptSig = bscript.compile(chunks.slice(0, -1)) |
|
||||
|
|
||||
// match types
|
// match types
|
||||
if (p2pkh.input.check(witnessRawScriptSig) && |
if ( |
||||
p2pkh.output.check(witnessScriptChunks)) return true |
p2pkh.input.check(witnessRawScriptSig) && |
||||
|
p2pkh.output.check(witnessScriptChunks) |
||||
if (p2ms.input.check(witnessRawScriptSig, allowIncomplete) && |
) |
||||
p2ms.output.check(witnessScriptChunks)) return true |
return true; |
||||
|
if ( |
||||
if (p2pk.input.check(witnessRawScriptSig) && |
p2ms.input.check(witnessRawScriptSig, allowIncomplete) && |
||||
p2pk.output.check(witnessScriptChunks)) return true |
p2ms.output.check(witnessScriptChunks) |
||||
|
) |
||||
return false |
return true; |
||||
|
if ( |
||||
|
p2pk.input.check(witnessRawScriptSig) && |
||||
|
p2pk.output.check(witnessScriptChunks) |
||||
|
) |
||||
|
return true; |
||||
|
return false; |
||||
} |
} |
||||
check.toJSON = function () { return 'witnessScriptHash input' } |
exports.check = check; |
||||
|
check.toJSON = () => { |
||||
module.exports = { check } |
return 'witnessScriptHash input'; |
||||
|
}; |
||||
|
@ -1,15 +1,17 @@ |
|||||
|
'use strict'; |
||||
// OP_0 {scriptHash}
|
// OP_0 {scriptHash}
|
||||
|
Object.defineProperty(exports, '__esModule', { value: true }); |
||||
const bscript = require('../../script') |
const bscript = require('../../script'); |
||||
const OPS = require('bitcoin-ops') |
const script_1 = require('../../script'); |
||||
|
function check(script) { |
||||
function check (script) { |
const buffer = bscript.compile(script); |
||||
const buffer = bscript.compile(script) |
return ( |
||||
|
buffer.length === 34 && |
||||
return buffer.length === 34 && |
buffer[0] === script_1.OPS.OP_0 && |
||||
buffer[0] === OPS.OP_0 && |
|
||||
buffer[1] === 0x20 |
buffer[1] === 0x20 |
||||
|
); |
||||
} |
} |
||||
check.toJSON = function () { return 'Witness scriptHash output' } |
exports.check = check; |
||||
|
check.toJSON = () => { |
||||
module.exports = { check } |
return 'Witness scriptHash output'; |
||||
|
}; |
||||
|
File diff suppressed because it is too large
@ -1,49 +1,50 @@ |
|||||
const typeforce = require('typeforce') |
'use strict'; |
||||
|
Object.defineProperty(exports, '__esModule', { value: true }); |
||||
const UINT31_MAX = Math.pow(2, 31) - 1 |
const typeforce = require('typeforce'); |
||||
function UInt31 (value) { |
const UINT31_MAX = Math.pow(2, 31) - 1; |
||||
return typeforce.UInt32(value) && value <= UINT31_MAX |
function UInt31(value) { |
||||
|
return typeforce.UInt32(value) && value <= UINT31_MAX; |
||||
} |
} |
||||
|
exports.UInt31 = UInt31; |
||||
function BIP32Path (value) { |
function BIP32Path(value) { |
||||
return typeforce.String(value) && value.match(/^(m\/)?(\d+'?\/)*\d+'?$/) |
return typeforce.String(value) && !!value.match(/^(m\/)?(\d+'?\/)*\d+'?$/); |
||||
} |
} |
||||
BIP32Path.toJSON = function () { return 'BIP32 derivation path' } |
exports.BIP32Path = BIP32Path; |
||||
|
BIP32Path.toJSON = () => { |
||||
const SATOSHI_MAX = 21 * 1e14 |
return 'BIP32 derivation path'; |
||||
function Satoshi (value) { |
}; |
||||
return typeforce.UInt53(value) && value <= SATOSHI_MAX |
const SATOSHI_MAX = 21 * 1e14; |
||||
|
function Satoshi(value) { |
||||
|
return typeforce.UInt53(value) && value <= SATOSHI_MAX; |
||||
} |
} |
||||
|
exports.Satoshi = Satoshi; |
||||
// external dependent types
|
// external dependent types
|
||||
const ECPoint = typeforce.quacksLike('Point') |
exports.ECPoint = typeforce.quacksLike('Point'); |
||||
|
|
||||
// exposed, external API
|
// exposed, external API
|
||||
const Network = typeforce.compile({ |
exports.Network = typeforce.compile({ |
||||
messagePrefix: typeforce.oneOf(typeforce.Buffer, typeforce.String), |
messagePrefix: typeforce.oneOf(typeforce.Buffer, typeforce.String), |
||||
bip32: { |
bip32: { |
||||
public: typeforce.UInt32, |
public: typeforce.UInt32, |
||||
private: typeforce.UInt32 |
private: typeforce.UInt32, |
||||
}, |
}, |
||||
pubKeyHash: typeforce.UInt8, |
pubKeyHash: typeforce.UInt8, |
||||
scriptHash: typeforce.UInt8, |
scriptHash: typeforce.UInt8, |
||||
wif: typeforce.UInt8 |
wif: typeforce.UInt8, |
||||
}) |
}); |
||||
|
exports.Buffer256bit = typeforce.BufferN(32); |
||||
// extend typeforce types with ours
|
exports.Hash160bit = typeforce.BufferN(20); |
||||
const types = { |
exports.Hash256bit = typeforce.BufferN(32); |
||||
BIP32Path: BIP32Path, |
exports.Number = typeforce.Number; // tslint:disable-line variable-name
|
||||
Buffer256bit: typeforce.BufferN(32), |
exports.Array = typeforce.Array; |
||||
ECPoint: ECPoint, |
exports.Boolean = typeforce.Boolean; // tslint:disable-line variable-name
|
||||
Hash160bit: typeforce.BufferN(20), |
exports.String = typeforce.String; // tslint:disable-line variable-name
|
||||
Hash256bit: typeforce.BufferN(32), |
exports.Buffer = typeforce.Buffer; |
||||
Network: Network, |
exports.Hex = typeforce.Hex; |
||||
Satoshi: Satoshi, |
exports.maybe = typeforce.maybe; |
||||
UInt31: UInt31 |
exports.tuple = typeforce.tuple; |
||||
} |
exports.UInt8 = typeforce.UInt8; |
||||
|
exports.UInt32 = typeforce.UInt32; |
||||
for (var typeName in typeforce) { |
exports.Function = typeforce.Function; |
||||
types[typeName] = typeforce[typeName] |
exports.BufferN = typeforce.BufferN; |
||||
} |
exports.Null = typeforce.Null; |
||||
|
exports.oneOf = typeforce.oneOf; |
||||
module.exports = types |
|
||||
|
@ -1,104 +0,0 @@ |
|||||
/* global describe, it */ |
|
||||
|
|
||||
const assert = require('assert') |
|
||||
const BN = require('bn.js') |
|
||||
const bitcoin = require('../../') |
|
||||
const bip32 = require('bip32') |
|
||||
const crypto = require('crypto') |
|
||||
const tinysecp = require('tiny-secp256k1') |
|
||||
|
|
||||
describe('bitcoinjs-lib (crypto)', function () { |
|
||||
it('can recover a private key from duplicate R values', function () { |
|
||||
// https://blockchain.info/tx/f4c16475f2a6e9c602e4a287f9db3040e319eb9ece74761a4b84bc820fbeef50
|
|
||||
const tx = bitcoin.Transaction.fromHex('01000000020b668015b32a6178d8524cfef6dc6fc0a4751915c2e9b2ed2d2eab02424341c8000000006a47304402205e00298dc5265b7a914974c9d0298aa0e69a0ca932cb52a360436d6a622e5cd7022024bf5f506968f5f23f1835574d5afe0e9021b4a5b65cf9742332d5e4acb68f41012103fd089f73735129f3d798a657aaaa4aa62a00fa15c76b61fc7f1b27ed1d0f35b8ffffffffa95fa69f11dc1cbb77ef64f25a95d4b12ebda57d19d843333819d95c9172ff89000000006b48304502205e00298dc5265b7a914974c9d0298aa0e69a0ca932cb52a360436d6a622e5cd7022100832176b59e8f50c56631acbc824bcba936c9476c559c42a4468be98975d07562012103fd089f73735129f3d798a657aaaa4aa62a00fa15c76b61fc7f1b27ed1d0f35b8ffffffff02b000eb04000000001976a91472956eed9a8ecb19ae7e3ebd7b06cae4668696a788ac303db000000000001976a9146c0bd55dd2592287cd9992ce3ba3fc1208fb76da88ac00000000') |
|
||||
|
|
||||
tx.ins.forEach(function (input, vin) { |
|
||||
const { output: prevOutput, pubkey, signature } = bitcoin.payments.p2pkh({ input: input.script }) |
|
||||
|
|
||||
const scriptSignature = bitcoin.script.signature.decode(signature) |
|
||||
const m = tx.hashForSignature(vin, prevOutput, scriptSignature.hashType) |
|
||||
assert(bitcoin.ECPair.fromPublicKey(pubkey).verify(m, scriptSignature.signature), 'Invalid m') |
|
||||
|
|
||||
// store the required information
|
|
||||
input.signature = scriptSignature.signature |
|
||||
input.z = new BN(m) |
|
||||
}) |
|
||||
|
|
||||
const n = new BN('fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141', 16) |
|
||||
|
|
||||
for (var i = 0; i < tx.ins.length; ++i) { |
|
||||
for (var j = i + 1; j < tx.ins.length; ++j) { |
|
||||
const inputA = tx.ins[i] |
|
||||
const inputB = tx.ins[j] |
|
||||
|
|
||||
// enforce matching r values
|
|
||||
const r = inputA.signature.slice(0, 32) |
|
||||
const rB = inputB.signature.slice(0, 32) |
|
||||
assert.strictEqual(r.toString('hex'), rB.toString('hex')) |
|
||||
|
|
||||
const rInv = new BN(r).invm(n) |
|
||||
|
|
||||
const s1 = new BN(inputA.signature.slice(32, 64)) |
|
||||
const s2 = new BN(inputB.signature.slice(32, 64)) |
|
||||
const z1 = inputA.z |
|
||||
const z2 = inputB.z |
|
||||
|
|
||||
const zz = z1.sub(z2).mod(n) |
|
||||
const ss = s1.sub(s2).mod(n) |
|
||||
|
|
||||
// k = (z1 - z2) / (s1 - s2)
|
|
||||
// d1 = (s1 * k - z1) / r
|
|
||||
// d2 = (s2 * k - z2) / r
|
|
||||
const k = zz.mul(ss.invm(n)).mod(n) |
|
||||
const d1 = ((s1.mul(k).mod(n)).sub(z1).mod(n)).mul(rInv).mod(n) |
|
||||
const d2 = ((s2.mul(k).mod(n)).sub(z2).mod(n)).mul(rInv).mod(n) |
|
||||
|
|
||||
// enforce matching private keys
|
|
||||
assert.strictEqual(d1.toString(), d2.toString()) |
|
||||
} |
|
||||
} |
|
||||
}) |
|
||||
|
|
||||
it('can recover a BIP32 parent private key from the parent public key, and a derived, non-hardened child private key', function () { |
|
||||
function recoverParent (master, child) { |
|
||||
assert(master.isNeutered(), 'You already have the parent private key') |
|
||||
assert(!child.isNeutered(), 'Missing child private key') |
|
||||
|
|
||||
const serQP = master.publicKey |
|
||||
const d1 = child.privateKey |
|
||||
const data = Buffer.alloc(37) |
|
||||
serQP.copy(data, 0) |
|
||||
|
|
||||
// search index space until we find it
|
|
||||
let d2 |
|
||||
for (var i = 0; i < 0x80000000; ++i) { |
|
||||
data.writeUInt32BE(i, 33) |
|
||||
|
|
||||
// calculate I
|
|
||||
const I = crypto.createHmac('sha512', master.chainCode).update(data).digest() |
|
||||
const IL = I.slice(0, 32) |
|
||||
|
|
||||
// See bip32.js:273 to understand
|
|
||||
d2 = tinysecp.privateSub(d1, IL) |
|
||||
|
|
||||
const Qp = bip32.fromPrivateKey(d2, Buffer.alloc(32, 0)).publicKey |
|
||||
if (Qp.equals(serQP)) break |
|
||||
} |
|
||||
|
|
||||
const node = bip32.fromPrivateKey(d2, master.chainCode, master.network) |
|
||||
node.depth = master.depth |
|
||||
node.index = master.index |
|
||||
node.masterFingerprint = master.masterFingerprint |
|
||||
return node |
|
||||
} |
|
||||
|
|
||||
const seed = crypto.randomBytes(32) |
|
||||
const master = bip32.fromSeed(seed) |
|
||||
const child = master.derive(6) // m/6
|
|
||||
|
|
||||
// now for the recovery
|
|
||||
const neuteredMaster = master.neutered() |
|
||||
const recovered = recoverParent(neuteredMaster, child) |
|
||||
assert.strictEqual(recovered.toBase58(), master.toBase58()) |
|
||||
}) |
|
||||
}) |
|
@ -1,168 +0,0 @@ |
|||||
/* global describe, it */ |
|
||||
|
|
||||
const assert = require('assert') |
|
||||
const bitcoin = require('../../') |
|
||||
const ecc = require('tiny-secp256k1') |
|
||||
|
|
||||
function getAddress (node, network) { |
|
||||
return bitcoin.payments.p2pkh({ pubkey: node.publicKey, network }).address |
|
||||
} |
|
||||
|
|
||||
// vG = (dG \+ sha256(e * dG)G)
|
|
||||
function stealthSend (e, Q) { |
|
||||
const eQ = ecc.pointMultiply(Q, e, true) // shared secret
|
|
||||
const c = bitcoin.crypto.sha256(eQ) |
|
||||
const Qc = ecc.pointAddScalar(Q, c) |
|
||||
const vG = bitcoin.ECPair.fromPublicKey(Qc) |
|
||||
|
|
||||
return vG |
|
||||
} |
|
||||
|
|
||||
// v = (d + sha256(eG * d))
|
|
||||
function stealthReceive (d, eG) { |
|
||||
const eQ = ecc.pointMultiply(eG, d) // shared secret
|
|
||||
const c = bitcoin.crypto.sha256(eQ) |
|
||||
const dc = ecc.privateAdd(d, c) |
|
||||
const v = bitcoin.ECPair.fromPrivateKey(dc) |
|
||||
|
|
||||
return v |
|
||||
} |
|
||||
|
|
||||
// d = (v - sha256(e * dG))
|
|
||||
function stealthRecoverLeaked (v, e, Q) { |
|
||||
const eQ = ecc.pointMultiply(Q, e) // shared secret
|
|
||||
const c = bitcoin.crypto.sha256(eQ) |
|
||||
const vc = ecc.privateSub(v, c) |
|
||||
const d = bitcoin.ECPair.fromPrivateKey(vc) |
|
||||
|
|
||||
return d |
|
||||
} |
|
||||
|
|
||||
// vG = (rG \+ sha256(e * dG)G)
|
|
||||
function stealthDualSend (e, R, Q) { |
|
||||
const eQ = ecc.pointMultiply(Q, e) // shared secret
|
|
||||
const c = bitcoin.crypto.sha256(eQ) |
|
||||
const Rc = ecc.pointAddScalar(R, c) |
|
||||
const vG = bitcoin.ECPair.fromPublicKey(Rc) |
|
||||
|
|
||||
return vG |
|
||||
} |
|
||||
|
|
||||
// vG = (rG \+ sha256(eG * d)G)
|
|
||||
function stealthDualScan (d, R, eG) { |
|
||||
const eQ = ecc.pointMultiply(eG, d) // shared secret
|
|
||||
const c = bitcoin.crypto.sha256(eQ) |
|
||||
const Rc = ecc.pointAddScalar(R, c) |
|
||||
const vG = bitcoin.ECPair.fromPublicKey(Rc) |
|
||||
|
|
||||
return vG |
|
||||
} |
|
||||
|
|
||||
// v = (r + sha256(eG * d))
|
|
||||
function stealthDualReceive (d, r, eG) { |
|
||||
const eQ = ecc.pointMultiply(eG, d) // shared secret
|
|
||||
const c = bitcoin.crypto.sha256(eQ) |
|
||||
const rc = ecc.privateAdd(r, c) |
|
||||
const v = bitcoin.ECPair.fromPrivateKey(rc) |
|
||||
|
|
||||
return v |
|
||||
} |
|
||||
|
|
||||
describe('bitcoinjs-lib (crypto)', function () { |
|
||||
it('can generate a single-key stealth address', function () { |
|
||||
// XXX: should be randomly generated, see next test for example
|
|
||||
const recipient = bitcoin.ECPair.fromWIF('5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss') // private to recipient
|
|
||||
const nonce = bitcoin.ECPair.fromWIF('KxVqB96pxbw1pokzQrZkQbLfVBjjHFfp2mFfEp8wuEyGenLFJhM9') // private to sender
|
|
||||
|
|
||||
// ... recipient reveals public key (recipient.Q) to sender
|
|
||||
const forSender = stealthSend(nonce.privateKey, recipient.publicKey) |
|
||||
assert.equal(getAddress(forSender), '1CcZWwCpACJL3AxqoDbwEt4JgDFuTHUspE') |
|
||||
assert.throws(function () { forSender.toWIF() }, /Error: Missing private key/) |
|
||||
|
|
||||
// ... sender reveals nonce public key (nonce.Q) to recipient
|
|
||||
const forRecipient = stealthReceive(recipient.privateKey, nonce.publicKey) |
|
||||
assert.equal(getAddress(forRecipient), '1CcZWwCpACJL3AxqoDbwEt4JgDFuTHUspE') |
|
||||
assert.equal(forRecipient.toWIF(), 'L1yjUN3oYyCXV3LcsBrmxCNTa62bZKWCybxVJMvqjMmmfDE8yk7n') |
|
||||
|
|
||||
// sender and recipient, both derived same address
|
|
||||
assert.equal(getAddress(forSender), getAddress(forRecipient)) |
|
||||
}) |
|
||||
|
|
||||
it('can generate a single-key stealth address (randomly)', function () { |
|
||||
const recipient = bitcoin.ECPair.makeRandom() // private to recipient
|
|
||||
const nonce = bitcoin.ECPair.makeRandom() // private to sender
|
|
||||
|
|
||||
// ... recipient reveals public key (recipient.Q) to sender
|
|
||||
const forSender = stealthSend(nonce.privateKey, recipient.publicKey) |
|
||||
assert.throws(function () { forSender.toWIF() }, /Error: Missing private key/) |
|
||||
|
|
||||
// ... sender reveals nonce public key (nonce.Q) to recipient
|
|
||||
const forRecipient = stealthReceive(recipient.privateKey, nonce.publicKey) |
|
||||
assert.doesNotThrow(function () { forRecipient.toWIF() }) |
|
||||
|
|
||||
// sender and recipient, both derived same address
|
|
||||
assert.equal(getAddress(forSender), getAddress(forRecipient)) |
|
||||
}) |
|
||||
|
|
||||
it('can recover parent recipient.d, if a derived private key is leaked [and nonce was revealed]', function () { |
|
||||
const recipient = bitcoin.ECPair.makeRandom() // private to recipient
|
|
||||
const nonce = bitcoin.ECPair.makeRandom() // private to sender
|
|
||||
|
|
||||
// ... recipient reveals public key (recipient.Q) to sender
|
|
||||
const forSender = stealthSend(nonce.privateKey, recipient.publicKey) |
|
||||
assert.throws(function () { forSender.toWIF() }, /Error: Missing private key/) |
|
||||
|
|
||||
// ... sender reveals nonce public key (nonce.Q) to recipient
|
|
||||
const forRecipient = stealthReceive(recipient.privateKey, nonce.publicKey) |
|
||||
assert.doesNotThrow(function () { forRecipient.toWIF() }) |
|
||||
|
|
||||
// ... recipient accidentally leaks forRecipient.d on the blockchain
|
|
||||
const leaked = stealthRecoverLeaked(forRecipient.privateKey, nonce.privateKey, recipient.publicKey) |
|
||||
assert.equal(leaked.toWIF(), recipient.toWIF()) |
|
||||
}) |
|
||||
|
|
||||
it('can generate a dual-key stealth address', function () { |
|
||||
// XXX: should be randomly generated, see next test for example
|
|
||||
const recipient = bitcoin.ECPair.fromWIF('5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss') // private to recipient
|
|
||||
const scan = bitcoin.ECPair.fromWIF('L5DkCk3xLLoGKncqKsWQTdaPSR4V8gzc14WVghysQGkdryRudjBM') // private to scanner/recipient
|
|
||||
const nonce = bitcoin.ECPair.fromWIF('KxVqB96pxbw1pokzQrZkQbLfVBjjHFfp2mFfEp8wuEyGenLFJhM9') // private to sender
|
|
||||
|
|
||||
// ... recipient reveals public key(s) (recipient.Q, scan.Q) to sender
|
|
||||
const forSender = stealthDualSend(nonce.privateKey, recipient.publicKey, scan.publicKey) |
|
||||
assert.throws(function () { forSender.toWIF() }, /Error: Missing private key/) |
|
||||
|
|
||||
// ... sender reveals nonce public key (nonce.Q) to scanner
|
|
||||
const forScanner = stealthDualScan(scan.privateKey, recipient.publicKey, nonce.publicKey) |
|
||||
assert.throws(function () { forScanner.toWIF() }, /Error: Missing private key/) |
|
||||
|
|
||||
// ... scanner reveals relevant transaction + nonce public key (nonce.Q) to recipient
|
|
||||
const forRecipient = stealthDualReceive(scan.privateKey, recipient.privateKey, nonce.publicKey) |
|
||||
assert.doesNotThrow(function () { forRecipient.toWIF() }) |
|
||||
|
|
||||
// scanner, sender and recipient, all derived same address
|
|
||||
assert.equal(getAddress(forSender), getAddress(forScanner)) |
|
||||
assert.equal(getAddress(forSender), getAddress(forRecipient)) |
|
||||
}) |
|
||||
|
|
||||
it('can generate a dual-key stealth address (randomly)', function () { |
|
||||
const recipient = bitcoin.ECPair.makeRandom() // private to recipient
|
|
||||
const scan = bitcoin.ECPair.makeRandom() // private to scanner/recipient
|
|
||||
const nonce = bitcoin.ECPair.makeRandom() // private to sender
|
|
||||
|
|
||||
// ... recipient reveals public key(s) (recipient.Q, scan.Q) to sender
|
|
||||
const forSender = stealthDualSend(nonce.privateKey, recipient.publicKey, scan.publicKey) |
|
||||
assert.throws(function () { forSender.toWIF() }, /Error: Missing private key/) |
|
||||
|
|
||||
// ... sender reveals nonce public key (nonce.Q) to scanner
|
|
||||
const forScanner = stealthDualScan(scan.privateKey, recipient.publicKey, nonce.publicKey) |
|
||||
assert.throws(function () { forScanner.toWIF() }, /Error: Missing private key/) |
|
||||
|
|
||||
// ... scanner reveals relevant transaction + nonce public key (nonce.Q) to recipient
|
|
||||
const forRecipient = stealthDualReceive(scan.privateKey, recipient.privateKey, nonce.publicKey) |
|
||||
assert.doesNotThrow(function () { forRecipient.toWIF() }) |
|
||||
|
|
||||
// scanner, sender and recipient, all derived same address
|
|
||||
assert.equal(getAddress(forSender), getAddress(forScanner)) |
|
||||
assert.equal(getAddress(forSender), getAddress(forRecipient)) |
|
||||
}) |
|
||||
}) |
|
@ -0,0 +1,119 @@ |
|||||
|
import { Network } from './networks'; |
||||
|
import * as networks from './networks'; |
||||
|
import * as payments from './payments'; |
||||
|
import * as bscript from './script'; |
||||
|
import * as types from './types'; |
||||
|
|
||||
|
const bech32 = require('bech32'); |
||||
|
const bs58check = require('bs58check'); |
||||
|
const typeforce = require('typeforce'); |
||||
|
|
||||
|
export interface Base58CheckResult { |
||||
|
hash: Buffer; |
||||
|
version: number; |
||||
|
} |
||||
|
|
||||
|
export interface Bech32Result { |
||||
|
version: number; |
||||
|
prefix: string; |
||||
|
data: Buffer; |
||||
|
} |
||||
|
|
||||
|
export function fromBase58Check(address: string): Base58CheckResult { |
||||
|
const payload = bs58check.decode(address); |
||||
|
|
||||
|
// TODO: 4.0.0, move to "toOutputScript"
|
||||
|
if (payload.length < 21) throw new TypeError(address + ' is too short'); |
||||
|
if (payload.length > 21) throw new TypeError(address + ' is too long'); |
||||
|
|
||||
|
const version = payload.readUInt8(0); |
||||
|
const hash = payload.slice(1); |
||||
|
|
||||
|
return { version, hash }; |
||||
|
} |
||||
|
|
||||
|
export function fromBech32(address: string): Bech32Result { |
||||
|
const result = bech32.decode(address); |
||||
|
const data = bech32.fromWords(result.words.slice(1)); |
||||
|
|
||||
|
return { |
||||
|
version: result.words[0], |
||||
|
prefix: result.prefix, |
||||
|
data: Buffer.from(data), |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
export function toBase58Check(hash: Buffer, version: number): string { |
||||
|
typeforce(types.tuple(types.Hash160bit, types.UInt8), arguments); |
||||
|
|
||||
|
const payload = Buffer.allocUnsafe(21); |
||||
|
payload.writeUInt8(version, 0); |
||||
|
hash.copy(payload, 1); |
||||
|
|
||||
|
return bs58check.encode(payload); |
||||
|
} |
||||
|
|
||||
|
export function toBech32( |
||||
|
data: Buffer, |
||||
|
version: number, |
||||
|
prefix: string, |
||||
|
): string { |
||||
|
const words = bech32.toWords(data); |
||||
|
words.unshift(version); |
||||
|
|
||||
|
return bech32.encode(prefix, words); |
||||
|
} |
||||
|
|
||||
|
export function fromOutputScript(output: Buffer, network?: Network): string { |
||||
|
// TODO: Network
|
||||
|
network = network || networks.bitcoin; |
||||
|
|
||||
|
try { |
||||
|
return payments.p2pkh({ output, network }).address as string; |
||||
|
} catch (e) {} |
||||
|
try { |
||||
|
return payments.p2sh({ output, network }).address as string; |
||||
|
} catch (e) {} |
||||
|
try { |
||||
|
return payments.p2wpkh({ output, network }).address as string; |
||||
|
} catch (e) {} |
||||
|
try { |
||||
|
return payments.p2wsh({ output, network }).address as string; |
||||
|
} catch (e) {} |
||||
|
|
||||
|
throw new Error(bscript.toASM(output) + ' has no matching Address'); |
||||
|
} |
||||
|
|
||||
|
export function toOutputScript(address: string, network?: Network): Buffer { |
||||
|
network = network || networks.bitcoin; |
||||
|
|
||||
|
let decodeBase58: Base58CheckResult | undefined; |
||||
|
let decodeBech32: Bech32Result | undefined; |
||||
|
try { |
||||
|
decodeBase58 = fromBase58Check(address); |
||||
|
} catch (e) {} |
||||
|
|
||||
|
if (decodeBase58) { |
||||
|
if (decodeBase58.version === network.pubKeyHash) |
||||
|
return payments.p2pkh({ hash: decodeBase58.hash }).output as Buffer; |
||||
|
if (decodeBase58.version === network.scriptHash) |
||||
|
return payments.p2sh({ hash: decodeBase58.hash }).output as Buffer; |
||||
|
} else { |
||||
|
try { |
||||
|
decodeBech32 = fromBech32(address); |
||||
|
} catch (e) {} |
||||
|
|
||||
|
if (decodeBech32) { |
||||
|
if (decodeBech32.prefix !== network.bech32) |
||||
|
throw new Error(address + ' has an invalid prefix'); |
||||
|
if (decodeBech32.version === 0) { |
||||
|
if (decodeBech32.data.length === 20) |
||||
|
return payments.p2wpkh({ hash: decodeBech32.data }).output as Buffer; |
||||
|
if (decodeBech32.data.length === 32) |
||||
|
return payments.p2wsh({ hash: decodeBech32.data }).output as Buffer; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
throw new Error(address + ' has no matching Script'); |
||||
|
} |
@ -0,0 +1,285 @@ |
|||||
|
import { reverseBuffer } from './bufferutils'; |
||||
|
import * as bcrypto from './crypto'; |
||||
|
import { Transaction } from './transaction'; |
||||
|
import * as types from './types'; |
||||
|
|
||||
|
const fastMerkleRoot = require('merkle-lib/fastRoot'); |
||||
|
const typeforce = require('typeforce'); |
||||
|
const varuint = require('varuint-bitcoin'); |
||||
|
|
||||
|
const errorMerkleNoTxes = new TypeError( |
||||
|
'Cannot compute merkle root for zero transactions', |
||||
|
); |
||||
|
const errorWitnessNotSegwit = new TypeError( |
||||
|
'Cannot compute witness commit for non-segwit block', |
||||
|
); |
||||
|
|
||||
|
export class Block { |
||||
|
static fromBuffer(buffer: Buffer): Block { |
||||
|
if (buffer.length < 80) throw new Error('Buffer too small (< 80 bytes)'); |
||||
|
|
||||
|
let offset: number = 0; |
||||
|
const readSlice = (n: number): Buffer => { |
||||
|
offset += n; |
||||
|
return buffer.slice(offset - n, offset); |
||||
|
}; |
||||
|
|
||||
|
const readUInt32 = (): number => { |
||||
|
const i = buffer.readUInt32LE(offset); |
||||
|
offset += 4; |
||||
|
return i; |
||||
|
}; |
||||
|
|
||||
|
const readInt32 = (): number => { |
||||
|
const i = buffer.readInt32LE(offset); |
||||
|
offset += 4; |
||||
|
return i; |
||||
|
}; |
||||
|
|
||||
|
const block = new Block(); |
||||
|
block.version = readInt32(); |
||||
|
block.prevHash = readSlice(32); |
||||
|
block.merkleRoot = readSlice(32); |
||||
|
block.timestamp = readUInt32(); |
||||
|
block.bits = readUInt32(); |
||||
|
block.nonce = readUInt32(); |
||||
|
|
||||
|
if (buffer.length === 80) return block; |
||||
|
|
||||
|
const readVarInt = (): number => { |
||||
|
const vi = varuint.decode(buffer, offset); |
||||
|
offset += varuint.decode.bytes; |
||||
|
return vi; |
||||
|
}; |
||||
|
|
||||
|
const readTransaction = (): any => { |
||||
|
const tx = Transaction.fromBuffer(buffer.slice(offset), true); |
||||
|
offset += tx.byteLength(); |
||||
|
return tx; |
||||
|
}; |
||||
|
|
||||
|
const nTransactions = readVarInt(); |
||||
|
block.transactions = []; |
||||
|
|
||||
|
for (let i = 0; i < nTransactions; ++i) { |
||||
|
const tx = readTransaction(); |
||||
|
block.transactions.push(tx); |
||||
|
} |
||||
|
|
||||
|
const witnessCommit = block.getWitnessCommit(); |
||||
|
// This Block contains a witness commit
|
||||
|
if (witnessCommit) block.witnessCommit = witnessCommit; |
||||
|
|
||||
|
return block; |
||||
|
} |
||||
|
|
||||
|
static fromHex(hex: string): Block { |
||||
|
return Block.fromBuffer(Buffer.from(hex, 'hex')); |
||||
|
} |
||||
|
|
||||
|
static calculateTarget(bits: number): Buffer { |
||||
|
const exponent = ((bits & 0xff000000) >> 24) - 3; |
||||
|
const mantissa = bits & 0x007fffff; |
||||
|
const target = Buffer.alloc(32, 0); |
||||
|
target.writeUIntBE(mantissa, 29 - exponent, 3); |
||||
|
return target; |
||||
|
} |
||||
|
|
||||
|
static calculateMerkleRoot( |
||||
|
transactions: Transaction[], |
||||
|
forWitness?: boolean, |
||||
|
): Buffer { |
||||
|
typeforce([{ getHash: types.Function }], transactions); |
||||
|
if (transactions.length === 0) throw errorMerkleNoTxes; |
||||
|
if (forWitness && !txesHaveWitnessCommit(transactions)) |
||||
|
throw errorWitnessNotSegwit; |
||||
|
|
||||
|
const hashes = transactions.map(transaction => |
||||
|
transaction.getHash(forWitness!), |
||||
|
); |
||||
|
|
||||
|
const rootHash = fastMerkleRoot(hashes, bcrypto.hash256); |
||||
|
|
||||
|
return forWitness |
||||
|
? bcrypto.hash256( |
||||
|
Buffer.concat([rootHash, transactions[0].ins[0].witness[0]]), |
||||
|
) |
||||
|
: rootHash; |
||||
|
} |
||||
|
|
||||
|
version: number = 1; |
||||
|
prevHash?: Buffer = undefined; |
||||
|
merkleRoot?: Buffer = undefined; |
||||
|
timestamp: number = 0; |
||||
|
witnessCommit?: Buffer = undefined; |
||||
|
bits: number = 0; |
||||
|
nonce: number = 0; |
||||
|
transactions?: Transaction[] = undefined; |
||||
|
|
||||
|
getWitnessCommit(): Buffer | null { |
||||
|
if (!txesHaveWitnessCommit(this.transactions!)) return null; |
||||
|
|
||||
|
// The merkle root for the witness data is in an OP_RETURN output.
|
||||
|
// There is no rule for the index of the output, so use filter to find it.
|
||||
|
// The root is prepended with 0xaa21a9ed so check for 0x6a24aa21a9ed
|
||||
|
// If multiple commits are found, the output with highest index is assumed.
|
||||
|
const witnessCommits = this.transactions![0].outs.filter(out => |
||||
|
out.script.slice(0, 6).equals(Buffer.from('6a24aa21a9ed', 'hex')), |
||||
|
).map(out => out.script.slice(6, 38)); |
||||
|
if (witnessCommits.length === 0) return null; |
||||
|
// Use the commit with the highest output (should only be one though)
|
||||
|
const result = witnessCommits[witnessCommits.length - 1]; |
||||
|
|
||||
|
if (!(result instanceof Buffer && result.length === 32)) return null; |
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
hasWitnessCommit(): boolean { |
||||
|
if ( |
||||
|
this.witnessCommit instanceof Buffer && |
||||
|
this.witnessCommit.length === 32 |
||||
|
) |
||||
|
return true; |
||||
|
if (this.getWitnessCommit() !== null) return true; |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
hasWitness(): boolean { |
||||
|
return anyTxHasWitness(this.transactions!); |
||||
|
} |
||||
|
|
||||
|
byteLength(headersOnly: boolean): number { |
||||
|
if (headersOnly || !this.transactions) return 80; |
||||
|
|
||||
|
return ( |
||||
|
80 + |
||||
|
varuint.encodingLength(this.transactions.length) + |
||||
|
this.transactions.reduce((a, x) => a + x.byteLength(), 0) |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
getHash(): Buffer { |
||||
|
return bcrypto.hash256(this.toBuffer(true)); |
||||
|
} |
||||
|
|
||||
|
getId(): string { |
||||
|
return reverseBuffer(this.getHash()).toString('hex'); |
||||
|
} |
||||
|
|
||||
|
getUTCDate(): Date { |
||||
|
const date = new Date(0); // epoch
|
||||
|
date.setUTCSeconds(this.timestamp); |
||||
|
|
||||
|
return date; |
||||
|
} |
||||
|
|
||||
|
// TODO: buffer, offset compatibility
|
||||
|
toBuffer(headersOnly: boolean): Buffer { |
||||
|
const buffer: Buffer = Buffer.allocUnsafe(this.byteLength(headersOnly)); |
||||
|
|
||||
|
let offset: number = 0; |
||||
|
const writeSlice = (slice: Buffer): void => { |
||||
|
slice.copy(buffer, offset); |
||||
|
offset += slice.length; |
||||
|
}; |
||||
|
|
||||
|
const writeInt32 = (i: number): void => { |
||||
|
buffer.writeInt32LE(i, offset); |
||||
|
offset += 4; |
||||
|
}; |
||||
|
const writeUInt32 = (i: number): void => { |
||||
|
buffer.writeUInt32LE(i, offset); |
||||
|
offset += 4; |
||||
|
}; |
||||
|
|
||||
|
writeInt32(this.version); |
||||
|
writeSlice(this.prevHash!); |
||||
|
writeSlice(this.merkleRoot!); |
||||
|
writeUInt32(this.timestamp); |
||||
|
writeUInt32(this.bits); |
||||
|
writeUInt32(this.nonce); |
||||
|
|
||||
|
if (headersOnly || !this.transactions) return buffer; |
||||
|
|
||||
|
varuint.encode(this.transactions.length, buffer, offset); |
||||
|
offset += varuint.encode.bytes; |
||||
|
|
||||
|
this.transactions.forEach(tx => { |
||||
|
const txSize = tx.byteLength(); // TODO: extract from toBuffer?
|
||||
|
tx.toBuffer(buffer, offset); |
||||
|
offset += txSize; |
||||
|
}); |
||||
|
|
||||
|
return buffer; |
||||
|
} |
||||
|
|
||||
|
toHex(headersOnly: boolean): string { |
||||
|
return this.toBuffer(headersOnly).toString('hex'); |
||||
|
} |
||||
|
|
||||
|
checkTxRoots(): boolean { |
||||
|
// If the Block has segwit transactions but no witness commit,
|
||||
|
// there's no way it can be valid, so fail the check.
|
||||
|
const hasWitnessCommit = this.hasWitnessCommit(); |
||||
|
if (!hasWitnessCommit && this.hasWitness()) return false; |
||||
|
return ( |
||||
|
this.__checkMerkleRoot() && |
||||
|
(hasWitnessCommit ? this.__checkWitnessCommit() : true) |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
checkProofOfWork(): boolean { |
||||
|
const hash: Buffer = reverseBuffer(this.getHash()); |
||||
|
const target = Block.calculateTarget(this.bits); |
||||
|
|
||||
|
return hash.compare(target) <= 0; |
||||
|
} |
||||
|
|
||||
|
private __checkMerkleRoot(): boolean { |
||||
|
if (!this.transactions) throw errorMerkleNoTxes; |
||||
|
|
||||
|
const actualMerkleRoot = Block.calculateMerkleRoot(this.transactions); |
||||
|
return this.merkleRoot!.compare(actualMerkleRoot) === 0; |
||||
|
} |
||||
|
|
||||
|
private __checkWitnessCommit(): boolean { |
||||
|
if (!this.transactions) throw errorMerkleNoTxes; |
||||
|
if (!this.hasWitnessCommit()) throw errorWitnessNotSegwit; |
||||
|
|
||||
|
const actualWitnessCommit = Block.calculateMerkleRoot( |
||||
|
this.transactions, |
||||
|
true, |
||||
|
); |
||||
|
return this.witnessCommit!.compare(actualWitnessCommit) === 0; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function txesHaveWitnessCommit(transactions: Transaction[]): boolean { |
||||
|
return ( |
||||
|
transactions instanceof Array && |
||||
|
transactions[0] && |
||||
|
transactions[0].ins && |
||||
|
transactions[0].ins instanceof Array && |
||||
|
transactions[0].ins[0] && |
||||
|
transactions[0].ins[0].witness && |
||||
|
transactions[0].ins[0].witness instanceof Array && |
||||
|
transactions[0].ins[0].witness.length > 0 |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
function anyTxHasWitness(transactions: Transaction[]): boolean { |
||||
|
return ( |
||||
|
transactions instanceof Array && |
||||
|
transactions.some( |
||||
|
tx => |
||||
|
typeof tx === 'object' && |
||||
|
tx.ins instanceof Array && |
||||
|
tx.ins.some( |
||||
|
input => |
||||
|
typeof input === 'object' && |
||||
|
input.witness instanceof Array && |
||||
|
input.witness.length > 0, |
||||
|
), |
||||
|
) |
||||
|
); |
||||
|
} |
@ -0,0 +1,44 @@ |
|||||
|
// https://github.com/feross/buffer/blob/master/index.js#L1127
|
||||
|
function verifuint(value: number, max: number): void { |
||||
|
if (typeof value !== 'number') |
||||
|
throw new Error('cannot write a non-number as a number'); |
||||
|
if (value < 0) |
||||
|
throw new Error('specified a negative value for writing an unsigned value'); |
||||
|
if (value > max) throw new Error('RangeError: value out of range'); |
||||
|
if (Math.floor(value) !== value) |
||||
|
throw new Error('value has a fractional component'); |
||||
|
} |
||||
|
|
||||
|
export function readUInt64LE(buffer: Buffer, offset: number): number { |
||||
|
const a = buffer.readUInt32LE(offset); |
||||
|
let b = buffer.readUInt32LE(offset + 4); |
||||
|
b *= 0x100000000; |
||||
|
|
||||
|
verifuint(b + a, 0x001fffffffffffff); |
||||
|
return b + a; |
||||
|
} |
||||
|
|
||||
|
export function writeUInt64LE( |
||||
|
buffer: Buffer, |
||||
|
value: number, |
||||
|
offset: number, |
||||
|
): number { |
||||
|
verifuint(value, 0x001fffffffffffff); |
||||
|
|
||||
|
buffer.writeInt32LE(value & -1, offset); |
||||
|
buffer.writeUInt32LE(Math.floor(value / 0x100000000), offset + 4); |
||||
|
return offset + 8; |
||||
|
} |
||||
|
|
||||
|
export function reverseBuffer(buffer: Buffer): Buffer { |
||||
|
if (buffer.length < 1) return buffer; |
||||
|
let j = buffer.length - 1; |
||||
|
let tmp = 0; |
||||
|
for (let i = 0; i < buffer.length / 2; i++) { |
||||
|
tmp = buffer[i]; |
||||
|
buffer[i] = buffer[j]; |
||||
|
buffer[j] = tmp; |
||||
|
j--; |
||||
|
} |
||||
|
return buffer; |
||||
|
} |
@ -0,0 +1,71 @@ |
|||||
|
import { decompile } from './script'; |
||||
|
import * as multisig from './templates/multisig'; |
||||
|
import * as nullData from './templates/nulldata'; |
||||
|
import * as pubKey from './templates/pubkey'; |
||||
|
import * as pubKeyHash from './templates/pubkeyhash'; |
||||
|
import * as scriptHash from './templates/scripthash'; |
||||
|
import * as witnessCommitment from './templates/witnesscommitment'; |
||||
|
import * as witnessPubKeyHash from './templates/witnesspubkeyhash'; |
||||
|
import * as witnessScriptHash from './templates/witnessscripthash'; |
||||
|
|
||||
|
const types = { |
||||
|
P2MS: 'multisig' as string, |
||||
|
NONSTANDARD: 'nonstandard' as string, |
||||
|
NULLDATA: 'nulldata' as string, |
||||
|
P2PK: 'pubkey' as string, |
||||
|
P2PKH: 'pubkeyhash' as string, |
||||
|
P2SH: 'scripthash' as string, |
||||
|
P2WPKH: 'witnesspubkeyhash' as string, |
||||
|
P2WSH: 'witnessscripthash' as string, |
||||
|
WITNESS_COMMITMENT: 'witnesscommitment' as string, |
||||
|
}; |
||||
|
|
||||
|
function classifyOutput(script: Buffer): string { |
||||
|
if (witnessPubKeyHash.output.check(script)) return types.P2WPKH; |
||||
|
if (witnessScriptHash.output.check(script)) return types.P2WSH; |
||||
|
if (pubKeyHash.output.check(script)) return types.P2PKH; |
||||
|
if (scriptHash.output.check(script)) return types.P2SH; |
||||
|
|
||||
|
// XXX: optimization, below functions .decompile before use
|
||||
|
const chunks = decompile(script); |
||||
|
if (!chunks) throw new TypeError('Invalid script'); |
||||
|
|
||||
|
if (multisig.output.check(chunks)) return types.P2MS; |
||||
|
if (pubKey.output.check(chunks)) return types.P2PK; |
||||
|
if (witnessCommitment.output.check(chunks)) return types.WITNESS_COMMITMENT; |
||||
|
if (nullData.output.check(chunks)) return types.NULLDATA; |
||||
|
|
||||
|
return types.NONSTANDARD; |
||||
|
} |
||||
|
|
||||
|
function classifyInput(script: Buffer, allowIncomplete: boolean): string { |
||||
|
// XXX: optimization, below functions .decompile before use
|
||||
|
const chunks = decompile(script); |
||||
|
if (!chunks) throw new TypeError('Invalid script'); |
||||
|
|
||||
|
if (pubKeyHash.input.check(chunks)) return types.P2PKH; |
||||
|
if (scriptHash.input.check(chunks, allowIncomplete)) return types.P2SH; |
||||
|
if (multisig.input.check(chunks, allowIncomplete)) return types.P2MS; |
||||
|
if (pubKey.input.check(chunks)) return types.P2PK; |
||||
|
|
||||
|
return types.NONSTANDARD; |
||||
|
} |
||||
|
|
||||
|
function classifyWitness(script: Buffer[], allowIncomplete: boolean): string { |
||||
|
// XXX: optimization, below functions .decompile before use
|
||||
|
const chunks = decompile(script); |
||||
|
if (!chunks) throw new TypeError('Invalid script'); |
||||
|
|
||||
|
if (witnessPubKeyHash.input.check(chunks)) return types.P2WPKH; |
||||
|
if (witnessScriptHash.input.check(chunks as Buffer[], allowIncomplete)) |
||||
|
return types.P2WSH; |
||||
|
|
||||
|
return types.NONSTANDARD; |
||||
|
} |
||||
|
|
||||
|
export { |
||||
|
classifyInput as input, |
||||
|
classifyOutput as output, |
||||
|
classifyWitness as witness, |
||||
|
types, |
||||
|
}; |
@ -0,0 +1,33 @@ |
|||||
|
const createHash = require('create-hash'); |
||||
|
|
||||
|
export function ripemd160(buffer: Buffer): Buffer { |
||||
|
try { |
||||
|
return createHash('rmd160') |
||||
|
.update(buffer) |
||||
|
.digest(); |
||||
|
} catch (err) { |
||||
|
return createHash('ripemd160') |
||||
|
.update(buffer) |
||||
|
.digest(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
export function sha1(buffer: Buffer): Buffer { |
||||
|
return createHash('sha1') |
||||
|
.update(buffer) |
||||
|
.digest(); |
||||
|
} |
||||
|
|
||||
|
export function sha256(buffer: Buffer): Buffer { |
||||
|
return createHash('sha256') |
||||
|
.update(buffer) |
||||
|
.digest(); |
||||
|
} |
||||
|
|
||||
|
export function hash160(buffer: Buffer): Buffer { |
||||
|
return ripemd160(sha256(buffer)); |
||||
|
} |
||||
|
|
||||
|
export function hash256(buffer: Buffer): Buffer { |
||||
|
return sha256(sha256(buffer)); |
||||
|
} |
@ -0,0 +1,146 @@ |
|||||
|
import { Network } from './networks'; |
||||
|
import * as NETWORKS from './networks'; |
||||
|
import * as types from './types'; |
||||
|
const ecc = require('tiny-secp256k1'); |
||||
|
const randomBytes = require('randombytes'); |
||||
|
const typeforce = require('typeforce'); |
||||
|
const wif = require('wif'); |
||||
|
|
||||
|
const isOptions = typeforce.maybe( |
||||
|
typeforce.compile({ |
||||
|
compressed: types.maybe(types.Boolean), |
||||
|
network: types.maybe(types.Network), |
||||
|
}), |
||||
|
); |
||||
|
|
||||
|
interface ECPairOptions { |
||||
|
compressed?: boolean; |
||||
|
network?: Network; |
||||
|
rng?(arg0: number): Buffer; |
||||
|
} |
||||
|
|
||||
|
export interface ECPairInterface { |
||||
|
compressed: boolean; |
||||
|
network: Network; |
||||
|
publicKey: Buffer; |
||||
|
privateKey?: Buffer; |
||||
|
toWIF(): string; |
||||
|
sign(hash: Buffer, lowR?: boolean): Buffer; |
||||
|
verify(hash: Buffer, signature: Buffer): boolean; |
||||
|
getPublicKey?(): Buffer; |
||||
|
} |
||||
|
|
||||
|
class ECPair implements ECPairInterface { |
||||
|
compressed: boolean; |
||||
|
network: Network; |
||||
|
|
||||
|
constructor( |
||||
|
private __D?: Buffer, |
||||
|
private __Q?: Buffer, |
||||
|
options?: ECPairOptions, |
||||
|
) { |
||||
|
if (options === undefined) options = {}; |
||||
|
this.compressed = |
||||
|
options.compressed === undefined ? true : options.compressed; |
||||
|
this.network = options.network || NETWORKS.bitcoin; |
||||
|
|
||||
|
if (__Q !== undefined) this.__Q = ecc.pointCompress(__Q, this.compressed); |
||||
|
} |
||||
|
|
||||
|
get privateKey(): Buffer | undefined { |
||||
|
return this.__D; |
||||
|
} |
||||
|
|
||||
|
get publicKey(): Buffer { |
||||
|
if (!this.__Q) |
||||
|
this.__Q = ecc.pointFromScalar(this.__D, this.compressed) as Buffer; |
||||
|
return this.__Q; |
||||
|
} |
||||
|
|
||||
|
toWIF(): string { |
||||
|
if (!this.__D) throw new Error('Missing private key'); |
||||
|
return wif.encode(this.network.wif, this.__D, this.compressed); |
||||
|
} |
||||
|
|
||||
|
sign(hash: Buffer, lowR: boolean = false): Buffer { |
||||
|
if (!this.__D) throw new Error('Missing private key'); |
||||
|
if (lowR === false) { |
||||
|
return ecc.sign(hash, this.__D); |
||||
|
} else { |
||||
|
let sig = ecc.sign(hash, this.__D); |
||||
|
const extraData = Buffer.alloc(32, 0); |
||||
|
let counter = 0; |
||||
|
// if first try is lowR, skip the loop
|
||||
|
// for second try and on, add extra entropy counting up
|
||||
|
while (sig[0] > 0x7f) { |
||||
|
counter++; |
||||
|
extraData.writeUIntLE(counter, 0, 6); |
||||
|
sig = ecc.signWithEntropy(hash, this.__D, extraData); |
||||
|
} |
||||
|
return sig; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
verify(hash: Buffer, signature: Buffer): boolean { |
||||
|
return ecc.verify(hash, this.publicKey, signature); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function fromPrivateKey(buffer: Buffer, options?: ECPairOptions): ECPair { |
||||
|
typeforce(types.Buffer256bit, buffer); |
||||
|
if (!ecc.isPrivate(buffer)) |
||||
|
throw new TypeError('Private key not in range [1, n)'); |
||||
|
typeforce(isOptions, options); |
||||
|
|
||||
|
return new ECPair(buffer, undefined, options); |
||||
|
} |
||||
|
|
||||
|
function fromPublicKey(buffer: Buffer, options?: ECPairOptions): ECPair { |
||||
|
typeforce(ecc.isPoint, buffer); |
||||
|
typeforce(isOptions, options); |
||||
|
return new ECPair(undefined, buffer, options); |
||||
|
} |
||||
|
|
||||
|
function fromWIF(wifString: string, network?: Network | Network[]): ECPair { |
||||
|
const decoded = wif.decode(wifString); |
||||
|
const version = decoded.version; |
||||
|
|
||||
|
// list of networks?
|
||||
|
if (types.Array(network)) { |
||||
|
network = (network as Network[]) |
||||
|
.filter((x: Network) => { |
||||
|
return version === x.wif; |
||||
|
}) |
||||
|
.pop() as Network; |
||||
|
|
||||
|
if (!network) throw new Error('Unknown network version'); |
||||
|
|
||||
|
// otherwise, assume a network object (or default to bitcoin)
|
||||
|
} else { |
||||
|
network = network || NETWORKS.bitcoin; |
||||
|
|
||||
|
if (version !== (network as Network).wif) |
||||
|
throw new Error('Invalid network version'); |
||||
|
} |
||||
|
|
||||
|
return fromPrivateKey(decoded.privateKey, { |
||||
|
compressed: decoded.compressed, |
||||
|
network: network as Network, |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
function makeRandom(options?: ECPairOptions): ECPair { |
||||
|
typeforce(isOptions, options); |
||||
|
if (options === undefined) options = {}; |
||||
|
const rng = options.rng || randomBytes; |
||||
|
|
||||
|
let d; |
||||
|
do { |
||||
|
d = rng(32); |
||||
|
typeforce(types.Buffer256bit, d); |
||||
|
} while (!ecc.isPrivate(d)); |
||||
|
|
||||
|
return fromPrivateKey(d, options); |
||||
|
} |
||||
|
|
||||
|
export { makeRandom, fromPrivateKey, fromPublicKey, fromWIF }; |
@ -0,0 +1,20 @@ |
|||||
|
import * as bip32 from 'bip32'; |
||||
|
import * as address from './address'; |
||||
|
import * as crypto from './crypto'; |
||||
|
import * as ECPair from './ecpair'; |
||||
|
import * as networks from './networks'; |
||||
|
import * as payments from './payments'; |
||||
|
import * as script from './script'; |
||||
|
|
||||
|
export { ECPair, address, bip32, crypto, networks, payments, script }; |
||||
|
|
||||
|
export { Block } from './block'; |
||||
|
export { OPS as opcodes } from './script'; |
||||
|
export { Transaction } from './transaction'; |
||||
|
export { TransactionBuilder } from './transaction_builder'; |
||||
|
|
||||
|
export { BIP32Interface } from 'bip32'; |
||||
|
export { Network } from './networks'; |
||||
|
export { Payment, PaymentOpts } from './payments'; |
||||
|
export { OpCode } from './script'; |
||||
|
export { Input as TxInput, Output as TxOutput } from './transaction'; |
@ -0,0 +1,49 @@ |
|||||
|
// https://en.bitcoin.it/wiki/List_of_address_prefixes
|
||||
|
// Dogecoin BIP32 is a proposed standard: https://bitcointalk.org/index.php?topic=409731
|
||||
|
export interface Network { |
||||
|
messagePrefix: string; |
||||
|
bech32: string; |
||||
|
bip32: Bip32; |
||||
|
pubKeyHash: number; |
||||
|
scriptHash: number; |
||||
|
wif: number; |
||||
|
} |
||||
|
|
||||
|
interface Bip32 { |
||||
|
public: number; |
||||
|
private: number; |
||||
|
} |
||||
|
|
||||
|
export const bitcoin: Network = { |
||||
|
messagePrefix: '\x18Bitcoin Signed Message:\n', |
||||
|
bech32: 'bc', |
||||
|
bip32: { |
||||
|
public: 0x0488b21e, |
||||
|
private: 0x0488ade4, |
||||
|
}, |
||||
|
pubKeyHash: 0x00, |
||||
|
scriptHash: 0x05, |
||||
|
wif: 0x80, |
||||
|
}; |
||||
|
export const regtest: Network = { |
||||
|
messagePrefix: '\x18Bitcoin Signed Message:\n', |
||||
|
bech32: 'bcrt', |
||||
|
bip32: { |
||||
|
public: 0x043587cf, |
||||
|
private: 0x04358394, |
||||
|
}, |
||||
|
pubKeyHash: 0x6f, |
||||
|
scriptHash: 0xc4, |
||||
|
wif: 0xef, |
||||
|
}; |
||||
|
export const testnet: Network = { |
||||
|
messagePrefix: '\x18Bitcoin Signed Message:\n', |
||||
|
bech32: 'tb', |
||||
|
bip32: { |
||||
|
public: 0x043587cf, |
||||
|
private: 0x04358394, |
||||
|
}, |
||||
|
pubKeyHash: 0x6f, |
||||
|
scriptHash: 0xc4, |
||||
|
wif: 0xef, |
||||
|
}; |
@ -0,0 +1,58 @@ |
|||||
|
import { bitcoin as BITCOIN_NETWORK } from '../networks'; |
||||
|
import * as bscript from '../script'; |
||||
|
import { Payment, PaymentOpts, Stack } from './index'; |
||||
|
import * as lazy from './lazy'; |
||||
|
|
||||
|
const typef = require('typeforce'); |
||||
|
const OPS = bscript.OPS; |
||||
|
|
||||
|
function stacksEqual(a: Buffer[], b: Buffer[]): boolean { |
||||
|
if (a.length !== b.length) return false; |
||||
|
|
||||
|
return a.every((x, i) => { |
||||
|
return x.equals(b[i]); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
// output: OP_RETURN ...
|
||||
|
export function p2data(a: Payment, opts?: PaymentOpts): Payment { |
||||
|
if (!a.data && !a.output) throw new TypeError('Not enough data'); |
||||
|
opts = Object.assign({ validate: true }, opts || {}); |
||||
|
|
||||
|
typef( |
||||
|
{ |
||||
|
network: typef.maybe(typef.Object), |
||||
|
output: typef.maybe(typef.Buffer), |
||||
|
data: typef.maybe(typef.arrayOf(typef.Buffer)), |
||||
|
}, |
||||
|
a, |
||||
|
); |
||||
|
|
||||
|
const network = a.network || BITCOIN_NETWORK; |
||||
|
const o = { network } as Payment; |
||||
|
|
||||
|
lazy.prop(o, 'output', () => { |
||||
|
if (!a.data) return; |
||||
|
return bscript.compile(([OPS.OP_RETURN] as Stack).concat(a.data)); |
||||
|
}); |
||||
|
lazy.prop(o, 'data', () => { |
||||
|
if (!a.output) return; |
||||
|
return bscript.decompile(a.output)!.slice(1); |
||||
|
}); |
||||
|
|
||||
|
// extended validation
|
||||
|
if (opts.validate) { |
||||
|
if (a.output) { |
||||
|
const chunks = bscript.decompile(a.output); |
||||
|
if (chunks![0] !== OPS.OP_RETURN) |
||||
|
throw new TypeError('Output is invalid'); |
||||
|
if (!chunks!.slice(1).every(typef.Buffer)) |
||||
|
throw new TypeError('Output is invalid'); |
||||
|
|
||||
|
if (a.data && !stacksEqual(a.data, o.data as Buffer[])) |
||||
|
throw new TypeError('Data mismatch'); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return Object.assign(o, a); |
||||
|
} |
@ -0,0 +1,41 @@ |
|||||
|
import { Network } from '../networks'; |
||||
|
import { p2data as embed } from './embed'; |
||||
|
import { p2ms } from './p2ms'; |
||||
|
import { p2pk } from './p2pk'; |
||||
|
import { p2pkh } from './p2pkh'; |
||||
|
import { p2sh } from './p2sh'; |
||||
|
import { p2wpkh } from './p2wpkh'; |
||||
|
import { p2wsh } from './p2wsh'; |
||||
|
|
||||
|
export interface Payment { |
||||
|
network?: Network; |
||||
|
output?: Buffer; |
||||
|
data?: Buffer[]; |
||||
|
m?: number; |
||||
|
n?: number; |
||||
|
pubkeys?: Buffer[]; |
||||
|
input?: Buffer; |
||||
|
signatures?: Buffer[]; |
||||
|
pubkey?: Buffer; |
||||
|
signature?: Buffer; |
||||
|
address?: string; |
||||
|
hash?: Buffer; |
||||
|
redeem?: Payment; |
||||
|
witness?: Buffer[]; |
||||
|
} |
||||
|
|
||||
|
export type PaymentFunction = () => Payment; |
||||
|
|
||||
|
export interface PaymentOpts { |
||||
|
validate?: boolean; |
||||
|
allowIncomplete?: boolean; |
||||
|
} |
||||
|
|
||||
|
export type StackElement = Buffer | number; |
||||
|
export type Stack = StackElement[]; |
||||
|
export type StackFunction = () => Stack; |
||||
|
|
||||
|
export { embed, p2ms, p2pk, p2pkh, p2sh, p2wpkh, p2wsh }; |
||||
|
|
||||
|
// TODO
|
||||
|
// witness commitment
|
@ -0,0 +1,28 @@ |
|||||
|
export function prop(object: {}, name: string, f: () => any): void { |
||||
|
Object.defineProperty(object, name, { |
||||
|
configurable: true, |
||||
|
enumerable: true, |
||||
|
get(): any { |
||||
|
const _value = f.call(this); |
||||
|
this[name] = _value; |
||||
|
return _value; |
||||
|
}, |
||||
|
set(_value: any): void { |
||||
|
Object.defineProperty(this, name, { |
||||
|
configurable: true, |
||||
|
enumerable: true, |
||||
|
value: _value, |
||||
|
writable: true, |
||||
|
}); |
||||
|
}, |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
export function value<T>(f: () => T): () => T { |
||||
|
let _value: T; |
||||
|
return (): T => { |
||||
|
if (_value !== undefined) return _value; |
||||
|
_value = f(); |
||||
|
return _value; |
||||
|
}; |
||||
|
} |
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue