UNPKG

js-oip

Version:

A Javascript Library to fully interact with the Open Index Protocol

420 lines (326 loc) 12.1 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = exports.FLODATA_MAX_LEN = void 0; require("core-js/modules/web.dom.iterable"); var _bitcoinjsLib = require("bitcoinjs-lib"); var _varuintBitcoin = _interopRequireDefault(require("varuint-bitcoin")); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } // The maximum floData that fits in one transaction const FLODATA_MAX_LEN = 1040; exports.FLODATA_MAX_LEN = FLODATA_MAX_LEN; const EMPTY_SCRIPT = Buffer.alloc(0); const ZERO = Buffer.from('0000000000000000000000000000000000000000000000000000000000000000', 'hex'); const ONE = Buffer.from('0000000000000000000000000000000000000000000000000000000000000001', 'hex'); const VALUE_UINT64_MAX = Buffer.from('ffffffffffffffff', 'hex'); const BLANK_OUTPUT = { script: EMPTY_SCRIPT, valueBuffer: VALUE_UINT64_MAX }; const EMPTY_WITNESS = []; class FLOTransaction extends _bitcoinjsLib.Transaction { constructor() { super(); this.version = 2; this.floData = Buffer.from([]); } setFloData(floData, dataType) { let tmpFloData = Buffer.from(floData, dataType); // Check if the floData is too long :) if (tmpFloData.length > FLODATA_MAX_LEN) { throw new Error(`Attempted to set too much floData! Maximum is ${FLODATA_MAX_LEN}, you tried ${tmpFloData.length}!`); } this.floData = tmpFloData; } getFloData() { return this.floData; } clone() { let newTx = new FLOTransaction(); newTx.version = this.version; newTx.locktime = this.locktime; newTx.ins = this.ins.map(function (txIn) { return { hash: txIn.hash, index: txIn.index, script: txIn.script, sequence: txIn.sequence, witness: txIn.witness }; }); newTx.outs = this.outs.map(function (txOut) { return { script: txOut.script, value: txOut.value }; }); newTx.floData = Buffer.from(this.floData); return newTx; } /** * Calculate the Byte Length of the Tranasction * @param {Boolean} __allowWitness - Should Witness be used in the generation * @return {Integer} Returns the length of the Transaction */ __byteLength(__allowWitness, options) { let byteLength = _bitcoinjsLib.Transaction.prototype.__byteLength.call(this, __allowWitness); if (options && options.excludeFloData || this.version < 2) { return byteLength; } let floDataVarInt = _varuintBitcoin.default.encode(this.floData.length); return byteLength + floDataVarInt.length + this.floData.length; } static fromBuffer(buffer, _NO_STRICT) { let offset = 0; function readUInt64LE(buffer, offset) { const a = buffer.readUInt32LE(offset); let b = buffer.readUInt32LE(offset + 4); b *= 0x100000000; return b + a; } function readSlice(n) { offset += n; return buffer.slice(offset - n, offset); } function readUInt32() { const i = buffer.readUInt32LE(offset); offset += 4; return i; } function readInt32() { const i = buffer.readInt32LE(offset); offset += 4; return i; } function readUInt64() { const i = readUInt64LE(buffer, offset); offset += 8; return i; } function readVarInt() { const vi = _varuintBitcoin.default.decode(buffer, offset); offset += _varuintBitcoin.default.decode.bytes; return vi; } function readVarSlice() { return readSlice(readVarInt()); } function readVector() { const count = readVarInt(); const vector = []; for (let i = 0; i < count; i++) { vector.push(readVarSlice()); } return vector; } const tx = new FLOTransaction(); tx.version = readInt32(); const marker = buffer.readUInt8(offset); const flag = buffer.readUInt8(offset + 1); let hasWitnesses = false; if (marker === _bitcoinjsLib.Transaction.ADVANCED_TRANSACTION_MARKER && flag === _bitcoinjsLib.Transaction.ADVANCED_TRANSACTION_FLAG) { offset += 2; hasWitnesses = true; } const vinLen = readVarInt(); for (let i = 0; i < vinLen; ++i) { tx.ins.push({ hash: readSlice(32), index: readUInt32(), script: readVarSlice(), sequence: readUInt32(), witness: EMPTY_WITNESS }); } const voutLen = readVarInt(); for (let i = 0; i < voutLen; ++i) { tx.outs.push({ value: readUInt64(), script: readVarSlice() }); } if (hasWitnesses) { for (let i = 0; i < vinLen; ++i) { tx.ins[i].witness = readVector(); } // was this pointless? if (!tx.hasWitnesses()) { throw new Error('Transaction has superfluous witness data'); } } tx.locktime = readUInt32(); if (tx.version >= 2) { tx.floData = readVarSlice(); } if (_NO_STRICT) { return tx; } if (offset !== buffer.length) { throw new Error('Transaction has unexpected data'); } return tx; } static fromHex(hex) { return FLOTransaction.fromBuffer(Buffer.from(hex, 'hex')); } /** * Serialize the Transaction to a Buffer * @param {Buffer} buffer - The Buffer to use while building * @param {Integer} initialOffset - The initial offset of the buffer * @param {Boolean} __allowWitness -Should Witness be used in the serialization * @return {Buffer} Returns the TX as a Buffer */ __toBuffer(buffer, initialOffset, __allowWitness, options) { if (!buffer) { buffer = Buffer.allocUnsafe(this.__byteLength(__allowWitness, options)); } _bitcoinjsLib.Transaction.prototype.__toBuffer.call(this, buffer, initialOffset, __allowWitness); // Included for testing transaction signatures, and legacy signature hashing if (options && options.excludeFloData || this.version < 2) { return buffer; } // Calculate where we left off let offset = this.__byteLength(__allowWitness, options) - (this.floData.length + _varuintBitcoin.default.encode(this.floData.length).length); // Add the varint for the floData length _varuintBitcoin.default.encode(this.floData.length, buffer, offset); offset += _varuintBitcoin.default.encode.bytes; // Append the floData itself this.floData.copy(buffer, offset); // Return the built transaciton Buffer return buffer; } hashForSignature(inIndex, prevOutScript, hashType, options) { // https://github.com/bitcoin/bitcoin/blob/master/src/test/sighash_tests.cpp#L29 if (inIndex >= this.ins.length) { return ONE; } let ourScript = _bitcoinjsLib.script.compile(_bitcoinjsLib.script.decompile(prevOutScript).filter(x => { return x !== _bitcoinjsLib.script.OPS.OP_CODESEPARATOR; })); let txTmp = this.clone(); // SIGHASH_NONE: ignore all outputs? (wildcard payee) if ((hashType & 0x1f) === _bitcoinjsLib.Transaction.SIGHASH_NONE) { txTmp.outs = []; // ignore sequence numbers (except at inIndex) txTmp.ins.forEach((input, i) => { if (i === inIndex) { return; } input.sequence = 0; }); // SIGHASH_SINGLE: ignore all outputs, except at the same index? } else if ((hashType & 0x1f) === _bitcoinjsLib.Transaction.SIGHASH_SINGLE) { // https://github.com/bitcoin/bitcoin/blob/master/src/test/sighash_tests.cpp#L60 if (inIndex >= this.outs.length) { return ONE; } // truncate outputs after txTmp.outs.length = inIndex + 1; // "blank" outputs before for (let i = 0; i < inIndex; i++) { txTmp.outs[i] = BLANK_OUTPUT; } // ignore sequence numbers (except at inIndex) txTmp.ins.forEach((input, y) => { if (y === inIndex) { return; } input.sequence = 0; }); } // SIGHASH_ANYONECANPAY: ignore inputs entirely? if (hashType & _bitcoinjsLib.Transaction.SIGHASH_ANYONECANPAY) { txTmp.ins = [txTmp.ins[inIndex]]; txTmp.ins[0].script = ourScript; // SIGHASH_ALL: only ignore input scripts } else { // "blank" others input scripts txTmp.ins.forEach(input => { input.script = EMPTY_SCRIPT; }); txTmp.ins[inIndex].script = ourScript; } // serialize and hash let buffer = Buffer.alloc(txTmp.__byteLength(false, options) + 4); buffer.writeInt32LE(hashType, buffer.length - 4); txTmp.__toBuffer(buffer, 0, false, options); return _bitcoinjsLib.crypto.hash256(buffer); } hashForWitnessV0(inIndex, prevOutScript, value, hashType, options) { function writeUInt64LE(buffer, value, offset) { buffer.writeInt32LE(value & -1, offset); buffer.writeUInt32LE(Math.floor(value / 0x100000000), offset + 4); return offset + 8; } let tbuffer = Buffer.from([]); let toffset = 0; function writeSlice(slice) { toffset += slice.copy(tbuffer, toffset); } function writeUInt32(i) { toffset = tbuffer.writeUInt32LE(i, toffset); } function writeUInt64(i) { toffset = writeUInt64LE(tbuffer, i, toffset); } function writeVarInt(i) { _varuintBitcoin.default.encode(i, tbuffer, toffset); toffset += _varuintBitcoin.default.encode.bytes; } function writeVarSlice(slice) { writeVarInt(slice.length); writeSlice(slice); } function varSliceSize(someScript) { const length = someScript.length; return _varuintBitcoin.default.encodingLength(length) + length; } let hashOutputs = ZERO; let hashPrevouts = ZERO; let hashSequence = ZERO; if (!(hashType & _bitcoinjsLib.Transaction.SIGHASH_ANYONECANPAY)) { tbuffer = Buffer.allocUnsafe(36 * this.ins.length); toffset = 0; this.ins.forEach(txIn => { writeSlice(txIn.hash); writeUInt32(txIn.index); }); hashPrevouts = _bitcoinjsLib.crypto.hash256(tbuffer); } if (!(hashType & _bitcoinjsLib.Transaction.SIGHASH_ANYONECANPAY) && (hashType & 0x1f) !== _bitcoinjsLib.Transaction.SIGHASH_SINGLE && (hashType & 0x1f) !== _bitcoinjsLib.Transaction.SIGHASH_NONE) { tbuffer = Buffer.allocUnsafe(4 * this.ins.length); toffset = 0; this.ins.forEach(txIn => { writeUInt32(txIn.sequence); }); hashSequence = _bitcoinjsLib.crypto.hash256(tbuffer); } if ((hashType & 0x1f) !== _bitcoinjsLib.Transaction.SIGHASH_SINGLE && (hashType & 0x1f) !== _bitcoinjsLib.Transaction.SIGHASH_NONE) { const txOutsSize = this.outs.reduce((sum, output) => { return sum + 8 + varSliceSize(output.script); }, 0); tbuffer = Buffer.allocUnsafe(txOutsSize); toffset = 0; this.outs.forEach(out => { writeUInt64(out.value); writeVarSlice(out.script); }); hashOutputs = _bitcoinjsLib.crypto.hash256(tbuffer); } else if ((hashType & 0x1f) === _bitcoinjsLib.Transaction.SIGHASH_SINGLE && inIndex < this.outs.length) { const output = this.outs[inIndex]; tbuffer = Buffer.allocUnsafe(8 + varSliceSize(output.script)); toffset = 0; writeUInt64(output.value); writeVarSlice(output.script); hashOutputs = _bitcoinjsLib.crypto.hash256(tbuffer); } tbuffer = Buffer.alloc(156 + varSliceSize(prevOutScript) + varSliceSize(this.floData)); if (options && options.excludeFloData || this.version < 2) { tbuffer = Buffer.alloc(156 + varSliceSize(prevOutScript)); } toffset = 0; const input = this.ins[inIndex]; writeUInt32(this.version); writeSlice(hashPrevouts); writeSlice(hashSequence); writeSlice(input.hash); writeUInt32(input.index); writeVarSlice(prevOutScript); writeUInt64(value); writeUInt32(input.sequence); writeSlice(hashOutputs); writeUInt32(this.locktime); if (!(options && options.excludeFloData) && this.version >= 2) { writeVarSlice(this.floData); } writeUInt32(hashType); return _bitcoinjsLib.crypto.hash256(tbuffer); } } var _default = FLOTransaction; exports.default = _default;