@ledgerhq/hw-app-btc
Advanced tools
Comparing version 1.1.1-beta.068e2a14 to 1.1.2-beta.068e2a14
1221
lib/Btc.js
@@ -7,32 +7,28 @@ "use strict"; | ||
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); /******************************************************************************** | ||
* Ledger Node JS API | ||
* (c) 2016-2017 Ledger | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
********************************************************************************/ | ||
var _utils = require("./utils"); | ||
const MAX_SCRIPT_BLOCK = 50; /******************************************************************************** | ||
* Ledger Node JS API | ||
* (c) 2016-2017 Ledger | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
********************************************************************************/ | ||
// FIXME drop: | ||
const DEFAULT_LOCKTIME = 0; | ||
const DEFAULT_SEQUENCE = 0xffffffff; | ||
const SIGHASH_ALL = 1; | ||
var _utils = require("./utils"); | ||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } | ||
var MAX_SCRIPT_BLOCK = 50; | ||
var DEFAULT_LOCKTIME = 0; | ||
var DEFAULT_SEQUENCE = 0xffffffff; | ||
var SIGHASH_ALL = 1; | ||
/** | ||
@@ -45,6 +41,5 @@ * Bitcoin API. | ||
*/ | ||
var Btc = function () { | ||
function Btc(transport) { | ||
_classCallCheck(this, Btc); | ||
class Btc { | ||
constructor(transport) { | ||
this.transport = transport; | ||
@@ -59,704 +54,586 @@ transport.setScrambleKey("BTC"); | ||
*/ | ||
getWalletPublicKey(path) { | ||
const paths = (0, _utils.splitPath)(path); | ||
const buffer = Buffer.alloc(5 + 1 + paths.length * 4); | ||
buffer[0] = 0xe0; | ||
buffer[1] = 0x40; | ||
buffer[2] = 0x00; | ||
buffer[3] = 0x00; | ||
buffer[4] = 1 + paths.length * 4; | ||
buffer[5] = paths.length; | ||
paths.forEach((element, index) => { | ||
buffer.writeUInt32BE(element, 6 + 4 * index); | ||
}); | ||
return this.transport.exchange(buffer.toString("hex"), [0x9000]).then(responseHex => { | ||
const response = Buffer.from(responseHex, "hex"); | ||
const publicKeyLength = response[0]; | ||
const addressLength = response[1 + publicKeyLength]; | ||
const publicKey = response.slice(1, 1 + publicKeyLength).toString("hex"); | ||
const bitcoinAddress = response.slice(1 + publicKeyLength + 1, 1 + publicKeyLength + 1 + addressLength).toString("ascii"); | ||
const chainCode = response.slice(1 + publicKeyLength + 1 + addressLength, 1 + publicKeyLength + 1 + addressLength + 32).toString("hex"); | ||
return { publicKey, bitcoinAddress, chainCode }; | ||
}); | ||
} | ||
getTrustedInputRaw(transactionData, indexLookup) { | ||
let data; | ||
let firstRound = false; | ||
if (typeof indexLookup === "number") { | ||
firstRound = true; | ||
const prefix = Buffer.alloc(4); | ||
prefix.writeUInt32BE(indexLookup, 0); | ||
data = Buffer.concat([prefix, transactionData], transactionData.length + 4); | ||
} else { | ||
data = transactionData; | ||
} | ||
let buffer = Buffer.alloc(5); | ||
buffer[0] = 0xe0; | ||
buffer[1] = 0x42; | ||
buffer[2] = firstRound ? 0x00 : 0x80; | ||
buffer[3] = 0x00; | ||
buffer[4] = data.length; | ||
buffer = Buffer.concat([buffer, data], 5 + data.length); | ||
return this.transport.exchange(buffer.toString("hex"), [0x9000]).then(trustedInput => trustedInput.substring(0, trustedInput.length - 4)); | ||
} | ||
_createClass(Btc, [{ | ||
key: "getWalletPublicKey", | ||
value: function getWalletPublicKey(path) { | ||
var paths = (0, _utils.splitPath)(path); | ||
var buffer = Buffer.alloc(5 + 1 + paths.length * 4); | ||
buffer[0] = 0xe0; | ||
buffer[1] = 0x40; | ||
buffer[2] = 0x00; | ||
buffer[3] = 0x00; | ||
buffer[4] = 1 + paths.length * 4; | ||
buffer[5] = paths.length; | ||
paths.forEach(function (element, index) { | ||
buffer.writeUInt32BE(element, 6 + 4 * index); | ||
}); | ||
return this.transport.exchange(buffer.toString("hex"), [0x9000]).then(function (responseHex) { | ||
var response = Buffer.from(responseHex, "hex"); | ||
var publicKeyLength = response[0]; | ||
var addressLength = response[1 + publicKeyLength]; | ||
var publicKey = response.slice(1, 1 + publicKeyLength).toString("hex"); | ||
var bitcoinAddress = response.slice(1 + publicKeyLength + 1, 1 + publicKeyLength + 1 + addressLength).toString("ascii"); | ||
var chainCode = response.slice(1 + publicKeyLength + 1 + addressLength, 1 + publicKeyLength + 1 + addressLength + 32).toString("hex"); | ||
return { publicKey: publicKey, bitcoinAddress: bitcoinAddress, chainCode: chainCode }; | ||
}); | ||
getTrustedInput(indexLookup, transaction) { | ||
const { inputs, outputs, locktime } = transaction; | ||
if (!outputs || !locktime) { | ||
throw new Error("getTrustedInput: locktime & outputs is expected"); | ||
} | ||
}, { | ||
key: "getTrustedInputRaw", | ||
value: function getTrustedInputRaw(transactionData, indexLookup) { | ||
var data = void 0; | ||
var firstRound = false; | ||
if (typeof indexLookup === "number") { | ||
firstRound = true; | ||
var prefix = Buffer.alloc(4); | ||
prefix.writeUInt32BE(indexLookup, 0); | ||
data = Buffer.concat([prefix, transactionData], transactionData.length + 4); | ||
} else { | ||
data = transactionData; | ||
} | ||
var buffer = Buffer.alloc(5); | ||
buffer[0] = 0xe0; | ||
buffer[1] = 0x42; | ||
buffer[2] = firstRound ? 0x00 : 0x80; | ||
buffer[3] = 0x00; | ||
buffer[4] = data.length; | ||
buffer = Buffer.concat([buffer, data], 5 + data.length); | ||
return this.transport.exchange(buffer.toString("hex"), [0x9000]).then(function (trustedInput) { | ||
return trustedInput.substring(0, trustedInput.length - 4); | ||
}); | ||
} | ||
}, { | ||
key: "getTrustedInput", | ||
value: function getTrustedInput(indexLookup, transaction) { | ||
var _this = this; | ||
var inputs = transaction.inputs, | ||
outputs = transaction.outputs, | ||
locktime = transaction.locktime; | ||
if (!outputs || !locktime) { | ||
throw new Error("getTrustedInput: locktime & outputs is expected"); | ||
const processScriptBlocks = (script, sequence) => { | ||
const scriptBlocks = []; | ||
let offset = 0; | ||
while (offset !== script.length) { | ||
let blockSize = script.length - offset > MAX_SCRIPT_BLOCK ? MAX_SCRIPT_BLOCK : script.length - offset; | ||
if (offset + blockSize !== script.length) { | ||
scriptBlocks.push(script.slice(offset, offset + blockSize)); | ||
} else { | ||
scriptBlocks.push(Buffer.concat([script.slice(offset, offset + blockSize), sequence])); | ||
} | ||
offset += blockSize; | ||
} | ||
return (0, _utils.eachSeries)(scriptBlocks, scriptBlock => this.getTrustedInputRaw(scriptBlock)); | ||
}; | ||
var processScriptBlocks = function processScriptBlocks(script, sequence) { | ||
var scriptBlocks = []; | ||
var offset = 0; | ||
while (offset !== script.length) { | ||
var blockSize = script.length - offset > MAX_SCRIPT_BLOCK ? MAX_SCRIPT_BLOCK : script.length - offset; | ||
if (offset + blockSize !== script.length) { | ||
scriptBlocks.push(script.slice(offset, offset + blockSize)); | ||
} else { | ||
scriptBlocks.push(Buffer.concat([script.slice(offset, offset + blockSize), sequence])); | ||
} | ||
offset += blockSize; | ||
} | ||
return (0, _utils.eachSeries)(scriptBlocks, function (scriptBlock) { | ||
return _this.getTrustedInputRaw(scriptBlock); | ||
}); | ||
}; | ||
const processInputs = () => { | ||
return (0, _utils.eachSeries)(inputs, input => { | ||
const data = Buffer.concat([input.prevout, this.createVarint(input.script.length)]); | ||
return this.getTrustedInputRaw(data).then(() => | ||
// iteration (eachSeries) ended | ||
// TODO notify progress | ||
// deferred.notify("input"); | ||
processScriptBlocks(input.script, input.sequence)); | ||
}).then(() => { | ||
const data = this.createVarint(outputs.length); | ||
return this.getTrustedInputRaw(data); | ||
}); | ||
}; | ||
var processInputs = function processInputs() { | ||
return (0, _utils.eachSeries)(inputs, function (input) { | ||
var data = Buffer.concat([input.prevout, _this.createVarint(input.script.length)]); | ||
return _this.getTrustedInputRaw(data).then(function () { | ||
return ( | ||
// iteration (eachSeries) ended | ||
// TODO notify progress | ||
// deferred.notify("input"); | ||
processScriptBlocks(input.script, input.sequence) | ||
); | ||
}); | ||
}).then(function () { | ||
var data = _this.createVarint(outputs.length); | ||
return _this.getTrustedInputRaw(data); | ||
}); | ||
}; | ||
const processOutputs = () => (0, _utils.eachSeries)(outputs, output => { | ||
let data = output.amount; | ||
data = Buffer.concat([data, this.createVarint(output.script.length), output.script]); | ||
return this.getTrustedInputRaw(data).then(() => { | ||
// iteration (eachSeries) ended | ||
// TODO notify progress | ||
// deferred.notify("output"); | ||
}); | ||
}).then(() => this.getTrustedInputRaw(locktime)); | ||
var processOutputs = function processOutputs() { | ||
return (0, _utils.eachSeries)(outputs, function (output) { | ||
var data = output.amount; | ||
data = Buffer.concat([data, _this.createVarint(output.script.length), output.script]); | ||
return _this.getTrustedInputRaw(data).then(function () { | ||
// iteration (eachSeries) ended | ||
// TODO notify progress | ||
// deferred.notify("output"); | ||
}); | ||
}).then(function () { | ||
return _this.getTrustedInputRaw(locktime); | ||
}); | ||
}; | ||
const data = Buffer.concat([transaction.version, this.createVarint(inputs.length)]); | ||
return this.getTrustedInputRaw(data, indexLookup).then(processInputs).then(processOutputs); | ||
} | ||
var data = Buffer.concat([transaction.version, this.createVarint(inputs.length)]); | ||
return this.getTrustedInputRaw(data, indexLookup).then(processInputs).then(processOutputs); | ||
getVarint(data, offset) { | ||
if (data[offset] < 0xfd) { | ||
return [data[offset], 1]; | ||
} | ||
}, { | ||
key: "getVarint", | ||
value: function getVarint(data, offset) { | ||
if (data[offset] < 0xfd) { | ||
return [data[offset], 1]; | ||
} | ||
if (data[offset] === 0xfd) { | ||
return [(data[offset + 2] << 8) + data[offset + 1], 3]; | ||
} | ||
if (data[offset] === 0xfe) { | ||
return [(data[offset + 4] << 24) + (data[offset + 3] << 16) + (data[offset + 2] << 8) + data[offset + 1], 5]; | ||
} | ||
throw new Error("getVarint called with unexpected parameters"); | ||
if (data[offset] === 0xfd) { | ||
return [(data[offset + 2] << 8) + data[offset + 1], 3]; | ||
} | ||
}, { | ||
key: "startUntrustedHashTransactionInputRaw", | ||
value: function startUntrustedHashTransactionInputRaw(newTransaction, firstRound, transactionData) { | ||
var buffer = Buffer.alloc(5); | ||
buffer[0] = 0xe0; | ||
buffer[1] = 0x44; | ||
buffer[2] = firstRound ? 0x00 : 0x80; | ||
buffer[3] = newTransaction ? 0x00 : 0x80; | ||
buffer[4] = transactionData.length; | ||
buffer = Buffer.concat([buffer, transactionData], 5 + transactionData.length); | ||
return this.transport.exchange(buffer.toString("hex"), [0x9000]); | ||
if (data[offset] === 0xfe) { | ||
return [(data[offset + 4] << 24) + (data[offset + 3] << 16) + (data[offset + 2] << 8) + data[offset + 1], 5]; | ||
} | ||
}, { | ||
key: "startUntrustedHashTransactionInput", | ||
value: function startUntrustedHashTransactionInput(newTransaction, transaction, inputs) { | ||
var _this2 = this; | ||
var data = Buffer.concat([transaction.version, this.createVarint(transaction.inputs.length)]); | ||
return this.startUntrustedHashTransactionInputRaw(newTransaction, true, data).then(function () { | ||
var i = 0; | ||
return (0, _utils.eachSeries)(transaction.inputs, function (input) { | ||
// TODO : segwit | ||
var prefix = void 0; | ||
if (inputs[i].trustedInput) { | ||
prefix = Buffer.alloc(2); | ||
prefix[0] = 0x01; | ||
prefix[1] = inputs[i].value.length; | ||
throw new Error("getVarint called with unexpected parameters"); | ||
} | ||
startUntrustedHashTransactionInputRaw(newTransaction, firstRound, transactionData) { | ||
let buffer = Buffer.alloc(5); | ||
buffer[0] = 0xe0; | ||
buffer[1] = 0x44; | ||
buffer[2] = firstRound ? 0x00 : 0x80; | ||
buffer[3] = newTransaction ? 0x00 : 0x80; | ||
buffer[4] = transactionData.length; | ||
buffer = Buffer.concat([buffer, transactionData], 5 + transactionData.length); | ||
return this.transport.exchange(buffer.toString("hex"), [0x9000]); | ||
} | ||
startUntrustedHashTransactionInput(newTransaction, transaction, inputs) { | ||
let data = Buffer.concat([transaction.version, this.createVarint(transaction.inputs.length)]); | ||
return this.startUntrustedHashTransactionInputRaw(newTransaction, true, data).then(() => { | ||
let i = 0; | ||
return (0, _utils.eachSeries)(transaction.inputs, input => { | ||
// TODO : segwit | ||
let prefix; | ||
if (inputs[i].trustedInput) { | ||
prefix = Buffer.alloc(2); | ||
prefix[0] = 0x01; | ||
prefix[1] = inputs[i].value.length; | ||
} else { | ||
prefix = Buffer.alloc(1); | ||
prefix[0] = 0x00; | ||
} | ||
data = Buffer.concat([prefix, inputs[i].value, this.createVarint(input.script.length)]); | ||
return this.startUntrustedHashTransactionInputRaw(newTransaction, false, data).then(() => { | ||
let scriptBlocks = []; | ||
let offset = 0; | ||
if (input.script.length === 0) { | ||
scriptBlocks.push(input.sequence); | ||
} else { | ||
prefix = Buffer.alloc(1); | ||
prefix[0] = 0x00; | ||
} | ||
data = Buffer.concat([prefix, inputs[i].value, _this2.createVarint(input.script.length)]); | ||
return _this2.startUntrustedHashTransactionInputRaw(newTransaction, false, data).then(function () { | ||
var scriptBlocks = []; | ||
var offset = 0; | ||
if (input.script.length === 0) { | ||
scriptBlocks.push(input.sequence); | ||
} else { | ||
while (offset !== input.script.length) { | ||
var blockSize = input.script.length - offset > MAX_SCRIPT_BLOCK ? MAX_SCRIPT_BLOCK : input.script.length - offset; | ||
if (offset + blockSize !== input.script.length) { | ||
scriptBlocks.push(input.script.slice(offset, offset + blockSize)); | ||
} else { | ||
scriptBlocks.push(Buffer.concat([input.script.slice(offset, offset + blockSize), input.sequence])); | ||
} | ||
offset += blockSize; | ||
while (offset !== input.script.length) { | ||
let blockSize = input.script.length - offset > MAX_SCRIPT_BLOCK ? MAX_SCRIPT_BLOCK : input.script.length - offset; | ||
if (offset + blockSize !== input.script.length) { | ||
scriptBlocks.push(input.script.slice(offset, offset + blockSize)); | ||
} else { | ||
scriptBlocks.push(Buffer.concat([input.script.slice(offset, offset + blockSize), input.sequence])); | ||
} | ||
offset += blockSize; | ||
} | ||
return (0, _utils.eachSeries)(scriptBlocks, function (scriptBlock) { | ||
return _this2.startUntrustedHashTransactionInputRaw(newTransaction, false, scriptBlock); | ||
}).then(function () { | ||
i++; | ||
}); | ||
} | ||
return (0, _utils.eachSeries)(scriptBlocks, scriptBlock => { | ||
return this.startUntrustedHashTransactionInputRaw(newTransaction, false, scriptBlock); | ||
}).then(() => { | ||
i++; | ||
}); | ||
}); | ||
}); | ||
}); | ||
} | ||
provideOutputFullChangePath(path) { | ||
let paths = (0, _utils.splitPath)(path); | ||
let buffer = Buffer.alloc(5 + 1 + paths.length * 4); | ||
buffer[0] = 0xe0; | ||
buffer[1] = 0x4a; | ||
buffer[2] = 0xff; | ||
buffer[3] = 0x00; | ||
buffer[4] = 1 + paths.length * 4; | ||
buffer[5] = paths.length; | ||
paths.forEach((element, index) => { | ||
buffer.writeUInt32BE(element, 6 + 4 * index); | ||
}); | ||
return this.transport.exchange(buffer.toString("hex"), [0x9000]); | ||
} | ||
hashOutputFull(outputScript) { | ||
let offset = 0; | ||
return (0, _utils.asyncWhile)(() => offset < outputScript.length, () => { | ||
let blockSize = offset + MAX_SCRIPT_BLOCK >= outputScript.length ? outputScript.length - offset : MAX_SCRIPT_BLOCK; | ||
let p1 = offset + blockSize === outputScript.length ? 0x80 : 0x00; | ||
let prefix = Buffer.alloc(5); | ||
prefix[0] = 0xe0; | ||
prefix[1] = 0x4a; | ||
prefix[2] = p1; | ||
prefix[3] = 0x00; | ||
prefix[4] = blockSize; | ||
let data = Buffer.concat([prefix, outputScript.slice(offset, offset + blockSize)]); | ||
return this.transport.exchange(data.toString("hex"), [0x9000]).then(() => { | ||
offset += blockSize; | ||
}); | ||
}); | ||
} | ||
/** | ||
*/ | ||
signTransaction(path, lockTime = DEFAULT_LOCKTIME, sigHashType = SIGHASH_ALL) { | ||
const paths = (0, _utils.splitPath)(path); | ||
const buffer = Buffer.alloc(5 + 1 + paths.length * 4 + 1 + 4 + 1); | ||
let offset = 0; | ||
buffer[offset++] = 0xe0; | ||
buffer[offset++] = 0x48; | ||
buffer[offset++] = 0x00; | ||
buffer[offset++] = 0x00; | ||
buffer[offset++] = 1 + paths.length * 4 + 1 + 4 + 1; | ||
buffer[offset++] = paths.length; | ||
paths.forEach(element => { | ||
buffer.writeUInt32BE(element, offset); | ||
offset += 4; | ||
}); | ||
buffer[offset++] = 0x00; // authorization length | ||
buffer.writeUInt32LE(lockTime, offset); | ||
offset += 4; | ||
buffer[offset++] = sigHashType; | ||
return this.transport.exchange(buffer.toString("hex"), [0x9000]).then(signature => { | ||
const result = Buffer.from(signature, "hex"); | ||
result[0] = 0x30; | ||
return result.slice(0, result.length - 2); | ||
}); | ||
} | ||
/** | ||
* You can sign a message according to the Bitcoin Signature format and retrieve v, r, s given the message and the BIP 32 path of the account to sign. | ||
* @example | ||
btc.signMessageNew_async("44'/60'/0'/0'/0", Buffer.from("test").toString("hex")).then(function(result) { | ||
var v = result['v'] + 27 + 4; | ||
var signature = Buffer.from(v.toString(16) + result['r'] + result['s'], 'hex').toString('base64'); | ||
console.log("Signature : " + signature); | ||
}).catch(function(ex) {console.log(ex);}); | ||
*/ | ||
signMessageNew(path, messageHex) { | ||
const paths = (0, _utils.splitPath)(path); | ||
const message = new Buffer(messageHex, "hex"); | ||
let offset = 0; | ||
const apdus = []; | ||
while (offset !== message.length) { | ||
let maxChunkSize = offset === 0 ? MAX_SCRIPT_BLOCK - 1 - paths.length * 4 - 4 : MAX_SCRIPT_BLOCK; | ||
let chunkSize = offset + maxChunkSize > message.length ? message.length - offset : maxChunkSize; | ||
const buffer = new Buffer(offset === 0 ? 5 + 1 + paths.length * 4 + 2 + chunkSize : 5 + chunkSize); | ||
buffer[0] = 0xe0; | ||
buffer[1] = 0x4e; | ||
buffer[2] = 0x00; | ||
buffer[3] = offset === 0 ? 0x01 : 0x80; | ||
buffer[4] = offset === 0 ? 1 + paths.length * 4 + 2 + chunkSize : chunkSize; | ||
if (offset === 0) { | ||
buffer[5] = paths.length; | ||
paths.forEach((element, index) => { | ||
buffer.writeUInt32BE(element, 6 + 4 * index); | ||
}); | ||
buffer.writeUInt16BE(message.length, 6 + 4 * paths.length); | ||
message.copy(buffer, 6 + 4 * paths.length + 2, offset, offset + chunkSize); | ||
} else { | ||
message.copy(buffer, 5, offset, offset + chunkSize); | ||
} | ||
apdus.push(buffer.toString("hex")); | ||
offset += chunkSize; | ||
} | ||
}, { | ||
key: "provideOutputFullChangePath", | ||
value: function provideOutputFullChangePath(path) { | ||
var paths = (0, _utils.splitPath)(path); | ||
var buffer = Buffer.alloc(5 + 1 + paths.length * 4); | ||
return (0, _utils.foreach)(apdus, apdu => this.transport.exchange(apdu, [0x9000])).then(() => { | ||
const buffer = Buffer.alloc(6); | ||
buffer[0] = 0xe0; | ||
buffer[1] = 0x4a; | ||
buffer[2] = 0xff; | ||
buffer[1] = 0x4e; | ||
buffer[2] = 0x80; | ||
buffer[3] = 0x00; | ||
buffer[4] = 1 + paths.length * 4; | ||
buffer[5] = paths.length; | ||
paths.forEach(function (element, index) { | ||
buffer.writeUInt32BE(element, 6 + 4 * index); | ||
buffer[4] = 0x01; | ||
buffer[5] = 0x00; | ||
return this.transport.exchange(buffer.toString("hex"), [0x9000]).then(apduResponse => { | ||
const response = Buffer.from(apduResponse, "hex"); | ||
const v = response[0] - 0x30; | ||
let r = response.slice(4, 4 + response[3]); | ||
if (r[0] === 0) { | ||
r = r.slice(1); | ||
} | ||
r = r.toString("hex"); | ||
let offset = 4 + response[3] + 2; | ||
let s = response.slice(offset, offset + response[offset - 1]); | ||
if (s[0] === 0) { | ||
s = s.slice(1); | ||
} | ||
s = s.toString("hex"); | ||
return { v, r, s }; | ||
}); | ||
return this.transport.exchange(buffer.toString("hex"), [0x9000]); | ||
} | ||
}, { | ||
key: "hashOutputFull", | ||
value: function hashOutputFull(outputScript) { | ||
var _this3 = this; | ||
}); | ||
} | ||
var offset = 0; | ||
return (0, _utils.asyncWhile)(function () { | ||
return offset < outputScript.length; | ||
}, function () { | ||
var blockSize = offset + MAX_SCRIPT_BLOCK >= outputScript.length ? outputScript.length - offset : MAX_SCRIPT_BLOCK; | ||
var p1 = offset + blockSize === outputScript.length ? 0x80 : 0x00; | ||
var prefix = Buffer.alloc(5); | ||
prefix[0] = 0xe0; | ||
prefix[1] = 0x4a; | ||
prefix[2] = p1; | ||
prefix[3] = 0x00; | ||
prefix[4] = blockSize; | ||
var data = Buffer.concat([prefix, outputScript.slice(offset, offset + blockSize)]); | ||
return _this3.transport.exchange(data.toString("hex"), [0x9000]).then(function () { | ||
offset += blockSize; | ||
}); | ||
}); | ||
} | ||
/** | ||
* To sign a transaction involving standard (P2PKH) inputs, call createPaymentTransactionNew with the following parameters | ||
* @param inputs is an array of [ transaction, output_index, optional redeem script, optional sequence ] where | ||
* * transaction is the previously computed transaction object for this UTXO | ||
* * output_index is the output in the transaction used as input for this UTXO (counting from 0) | ||
* * redeem script is the optional redeem script to use when consuming a Segregated Witness input | ||
* * sequence is the sequence number to use for this input (when using RBF), or non present | ||
* @param associatedKeysets is an array of BIP 32 paths pointing to the path to the private key used for each UTXO | ||
* @param changePath is an optional BIP 32 path pointing to the path to the public key used to compute the change address | ||
* @param outputScript is the hexadecimal serialized outputs of the transaction to sign | ||
* @param lockTime is the optional lockTime of the transaction to sign, or default (0) | ||
* @param sigHashType is the hash type of the transaction to sign, or default (all) | ||
* @return the signed transaction ready to be broadcast | ||
* @example | ||
btc.createPaymentTransactionNew( | ||
[ [tx1, 1] ], | ||
["0'/0/0"], | ||
undefined, | ||
"01905f0100000000001976a91472a5d75c8d2d0565b656a5232703b167d50d5a2b88ac" | ||
).then(res => ...); | ||
*/ | ||
createPaymentTransactionNew(inputs, associatedKeysets, changePath, outputScriptHex, lockTime = DEFAULT_LOCKTIME, sigHashType = SIGHASH_ALL) { | ||
// Inputs are provided as arrays of [transaction, output_index, optional redeem script, optional sequence] | ||
// associatedKeysets are provided as arrays of [path] | ||
const nullScript = Buffer.alloc(0); | ||
const nullPrevout = Buffer.alloc(0); | ||
const defaultVersion = Buffer.alloc(4); | ||
defaultVersion.writeUInt32LE(1, 0); | ||
const trustedInputs = []; | ||
const regularOutputs = []; | ||
const signatures = []; | ||
const publicKeys = []; | ||
let firstRun = true; | ||
const resuming = false; | ||
const targetTransaction = { | ||
inputs: [], | ||
version: defaultVersion | ||
}; | ||
/** | ||
*/ | ||
const outputScript = Buffer.from(outputScriptHex, "hex"); | ||
}, { | ||
key: "signTransaction", | ||
value: function signTransaction(path) { | ||
var lockTime = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : DEFAULT_LOCKTIME; | ||
var sigHashType = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : SIGHASH_ALL; | ||
var paths = (0, _utils.splitPath)(path); | ||
var buffer = Buffer.alloc(5 + 1 + paths.length * 4 + 1 + 4 + 1); | ||
var offset = 0; | ||
buffer[offset++] = 0xe0; | ||
buffer[offset++] = 0x48; | ||
buffer[offset++] = 0x00; | ||
buffer[offset++] = 0x00; | ||
buffer[offset++] = 1 + paths.length * 4 + 1 + 4 + 1; | ||
buffer[offset++] = paths.length; | ||
paths.forEach(function (element) { | ||
buffer.writeUInt32BE(element, offset); | ||
offset += 4; | ||
return (0, _utils.foreach)(inputs, input => (0, _utils.doIf)(!resuming, () => this.getTrustedInput(input[1], input[0]).then(trustedInput => { | ||
trustedInputs.push({ | ||
trustedInput: true, | ||
value: Buffer.from(trustedInput, "hex") | ||
}); | ||
buffer[offset++] = 0x00; // authorization length | ||
buffer.writeUInt32LE(lockTime, offset); | ||
offset += 4; | ||
buffer[offset++] = sigHashType; | ||
return this.transport.exchange(buffer.toString("hex"), [0x9000]).then(function (signature) { | ||
var result = Buffer.from(signature, "hex"); | ||
result[0] = 0x30; | ||
return result.slice(0, result.length - 2); | ||
}); | ||
} | ||
/** | ||
* You can sign a message according to the Bitcoin Signature format and retrieve v, r, s given the message and the BIP 32 path of the account to sign. | ||
* @example | ||
btc.signMessageNew_async("44'/60'/0'/0'/0", Buffer.from("test").toString("hex")).then(function(result) { | ||
var v = result['v'] + 27 + 4; | ||
var signature = Buffer.from(v.toString(16) + result['r'] + result['s'], 'hex').toString('base64'); | ||
console.log("Signature : " + signature); | ||
}).catch(function(ex) {console.log(ex);}); | ||
*/ | ||
}, { | ||
key: "signMessageNew", | ||
value: function signMessageNew(path, messageHex) { | ||
var _this4 = this; | ||
var paths = (0, _utils.splitPath)(path); | ||
var message = new Buffer(messageHex, "hex"); | ||
var offset = 0; | ||
var apdus = []; | ||
var _loop = function _loop() { | ||
var maxChunkSize = offset === 0 ? MAX_SCRIPT_BLOCK - 1 - paths.length * 4 - 4 : MAX_SCRIPT_BLOCK; | ||
var chunkSize = offset + maxChunkSize > message.length ? message.length - offset : maxChunkSize; | ||
var buffer = new Buffer(offset === 0 ? 5 + 1 + paths.length * 4 + 2 + chunkSize : 5 + chunkSize); | ||
buffer[0] = 0xe0; | ||
buffer[1] = 0x4e; | ||
buffer[2] = 0x00; | ||
buffer[3] = offset === 0 ? 0x01 : 0x80; | ||
buffer[4] = offset === 0 ? 1 + paths.length * 4 + 2 + chunkSize : chunkSize; | ||
if (offset === 0) { | ||
buffer[5] = paths.length; | ||
paths.forEach(function (element, index) { | ||
buffer.writeUInt32BE(element, 6 + 4 * index); | ||
}); | ||
buffer.writeUInt16BE(message.length, 6 + 4 * paths.length); | ||
message.copy(buffer, 6 + 4 * paths.length + 2, offset, offset + chunkSize); | ||
} else { | ||
message.copy(buffer, 5, offset, offset + chunkSize); | ||
})).then(() => { | ||
const { outputs } = input[0]; | ||
const index = input[1]; | ||
if (outputs && index <= outputs.length - 1) { | ||
regularOutputs.push(outputs[index]); | ||
} | ||
})).then(() => { | ||
for (let i = 0; i < inputs.length; i++) { | ||
let sequence = Buffer.alloc(4); | ||
sequence.writeUInt32LE(inputs[i].length >= 4 && typeof inputs[i][3] === "number" ? inputs[i][3] : DEFAULT_SEQUENCE, 0); | ||
targetTransaction.inputs.push({ | ||
script: nullScript, | ||
prevout: nullPrevout, | ||
sequence | ||
}); | ||
} | ||
}).then(() => { | ||
return (0, _utils.doIf)(!resuming, () => | ||
// Collect public keys | ||
(0, _utils.foreach)(inputs, (input, i) => this.getWalletPublicKey(associatedKeysets[i])).then(result => { | ||
for (let index = 0; index < result.length; index++) { | ||
publicKeys.push(this.compressPublicKey(Buffer.from(result[index].publicKey, "hex"))); | ||
} | ||
apdus.push(buffer.toString("hex")); | ||
offset += chunkSize; | ||
}; | ||
while (offset !== message.length) { | ||
_loop(); | ||
})); | ||
}).then(() => (0, _utils.foreach)(inputs, (input, i) => { | ||
targetTransaction.inputs[i].script = inputs[i].length >= 3 && typeof inputs[i][2] === "string" ? Buffer.from(inputs[i][2], "hex") : regularOutputs[i].script; | ||
return this.startUntrustedHashTransactionInput(firstRun, targetTransaction, trustedInputs).then(() => Promise.resolve().then(() => { | ||
if (!resuming && typeof changePath !== "undefined") { | ||
return this.provideOutputFullChangePath(changePath); | ||
} | ||
}).then(() => this.hashOutputFull(outputScript)).then(() => this.signTransaction(associatedKeysets[i], lockTime, sigHashType).then(signature => { | ||
signatures.push(signature); | ||
targetTransaction.inputs[i].script = nullScript; | ||
if (firstRun) { | ||
firstRun = false; | ||
} | ||
}))); | ||
})).then(() => { | ||
// Populate the final input scripts | ||
for (let i = 0; i < inputs.length; i++) { | ||
const signatureSize = Buffer.alloc(1); | ||
const keySize = Buffer.alloc(1); | ||
signatureSize[0] = signatures[i].length; | ||
keySize[0] = publicKeys[i].length; | ||
targetTransaction.inputs[i].script = Buffer.concat([signatureSize, signatures[i], keySize, publicKeys[i]]); | ||
targetTransaction.inputs[i].prevout = trustedInputs[i].value.slice(4, 4 + 0x24); | ||
} | ||
return (0, _utils.foreach)(apdus, function (apdu) { | ||
return _this4.transport.exchange(apdu, [0x9000]); | ||
}).then(function () { | ||
var buffer = Buffer.alloc(6); | ||
buffer[0] = 0xe0; | ||
buffer[1] = 0x4e; | ||
buffer[2] = 0x80; | ||
buffer[3] = 0x00; | ||
buffer[4] = 0x01; | ||
buffer[5] = 0x00; | ||
return _this4.transport.exchange(buffer.toString("hex"), [0x9000]).then(function (apduResponse) { | ||
var response = Buffer.from(apduResponse, "hex"); | ||
var v = response[0] - 0x30; | ||
var r = response.slice(4, 4 + response[3]); | ||
if (r[0] === 0) { | ||
r = r.slice(1); | ||
} | ||
r = r.toString("hex"); | ||
var offset = 4 + response[3] + 2; | ||
var s = response.slice(offset, offset + response[offset - 1]); | ||
if (s[0] === 0) { | ||
s = s.slice(1); | ||
} | ||
s = s.toString("hex"); | ||
return { v: v, r: r, s: s }; | ||
}); | ||
}); | ||
} | ||
/** | ||
* To sign a transaction involving standard (P2PKH) inputs, call createPaymentTransactionNew with the following parameters | ||
* @param inputs is an array of [ transaction, output_index, optional redeem script, optional sequence ] where | ||
* * transaction is the previously computed transaction object for this UTXO | ||
* * output_index is the output in the transaction used as input for this UTXO (counting from 0) | ||
* * redeem script is the optional redeem script to use when consuming a Segregated Witness input | ||
* * sequence is the sequence number to use for this input (when using RBF), or non present | ||
* @param associatedKeysets is an array of BIP 32 paths pointing to the path to the private key used for each UTXO | ||
* @param changePath is an optional BIP 32 path pointing to the path to the public key used to compute the change address | ||
* @param outputScript is the hexadecimal serialized outputs of the transaction to sign | ||
* @param lockTime is the optional lockTime of the transaction to sign, or default (0) | ||
* @param sigHashType is the hash type of the transaction to sign, or default (all) | ||
* @return the signed transaction ready to be broadcast | ||
* @example | ||
btc.createPaymentTransactionNew( | ||
[ [tx1, 1] ], | ||
["0'/0/0"], | ||
undefined, | ||
"01905f0100000000001976a91472a5d75c8d2d0565b656a5232703b167d50d5a2b88ac" | ||
).then(res => ...); | ||
*/ | ||
const lockTimeBuffer = Buffer.alloc(4); | ||
lockTimeBuffer.writeUInt32LE(lockTime, 0); | ||
}, { | ||
key: "createPaymentTransactionNew", | ||
value: function createPaymentTransactionNew(inputs, associatedKeysets, changePath, outputScriptHex) { | ||
var _this5 = this; | ||
const result = Buffer.concat([this.serializeTransaction(targetTransaction), outputScript, lockTimeBuffer]); | ||
var lockTime = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : DEFAULT_LOCKTIME; | ||
var sigHashType = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : SIGHASH_ALL; | ||
return result.toString("hex"); | ||
}); | ||
} | ||
// Inputs are provided as arrays of [transaction, output_index, optional redeem script, optional sequence] | ||
// associatedKeysets are provided as arrays of [path] | ||
var nullScript = Buffer.alloc(0); | ||
var nullPrevout = Buffer.alloc(0); | ||
var defaultVersion = Buffer.alloc(4); | ||
defaultVersion.writeUInt32LE(1, 0); | ||
var trustedInputs = []; | ||
var regularOutputs = []; | ||
var signatures = []; | ||
var publicKeys = []; | ||
var firstRun = true; | ||
var resuming = false; | ||
var targetTransaction = { | ||
inputs: [], | ||
version: defaultVersion | ||
}; | ||
/** | ||
* To obtain the signature of multisignature (P2SH) inputs, call signP2SHTransaction_async with the folowing parameters | ||
* @param inputs is an array of [ transaction, output_index, redeem script, optional sequence ] where | ||
* * transaction is the previously computed transaction object for this UTXO | ||
* * output_index is the output in the transaction used as input for this UTXO (counting from 0) | ||
* * redeem script is the mandatory redeem script associated to the current P2SH input | ||
* * sequence is the sequence number to use for this input (when using RBF), or non present | ||
* @param associatedKeysets is an array of BIP 32 paths pointing to the path to the private key used for each UTXO | ||
* @param outputScript is the hexadecimal serialized outputs of the transaction to sign | ||
* @param lockTime is the optional lockTime of the transaction to sign, or default (0) | ||
* @param sigHashType is the hash type of the transaction to sign, or default (all) | ||
* @return the signed transaction ready to be broadcast | ||
* @example | ||
btc.signP2SHTransaction( | ||
[ [tx, 1, "52210289b4a3ad52a919abd2bdd6920d8a6879b1e788c38aa76f0440a6f32a9f1996d02103a3393b1439d1693b063482c04bd40142db97bdf139eedd1b51ffb7070a37eac321030b9a409a1e476b0d5d17b804fcdb81cf30f9b99c6f3ae1178206e08bc500639853ae"] ], | ||
["0'/0/0"], | ||
"01905f0100000000001976a91472a5d75c8d2d0565b656a5232703b167d50d5a2b88ac" | ||
).then(result => ...); | ||
*/ | ||
signP2SHTransaction(inputs, associatedKeysets, outputScriptHex, lockTime = DEFAULT_LOCKTIME, sigHashType = SIGHASH_ALL) { | ||
// Inputs are provided as arrays of [transaction, output_index, redeem script, optional sequence] | ||
// associatedKeysets are provided as arrays of [path] | ||
const nullScript = Buffer.alloc(0); | ||
const nullPrevout = Buffer.alloc(0); | ||
const defaultVersion = Buffer.alloc(4); | ||
defaultVersion.writeUInt32LE(1, 0); | ||
const trustedInputs = []; | ||
const regularOutputs = []; | ||
const signatures = []; | ||
let firstRun = true; | ||
let resuming = false; | ||
let targetTransaction = { | ||
inputs: [], | ||
version: defaultVersion | ||
}; | ||
var outputScript = Buffer.from(outputScriptHex, "hex"); | ||
const outputScript = Buffer.from(outputScriptHex, "hex"); | ||
return (0, _utils.foreach)(inputs, function (input) { | ||
return (0, _utils.doIf)(!resuming, function () { | ||
return _this5.getTrustedInput(input[1], input[0]).then(function (trustedInput) { | ||
trustedInputs.push({ | ||
trustedInput: true, | ||
value: Buffer.from(trustedInput, "hex") | ||
}); | ||
}); | ||
}).then(function () { | ||
var outputs = input[0].outputs; | ||
var index = input[1]; | ||
if (outputs && index <= outputs.length - 1) { | ||
regularOutputs.push(outputs[index]); | ||
} | ||
}); | ||
}).then(function () { | ||
for (var i = 0; i < inputs.length; i++) { | ||
var _sequence = Buffer.alloc(4); | ||
_sequence.writeUInt32LE(inputs[i].length >= 4 && typeof inputs[i][3] === "number" ? inputs[i][3] : DEFAULT_SEQUENCE, 0); | ||
targetTransaction.inputs.push({ | ||
script: nullScript, | ||
prevout: nullPrevout, | ||
sequence: _sequence | ||
}); | ||
return (0, _utils.foreach)(inputs, input => (0, _utils.doIf)(!resuming, () => this.getTrustedInput(input[1], input[0]).then(trustedInput => { | ||
let inputItem = {}; | ||
inputItem.trustedInput = false; | ||
inputItem.value = Buffer.from(trustedInput, "hex").slice(4, 4 + 0x24); | ||
trustedInputs.push(inputItem); | ||
})).then(() => { | ||
const { outputs } = input[0]; | ||
const index = input[1]; | ||
if (outputs && index <= outputs.length - 1) { | ||
regularOutputs.push(outputs[index]); | ||
} | ||
})).then(() => { | ||
// Pre-build the target transaction | ||
for (let i = 0; i < inputs.length; i++) { | ||
let tmp = Buffer.alloc(4); | ||
let sequence; | ||
if (inputs[i].length >= 4 && typeof inputs[i][3] === "number") { | ||
sequence = inputs[i][3]; | ||
} else { | ||
sequence = DEFAULT_SEQUENCE; | ||
} | ||
}).then(function () { | ||
return (0, _utils.doIf)(!resuming, function () { | ||
return ( | ||
// Collect public keys | ||
(0, _utils.foreach)(inputs, function (input, i) { | ||
return _this5.getWalletPublicKey(associatedKeysets[i]); | ||
}).then(function (result) { | ||
for (var index = 0; index < result.length; index++) { | ||
publicKeys.push(_this5.compressPublicKey(Buffer.from(result[index].publicKey, "hex"))); | ||
} | ||
}) | ||
); | ||
tmp.writeUInt32LE(sequence, 0); | ||
targetTransaction.inputs.push({ | ||
prevout: nullPrevout, | ||
script: nullScript, | ||
sequence: tmp | ||
}); | ||
}).then(function () { | ||
return (0, _utils.foreach)(inputs, function (input, i) { | ||
targetTransaction.inputs[i].script = inputs[i].length >= 3 && typeof inputs[i][2] === "string" ? Buffer.from(inputs[i][2], "hex") : regularOutputs[i].script; | ||
return _this5.startUntrustedHashTransactionInput(firstRun, targetTransaction, trustedInputs).then(function () { | ||
return Promise.resolve().then(function () { | ||
if (!resuming && typeof changePath !== "undefined") { | ||
return _this5.provideOutputFullChangePath(changePath); | ||
} | ||
}).then(function () { | ||
return _this5.hashOutputFull(outputScript); | ||
}).then(function () { | ||
return _this5.signTransaction(associatedKeysets[i], lockTime, sigHashType).then(function (signature) { | ||
signatures.push(signature); | ||
targetTransaction.inputs[i].script = nullScript; | ||
if (firstRun) { | ||
firstRun = false; | ||
} | ||
}); | ||
}); | ||
}); | ||
}); | ||
}).then(function () { | ||
// Populate the final input scripts | ||
for (var i = 0; i < inputs.length; i++) { | ||
var signatureSize = Buffer.alloc(1); | ||
var keySize = Buffer.alloc(1); | ||
signatureSize[0] = signatures[i].length; | ||
keySize[0] = publicKeys[i].length; | ||
targetTransaction.inputs[i].script = Buffer.concat([signatureSize, signatures[i], keySize, publicKeys[i]]); | ||
targetTransaction.inputs[i].prevout = trustedInputs[i].value.slice(4, 4 + 0x24); | ||
} | ||
}).then(() => (0, _utils.foreach)(inputs, (input, i) => { | ||
targetTransaction.inputs[i].script = inputs[i].length >= 3 && typeof inputs[i][2] === "string" ? Buffer.from(inputs[i][2], "hex") : regularOutputs[i].script; | ||
return this.startUntrustedHashTransactionInput(firstRun, targetTransaction, trustedInputs).then(() => this.hashOutputFull(outputScript)).then(() => this.signTransaction(associatedKeysets[i], lockTime, sigHashType).then(signature => { | ||
signatures.push(signature.slice(0, signature.length - 1).toString("hex")); | ||
targetTransaction.inputs[i].script = nullScript; | ||
if (firstRun) { | ||
firstRun = false; | ||
} | ||
})); | ||
})).then(() => signatures); | ||
} | ||
var lockTimeBuffer = Buffer.alloc(4); | ||
lockTimeBuffer.writeUInt32LE(lockTime, 0); | ||
compressPublicKey(publicKey) { | ||
const prefix = (publicKey[64] & 1) !== 0 ? 0x03 : 0x02; | ||
const prefixBuffer = Buffer.alloc(1); | ||
prefixBuffer[0] = prefix; | ||
return Buffer.concat([prefixBuffer, publicKey.slice(1, 1 + 32)]); | ||
} | ||
var result = Buffer.concat([_this5.serializeTransaction(targetTransaction), outputScript, lockTimeBuffer]); | ||
return result.toString("hex"); | ||
}); | ||
createVarint(value) { | ||
if (value < 0xfd) { | ||
const buffer = Buffer.alloc(1); | ||
buffer[0] = value; | ||
return buffer; | ||
} | ||
/** | ||
* To obtain the signature of multisignature (P2SH) inputs, call signP2SHTransaction_async with the folowing parameters | ||
* @param inputs is an array of [ transaction, output_index, redeem script, optional sequence ] where | ||
* * transaction is the previously computed transaction object for this UTXO | ||
* * output_index is the output in the transaction used as input for this UTXO (counting from 0) | ||
* * redeem script is the mandatory redeem script associated to the current P2SH input | ||
* * sequence is the sequence number to use for this input (when using RBF), or non present | ||
* @param associatedKeysets is an array of BIP 32 paths pointing to the path to the private key used for each UTXO | ||
* @param outputScript is the hexadecimal serialized outputs of the transaction to sign | ||
* @param lockTime is the optional lockTime of the transaction to sign, or default (0) | ||
* @param sigHashType is the hash type of the transaction to sign, or default (all) | ||
* @return the signed transaction ready to be broadcast | ||
* @example | ||
btc.signP2SHTransaction( | ||
[ [tx, 1, "52210289b4a3ad52a919abd2bdd6920d8a6879b1e788c38aa76f0440a6f32a9f1996d02103a3393b1439d1693b063482c04bd40142db97bdf139eedd1b51ffb7070a37eac321030b9a409a1e476b0d5d17b804fcdb81cf30f9b99c6f3ae1178206e08bc500639853ae"] ], | ||
["0'/0/0"], | ||
"01905f0100000000001976a91472a5d75c8d2d0565b656a5232703b167d50d5a2b88ac" | ||
).then(result => ...); | ||
*/ | ||
}, { | ||
key: "signP2SHTransaction", | ||
value: function signP2SHTransaction(inputs, associatedKeysets, outputScriptHex) { | ||
var _this6 = this; | ||
var lockTime = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : DEFAULT_LOCKTIME; | ||
var sigHashType = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : SIGHASH_ALL; | ||
// Inputs are provided as arrays of [transaction, output_index, redeem script, optional sequence] | ||
// associatedKeysets are provided as arrays of [path] | ||
var nullScript = Buffer.alloc(0); | ||
var nullPrevout = Buffer.alloc(0); | ||
var defaultVersion = Buffer.alloc(4); | ||
defaultVersion.writeUInt32LE(1, 0); | ||
var trustedInputs = []; | ||
var regularOutputs = []; | ||
var signatures = []; | ||
var firstRun = true; | ||
var resuming = false; | ||
var targetTransaction = { | ||
inputs: [], | ||
version: defaultVersion | ||
}; | ||
var outputScript = Buffer.from(outputScriptHex, "hex"); | ||
return (0, _utils.foreach)(inputs, function (input) { | ||
return (0, _utils.doIf)(!resuming, function () { | ||
return _this6.getTrustedInput(input[1], input[0]).then(function (trustedInput) { | ||
var inputItem = {}; | ||
inputItem.trustedInput = false; | ||
inputItem.value = Buffer.from(trustedInput, "hex").slice(4, 4 + 0x24); | ||
trustedInputs.push(inputItem); | ||
}); | ||
}).then(function () { | ||
var outputs = input[0].outputs; | ||
var index = input[1]; | ||
if (outputs && index <= outputs.length - 1) { | ||
regularOutputs.push(outputs[index]); | ||
} | ||
}); | ||
}).then(function () { | ||
// Pre-build the target transaction | ||
for (var i = 0; i < inputs.length; i++) { | ||
var tmp = Buffer.alloc(4); | ||
var _sequence2 = void 0; | ||
if (inputs[i].length >= 4 && typeof inputs[i][3] === "number") { | ||
_sequence2 = inputs[i][3]; | ||
} else { | ||
_sequence2 = DEFAULT_SEQUENCE; | ||
} | ||
tmp.writeUInt32LE(_sequence2, 0); | ||
targetTransaction.inputs.push({ | ||
prevout: nullPrevout, | ||
script: nullScript, | ||
sequence: tmp | ||
}); | ||
} | ||
}).then(function () { | ||
return (0, _utils.foreach)(inputs, function (input, i) { | ||
targetTransaction.inputs[i].script = inputs[i].length >= 3 && typeof inputs[i][2] === "string" ? Buffer.from(inputs[i][2], "hex") : regularOutputs[i].script; | ||
return _this6.startUntrustedHashTransactionInput(firstRun, targetTransaction, trustedInputs).then(function () { | ||
return _this6.hashOutputFull(outputScript); | ||
}).then(function () { | ||
return _this6.signTransaction(associatedKeysets[i], lockTime, sigHashType).then(function (signature) { | ||
signatures.push(signature.slice(0, signature.length - 1).toString("hex")); | ||
targetTransaction.inputs[i].script = nullScript; | ||
if (firstRun) { | ||
firstRun = false; | ||
} | ||
}); | ||
}); | ||
}); | ||
}).then(function () { | ||
return signatures; | ||
}); | ||
} | ||
}, { | ||
key: "compressPublicKey", | ||
value: function compressPublicKey(publicKey) { | ||
var prefix = (publicKey[64] & 1) !== 0 ? 0x03 : 0x02; | ||
var prefixBuffer = Buffer.alloc(1); | ||
prefixBuffer[0] = prefix; | ||
return Buffer.concat([prefixBuffer, publicKey.slice(1, 1 + 32)]); | ||
} | ||
}, { | ||
key: "createVarint", | ||
value: function createVarint(value) { | ||
if (value < 0xfd) { | ||
var _buffer = Buffer.alloc(1); | ||
_buffer[0] = value; | ||
return _buffer; | ||
} | ||
if (value <= 0xffff) { | ||
var _buffer2 = Buffer.alloc(3); | ||
_buffer2[0] = 0xfd; | ||
_buffer2[1] = value & 0xff; | ||
_buffer2[2] = value >> 8 & 0xff; | ||
return _buffer2; | ||
} | ||
var buffer = Buffer.alloc(5); | ||
buffer[0] = 0xfe; | ||
if (value <= 0xffff) { | ||
const buffer = Buffer.alloc(3); | ||
buffer[0] = 0xfd; | ||
buffer[1] = value & 0xff; | ||
buffer[2] = value >> 8 & 0xff; | ||
buffer[3] = value >> 16 & 0xff; | ||
buffer[4] = value >> 24 & 0xff; | ||
return buffer; | ||
} | ||
const buffer = Buffer.alloc(5); | ||
buffer[0] = 0xfe; | ||
buffer[1] = value & 0xff; | ||
buffer[2] = value >> 8 & 0xff; | ||
buffer[3] = value >> 16 & 0xff; | ||
buffer[4] = value >> 24 & 0xff; | ||
return buffer; | ||
} | ||
/** | ||
* For each UTXO included in your transaction, create a transaction object from the raw serialized version of the transaction used in this UTXO. | ||
* @example | ||
const tx1 = btc.splitTransaction("01000000014ea60aeac5252c14291d428915bd7ccd1bfc4af009f4d4dc57ae597ed0420b71010000008a47304402201f36a12c240dbf9e566bc04321050b1984cd6eaf6caee8f02bb0bfec08e3354b022012ee2aeadcbbfd1e92959f57c15c1c6debb757b798451b104665aa3010569b49014104090b15bde569386734abf2a2b99f9ca6a50656627e77de663ca7325702769986cf26cc9dd7fdea0af432c8e2becc867c932e1b9dd742f2a108997c2252e2bdebffffffff0281b72e00000000001976a91472a5d75c8d2d0565b656a5232703b167d50d5a2b88aca0860100000000001976a9144533f5fb9b4817f713c48f0bfe96b9f50c476c9b88ac00000000"); | ||
*/ | ||
}, { | ||
key: "splitTransaction", | ||
value: function splitTransaction(transactionHex) { | ||
var inputs = []; | ||
var outputs = []; | ||
var offset = 0; | ||
var transaction = Buffer.from(transactionHex, "hex"); | ||
var version = transaction.slice(offset, offset + 4); | ||
/** | ||
* For each UTXO included in your transaction, create a transaction object from the raw serialized version of the transaction used in this UTXO. | ||
* @example | ||
const tx1 = btc.splitTransaction("01000000014ea60aeac5252c14291d428915bd7ccd1bfc4af009f4d4dc57ae597ed0420b71010000008a47304402201f36a12c240dbf9e566bc04321050b1984cd6eaf6caee8f02bb0bfec08e3354b022012ee2aeadcbbfd1e92959f57c15c1c6debb757b798451b104665aa3010569b49014104090b15bde569386734abf2a2b99f9ca6a50656627e77de663ca7325702769986cf26cc9dd7fdea0af432c8e2becc867c932e1b9dd742f2a108997c2252e2bdebffffffff0281b72e00000000001976a91472a5d75c8d2d0565b656a5232703b167d50d5a2b88aca0860100000000001976a9144533f5fb9b4817f713c48f0bfe96b9f50c476c9b88ac00000000"); | ||
*/ | ||
splitTransaction(transactionHex) { | ||
const inputs = []; | ||
const outputs = []; | ||
let offset = 0; | ||
const transaction = Buffer.from(transactionHex, "hex"); | ||
const version = transaction.slice(offset, offset + 4); | ||
offset += 4; | ||
let varint = this.getVarint(transaction, offset); | ||
const numberInputs = varint[0]; | ||
offset += varint[1]; | ||
for (let i = 0; i < numberInputs; i++) { | ||
const prevout = transaction.slice(offset, offset + 36); | ||
offset += 36; | ||
varint = this.getVarint(transaction, offset); | ||
offset += varint[1]; | ||
const script = transaction.slice(offset, offset + varint[0]); | ||
offset += varint[0]; | ||
const sequence = transaction.slice(offset, offset + 4); | ||
offset += 4; | ||
var varint = this.getVarint(transaction, offset); | ||
var numberInputs = varint[0]; | ||
offset += varint[1]; | ||
for (var i = 0; i < numberInputs; i++) { | ||
var _prevout = transaction.slice(offset, offset + 36); | ||
offset += 36; | ||
varint = this.getVarint(transaction, offset); | ||
offset += varint[1]; | ||
var _script = transaction.slice(offset, offset + varint[0]); | ||
offset += varint[0]; | ||
var _sequence3 = transaction.slice(offset, offset + 4); | ||
offset += 4; | ||
inputs.push({ prevout: _prevout, script: _script, sequence: _sequence3 }); | ||
} | ||
inputs.push({ prevout, script, sequence }); | ||
} | ||
varint = this.getVarint(transaction, offset); | ||
const numberOutputs = varint[0]; | ||
offset += varint[1]; | ||
for (let i = 0; i < numberOutputs; i++) { | ||
const amount = transaction.slice(offset, offset + 8); | ||
offset += 8; | ||
varint = this.getVarint(transaction, offset); | ||
var numberOutputs = varint[0]; | ||
offset += varint[1]; | ||
for (var _i = 0; _i < numberOutputs; _i++) { | ||
var _amount = transaction.slice(offset, offset + 8); | ||
offset += 8; | ||
varint = this.getVarint(transaction, offset); | ||
offset += varint[1]; | ||
var _script2 = transaction.slice(offset, offset + varint[0]); | ||
offset += varint[0]; | ||
outputs.push({ amount: _amount, script: _script2 }); | ||
} | ||
var locktime = transaction.slice(offset, offset + 4); | ||
return { version: version, inputs: inputs, outputs: outputs, locktime: locktime }; | ||
const script = transaction.slice(offset, offset + varint[0]); | ||
offset += varint[0]; | ||
outputs.push({ amount, script }); | ||
} | ||
let locktime = transaction.slice(offset, offset + 4); | ||
return { version, inputs, outputs, locktime }; | ||
} | ||
/** | ||
@example | ||
const tx1 = btc.splitTransaction("01000000014ea60aeac5252c14291d428915bd7ccd1bfc4af009f4d4dc57ae597ed0420b71010000008a47304402201f36a12c240dbf9e566bc04321050b1984cd6eaf6caee8f02bb0bfec08e3354b022012ee2aeadcbbfd1e92959f57c15c1c6debb757b798451b104665aa3010569b49014104090b15bde569386734abf2a2b99f9ca6a50656627e77de663ca7325702769986cf26cc9dd7fdea0af432c8e2becc867c932e1b9dd742f2a108997c2252e2bdebffffffff0281b72e00000000001976a91472a5d75c8d2d0565b656a5232703b167d50d5a2b88aca0860100000000001976a9144533f5fb9b4817f713c48f0bfe96b9f50c476c9b88ac00000000"); | ||
const outputScript = btc.serializeTransactionOutputs(tx1).toString('hex'); | ||
*/ | ||
/** | ||
@example | ||
const tx1 = btc.splitTransaction("01000000014ea60aeac5252c14291d428915bd7ccd1bfc4af009f4d4dc57ae597ed0420b71010000008a47304402201f36a12c240dbf9e566bc04321050b1984cd6eaf6caee8f02bb0bfec08e3354b022012ee2aeadcbbfd1e92959f57c15c1c6debb757b798451b104665aa3010569b49014104090b15bde569386734abf2a2b99f9ca6a50656627e77de663ca7325702769986cf26cc9dd7fdea0af432c8e2becc867c932e1b9dd742f2a108997c2252e2bdebffffffff0281b72e00000000001976a91472a5d75c8d2d0565b656a5232703b167d50d5a2b88aca0860100000000001976a9144533f5fb9b4817f713c48f0bfe96b9f50c476c9b88ac00000000"); | ||
const outputScript = btc.serializeTransactionOutputs(tx1).toString('hex'); | ||
*/ | ||
serializeTransactionOutputs({ outputs }) { | ||
let outputBuffer = Buffer.alloc(0); | ||
if (typeof outputs !== "undefined") { | ||
outputBuffer = Buffer.concat([outputBuffer, this.createVarint(outputs.length)]); | ||
outputs.forEach(output => { | ||
outputBuffer = Buffer.concat([outputBuffer, output.amount, this.createVarint(output.script.length), output.script]); | ||
}); | ||
} | ||
return outputBuffer; | ||
} | ||
}, { | ||
key: "serializeTransactionOutputs", | ||
value: function serializeTransactionOutputs(_ref) { | ||
var _this7 = this; | ||
serializeTransaction(transaction) { | ||
let inputBuffer = Buffer.alloc(0); | ||
transaction.inputs.forEach(input => { | ||
inputBuffer = Buffer.concat([inputBuffer, input.prevout, this.createVarint(input.script.length), input.script, input.sequence]); | ||
}); | ||
var outputs = _ref.outputs; | ||
var outputBuffer = Buffer.alloc(0); | ||
if (typeof outputs !== "undefined") { | ||
outputBuffer = Buffer.concat([outputBuffer, this.createVarint(outputs.length)]); | ||
outputs.forEach(function (output) { | ||
outputBuffer = Buffer.concat([outputBuffer, output.amount, _this7.createVarint(output.script.length), output.script]); | ||
}); | ||
} | ||
return outputBuffer; | ||
let outputBuffer = this.serializeTransactionOutputs(transaction); | ||
if (typeof transaction.outputs !== "undefined" && typeof transaction.locktime !== "undefined") { | ||
outputBuffer = Buffer.concat([outputBuffer, transaction.locktime]); | ||
} | ||
}, { | ||
key: "serializeTransaction", | ||
value: function serializeTransaction(transaction) { | ||
var _this8 = this; | ||
var inputBuffer = Buffer.alloc(0); | ||
transaction.inputs.forEach(function (input) { | ||
inputBuffer = Buffer.concat([inputBuffer, input.prevout, _this8.createVarint(input.script.length), input.script, input.sequence]); | ||
}); | ||
return Buffer.concat([transaction.version, this.createVarint(transaction.inputs.length), inputBuffer, outputBuffer]); | ||
} | ||
var outputBuffer = this.serializeTransactionOutputs(transaction); | ||
if (typeof transaction.outputs !== "undefined" && typeof transaction.locktime !== "undefined") { | ||
outputBuffer = Buffer.concat([outputBuffer, transaction.locktime]); | ||
} | ||
return Buffer.concat([transaction.version, this.createVarint(transaction.inputs.length), inputBuffer, outputBuffer]); | ||
displayTransactionDebug(transaction) { | ||
console.log("version " + transaction.version.toString("hex")); | ||
transaction.inputs.forEach((input, i) => { | ||
const prevout = input.prevout.toString("hex"); | ||
const script = input.script.toString("hex"); | ||
const sequence = input.sequence.toString("hex"); | ||
console.log(`input ${i} prevout ${prevout} script ${script} sequence ${sequence}`); | ||
}); | ||
(transaction.outputs || []).forEach((output, i) => { | ||
const amount = output.amount.toString("hex"); | ||
const script = output.script.toString("hex"); | ||
console.log(`output ${i} amount ${amount} script ${script}`); | ||
}); | ||
if (typeof transaction.locktime !== "undefined") { | ||
console.log("locktime " + transaction.locktime.toString("hex")); | ||
} | ||
}, { | ||
key: "displayTransactionDebug", | ||
value: function displayTransactionDebug(transaction) { | ||
console.log("version " + transaction.version.toString("hex")); | ||
transaction.inputs.forEach(function (input, i) { | ||
var prevout = input.prevout.toString("hex"); | ||
var script = input.script.toString("hex"); | ||
var sequence = input.sequence.toString("hex"); | ||
console.log("input " + i + " prevout " + prevout + " script " + script + " sequence " + sequence); | ||
}); | ||
(transaction.outputs || []).forEach(function (output, i) { | ||
var amount = output.amount.toString("hex"); | ||
var script = output.script.toString("hex"); | ||
console.log("output " + i + " amount " + amount + " script " + script); | ||
}); | ||
if (typeof transaction.locktime !== "undefined") { | ||
console.log("locktime " + transaction.locktime.toString("hex")); | ||
} | ||
} | ||
}]); | ||
return Btc; | ||
}(); | ||
} | ||
} | ||
exports.default = Btc; | ||
//# sourceMappingURL=Btc.js.map |
@@ -13,5 +13,4 @@ "use strict"; | ||
function defer() { | ||
var resolve = void 0, | ||
reject = void 0; | ||
var promise = new Promise(function (success, failure) { | ||
let resolve, reject; | ||
let promise = new Promise(function (success, failure) { | ||
resolve = success; | ||
@@ -21,3 +20,3 @@ reject = failure; | ||
if (!resolve || !reject) throw "defer() error"; // this never happens and is just to make flow happy | ||
return { promise: promise, resolve: resolve, reject: reject }; | ||
return { promise, resolve, reject }; | ||
} | ||
@@ -45,6 +44,6 @@ | ||
function splitPath(path) { | ||
var result = []; | ||
var components = path.split("/"); | ||
components.forEach(function (element) { | ||
var number = parseInt(element, 10); | ||
let result = []; | ||
let components = path.split("/"); | ||
components.forEach(element => { | ||
let number = parseInt(element, 10); | ||
if (isNaN(number)) { | ||
@@ -64,7 +63,3 @@ return; // FIXME shouldn't it throws instead? | ||
function eachSeries(arr, fun) { | ||
return arr.reduce(function (p, e) { | ||
return p.then(function () { | ||
return fun(e); | ||
}); | ||
}, Promise.resolve()); | ||
return arr.reduce((p, e) => p.then(() => fun(e)), Promise.resolve()); | ||
} | ||
@@ -81,9 +76,7 @@ | ||
} | ||
return Promise.resolve().then(function () { | ||
return iterate(0, arr, []); | ||
}); | ||
return Promise.resolve().then(() => iterate(0, arr, [])); | ||
} | ||
function doIf(condition, callback) { | ||
return Promise.resolve().then(function () { | ||
return Promise.resolve().then(() => { | ||
if (condition) { | ||
@@ -100,3 +93,3 @@ return callback(); | ||
} else { | ||
return callback().then(function (res) { | ||
return callback().then(res => { | ||
result.push(res); | ||
@@ -103,0 +96,0 @@ return iterate(result); |
{ | ||
"name": "@ledgerhq/hw-app-btc", | ||
"version": "1.1.1-beta.068e2a14", | ||
"version": "1.1.2-beta.068e2a14", | ||
"description": "Ledger Hardware Wallet Bitcoin Application API", | ||
@@ -28,3 +28,3 @@ "keywords": [ | ||
"dependencies": { | ||
"@ledgerhq/hw-transport": "^1.1.1-beta.068e2a14" | ||
"@ledgerhq/hw-transport": "^1.1.2-beta.068e2a14" | ||
}, | ||
@@ -31,0 +31,0 @@ "devDependencies": { |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
167520
1864
+ Added@ledgerhq/hw-transport@1.1.2-beta.068e2a14(transitive)
- Removed@ledgerhq/hw-transport@1.1.1-beta.068e2a14(transitive)