Skip to content

Commit f39eaf1

Browse files
committed
feat(platform): support special txes (version, type, extra payload)
f: extraPayload
1 parent 0f033bf commit f39eaf1

File tree

2 files changed

+127
-30
lines changed

2 files changed

+127
-30
lines changed

dashtx.js

+87-22
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ var DashTx = ("object" === typeof module && exports) || {};
8989
let TxUtils = {};
9090

9191
const CURRENT_VERSION = 3;
92+
const TYPE_VERSION = 0;
9293
const SATOSHIS = 100000000;
9394

9495
const MAX_U8 = Math.pow(2, 8) - 1;
@@ -143,11 +144,13 @@ var DashTx = ("object" === typeof module && exports) || {};
143144
Tx.LEGACY_DUST = 2000;
144145

145146
Tx._HEADER_ONLY_SIZE =
146-
4 + // version
147+
2 + // version
148+
2 + // type
147149
4; // locktime
148150

149151
Tx.HEADER_SIZE =
150-
4 + // version
152+
2 + // version
153+
2 + // type
151154
1 + // input count
152155
1 + // output count
153156
4; // locktime
@@ -307,11 +310,13 @@ var DashTx = ("object" === typeof module && exports) || {};
307310

308311
/** @type {TxInfoSigned} */
309312
let txInfoSigned = {
313+
version: txInfo.version || CURRENT_VERSION,
314+
type: txInfo.type || TYPE_VERSION,
310315
/** @type {Array<TxInputSigned>} */
311316
inputs: [],
312317
outputs: txInfo.outputs,
313-
version: txInfo.version || CURRENT_VERSION,
314318
locktime: txInfo.locktime || 0x00,
319+
extraPayload: txInfo.extraPayload || "",
315320
transaction: "",
316321
};
317322

@@ -694,9 +699,10 @@ var DashTx = ("object" === typeof module && exports) || {};
694699
* or the largest available coins until that total is met.
695700
*/
696701
//@ts-ignore - TODO update typedefs
697-
Tx.createLegacyTx = function (coins, outputs, changeOutput) {
702+
Tx.createLegacyTx = function (coins, outputs, changeOutput, extraPayload) {
698703
// TODO bump to 4 for DIP: enforce tx hygiene
699704
let version = CURRENT_VERSION;
705+
let type = TYPE_VERSION;
700706

701707
coins = coins.slice(0);
702708
outputs = outputs.slice(0);
@@ -780,10 +786,12 @@ var DashTx = ("object" === typeof module && exports) || {};
780786

781787
let txInfo = {
782788
version,
789+
type,
783790
inputs,
784791
outputs,
785792
changeIndex,
786793
locktime,
794+
extraPayload,
787795
};
788796
// let change = txInfo.outputs[txInfo.changeIndex];
789797

@@ -1127,9 +1135,11 @@ var DashTx = ("object" === typeof module && exports) || {};
11271135
Tx.serialize = function (
11281136
{
11291137
version = CURRENT_VERSION,
1138+
type = TYPE_VERSION,
11301139
inputs,
1131-
locktime = 0x0,
11321140
outputs,
1141+
locktime = 0x0,
1142+
extraPayload = "",
11331143
/* maxFee = 10000, */
11341144
_debug = false,
11351145
},
@@ -1142,22 +1152,29 @@ var DashTx = ("object" === typeof module && exports) || {};
11421152

11431153
/** @type Array<String> */
11441154
let tx = [];
1145-
let v = TxUtils._toUint32LE(version);
1155+
let v = TxUtils._toUint16LE(version);
11461156
tx.push(v);
1157+
let t = TxUtils._toUint16LE(type);
1158+
tx.push(t);
11471159

11481160
void Tx.serializeInputs(inputs, { _tx: tx, _sep: _sep });
11491161
void Tx.serializeOutputs(outputs, { _tx: tx, _sep: _sep });
11501162

11511163
let locktimeHex = TxUtils._toUint32LE(locktime);
11521164
tx.push(locktimeHex);
11531165

1154-
let txHex = tx.join(_sep);
1166+
if (extraPayload) {
1167+
let nExtraPayload = Tx.utils.toVarInt(extraPayload.length / 2);
1168+
tx.push(nExtraPayload);
1169+
tx.push(extraPayload);
1170+
}
11551171

11561172
if (sigHashType) {
11571173
let sigHashTypeHex = TxUtils._toUint32LE(sigHashType);
1158-
txHex = `${txHex}${sigHashTypeHex}`;
1174+
tx.push(sigHashTypeHex);
11591175
}
11601176

1177+
let txHex = tx.join(_sep);
11611178
return txHex;
11621179
};
11631180
//@ts-ignore - same function, but typed and documented separately for clarity
@@ -1466,10 +1483,15 @@ var DashTx = ("object" === typeof module && exports) || {};
14661483

14671484
tx.offset = 0;
14681485

1469-
tx.versionHex = hex.substr(tx.offset, 8);
1486+
tx.versionHex = hex.substr(tx.offset, 4);
14701487
let versionHexRev = Tx.utils.reverseHex(tx.versionHex);
14711488
tx.version = parseInt(versionHexRev, 16);
1472-
tx.offset += 8;
1489+
tx.offset += 4;
1490+
1491+
tx.typeHex = hex.substr(tx.offset, 4);
1492+
let typeHexRev = Tx.utils.reverseHex(tx.typeHex);
1493+
tx.type = parseInt(typeHexRev, 16);
1494+
tx.offset += 4;
14731495

14741496
let [numInputs, numInputsSize] = TxUtils._parseVarIntHex(hex, tx.offset);
14751497
tx.offset += numInputsSize;
@@ -1638,10 +1660,26 @@ var DashTx = ("object" === typeof module && exports) || {};
16381660
tx.locktime = parseInt(locktimeHexRev, 16);
16391661
tx.offset += 8;
16401662

1663+
tx.extraPayloadSizeHex = "";
1664+
/** @type {Uint8?} */
1665+
tx.extraPayloadSize = null;
1666+
tx.extraPayloadHex = "";
1667+
if (tx.type > 0) {
1668+
// TODO varint
1669+
tx.extraPayloadSizeHex = hex.substr(tx.offset, 2);
1670+
tx.extraPayloadSize = parseInt(tx.extraPayloadSizeHex, 16);
1671+
tx.offset += 2;
1672+
1673+
tx.extraPayloadHex = hex.substr(tx.offset, 2 * tx.extraPayloadSize);
1674+
tx.offset += 2 * tx.extraPayloadSize;
1675+
}
1676+
16411677
tx.sigHashTypeHex = hex.substr(tx.offset);
16421678
if (tx.sigHashTypeHex) {
1643-
tx.sigHashType = parseInt(tx.sigHashTypeHex.slice(0, 2));
1644-
hex = hex.slice(0, -8);
1679+
let firstLEIntByte = tx.sigHashTypeHex.slice(0, 2);
1680+
tx.sigHashType = parseInt(firstLEIntByte);
1681+
let fullLEIntHexSize = 8;
1682+
hex = hex.slice(0, -fullLEIntHexSize); // but the size is actually 4 bytes
16451683
}
16461684

16471685
tx.size = hex.length / 2;
@@ -1806,6 +1844,18 @@ var DashTx = ("object" === typeof module && exports) || {};
18061844
throw err;
18071845
};
18081846

1847+
/**
1848+
* Just assumes that all target CPUs are Little-Endian,
1849+
* which is true in practice, and much simpler.
1850+
* @param {BigInt|Number} n - 16-bit positive int to encode
1851+
*/
1852+
TxUtils._toUint16LE = function (n) {
1853+
let hexLE = TxUtils._toUint32LE(n);
1854+
// ex: 03000800 => 0300
1855+
hexLE = hexLE.slice(0, 4);
1856+
return hexLE;
1857+
};
1858+
18091859
/**
18101860
* Just assumes that all target CPUs are Little-Endian,
18111861
* which is true in practice, and much simpler.
@@ -1916,6 +1966,7 @@ if ("object" === typeof module) {
19161966

19171967
/** @typedef {Number} Float64 */
19181968
/** @typedef {Number} Uint8 */
1969+
/** @typedef {Number} Uint16 */
19191970
/** @typedef {Number} Uint32 */
19201971
/** @typedef {Number} Uint53 */
19211972
/** @typedef {String} Hex */
@@ -1953,11 +2004,13 @@ if ("object" === typeof module) {
19532004

19542005
/**
19552006
* @typedef TxInfo
2007+
* @prop {Uint16} [version]
2008+
* @prop {Uint16} [type]
19562009
* @prop {Array<TxInputForSig>} inputs
1957-
* @prop {Uint32} [locktime] - 0 by default
19582010
* @prop {Array<TxOutput>} outputs
1959-
* @prop {Uint32} [version]
1960-
* @prop {String} [transaction] - signed transaction hex
2011+
* @prop {Uint32} [locktime] - 0 by default
2012+
* @prop {Hex} [extraPayload] - extra payload bytes
2013+
* @prop {Hex} [transaction] - signed transaction hex
19612014
* @prop {Boolean} [_debug] - bespoke debug output
19622015
*/
19632016

@@ -1971,10 +2024,12 @@ if ("object" === typeof module) {
19712024

19722025
/**
19732026
* @typedef TxInfoSigned
2027+
* @prop {Uint16} version
2028+
* @prop {Uint16} type
19742029
* @prop {Array<TxInputSigned>} inputs
1975-
* @prop {Uint32} locktime - 0 by default
19762030
* @prop {Array<TxOutput>} outputs
1977-
* @prop {Uint32} version
2031+
* @prop {Uint32} locktime - 0 by default
2032+
* @prop {Hex} extraPayload - extra payload bytes
19782033
* @prop {String} transaction - signed transaction hex
19792034
* @prop {Boolean} [_debug] - bespoke debug output
19802035
*/
@@ -2127,9 +2182,12 @@ if ("object" === typeof module) {
21272182
/**
21282183
* @callback TxCreateRaw
21292184
* @param {Object} opts
2185+
* @param {Uint16} [opts.version]
2186+
* @param {Uint16} [opts.type]
21302187
* @param {Array<TxInputRaw>} opts.inputs
21312188
* @param {Array<TxOutput>} opts.outputs
2132-
* @param {Uint32} [opts.version]
2189+
* @param {Uint32} [opts.locktime]
2190+
* @param {Hex} [opts.extraPayload]
21332191
* @param {Boolean} [opts._debug] - bespoke debug output
21342192
*/
21352193

@@ -2142,9 +2200,12 @@ if ("object" === typeof module) {
21422200
/**
21432201
* @callback TxCreateSigned
21442202
* @param {Object} opts
2203+
* @param {Uint16} [opts.version]
2204+
* @param {Uint16} [opts.type]
21452205
* @param {Array<TxInputSigned>} opts.inputs
21462206
* @param {Array<TxOutput>} opts.outputs
2147-
* @param {Uint32} [opts.version]
2207+
* @param {Uint32} [opts.locktime]
2208+
* @param {Hex} [opts.extraPayload]
21482209
* @param {Boolean} [opts._debug] - bespoke debug output
21492210
* xparam {String} [opts.sigHashType] - hex, typically 01 (ALL)
21502211
*/
@@ -2252,21 +2313,25 @@ if ("object" === typeof module) {
22522313
/**
22532314
* @callback TxSerialize
22542315
* @param {Object} txInfo
2316+
* @param {Uint16} [txInfo.version]
2317+
* @param {Uint16} [txInfo.type]
22552318
* @param {Array<TxInputRaw|TxInputForSig|TxInputSigned>} txInfo.inputs
2256-
* @param {Uint32} [txInfo.locktime]
22572319
* @param {Array<TxOutput>} txInfo.outputs
2258-
* @param {Uint32} [txInfo.version]
2320+
* @param {Uint32} [txInfo.locktime]
2321+
* @param {Hex?} [txInfo.extraPayload] - extra payload
22592322
* @param {Boolean} [txInfo._debug] - bespoke debug output
22602323
* @param {Uint32} [sigHashType]
22612324
*/
22622325

22632326
/**
22642327
* @callback TxSerializeForSig
22652328
* @param {Object} txInfo
2329+
* @param {Uint16} [txInfo.version]
2330+
* @param {Uint16} [txInfo.type]
22662331
* @param {Array<TxInputRaw|TxInputForSig>} txInfo.inputs
22672332
* @param {Uint32} [txInfo.locktime]
22682333
* @param {Array<TxOutput>} txInfo.outputs
2269-
* @param {Uint32} [txInfo.version]
2334+
* @param {Hex?} [txInfo.extraPayload] - extra payload
22702335
* @param {Boolean} [txInfo._debug] - bespoke debug output
22712336
* @param {Uint32} sigHashType
22722337
*/

tests/parser.js

+40-8
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,18 @@ async function test() {
1111
let filename = "dsf.tx-request.hex";
1212
let txInfo = await parseHexFile(filename);
1313

14-
if (txInfo.versionHex !== "02000000") {
15-
throw new Error(`${filename} versionHex is not 02000000`);
14+
if (txInfo.versionHex !== "0200") {
15+
throw new Error(`${filename} versionHex is not 0200`);
1616
}
1717
if (txInfo.version !== 2) {
1818
throw new Error(`${filename} version is not 2`);
1919
}
20+
if (txInfo.typeHex !== "0000") {
21+
throw new Error(`${filename} typeHex is not 0000`);
22+
}
23+
if (txInfo.type !== 0) {
24+
throw new Error(`${filename} type is not 0`);
25+
}
2026

2127
if (txInfo.inputs.length !== 18) {
2228
throw new Error(
@@ -36,6 +42,10 @@ async function test() {
3642
throw new Error(`${filename} locktime is not 0`);
3743
}
3844

45+
if (txInfo.extraPayloadHex !== "") {
46+
throw new Error(`${filename} extraPayloadHex is not '' (empty string)`);
47+
}
48+
3949
if (txInfo.sigHashTypeHex) {
4050
throw new Error(`${filename} should not have sigHashTypeHex`);
4151
}
@@ -48,12 +58,18 @@ async function test() {
4858
let filename = "dss.tx-response.hex";
4959
let txInfo = await parseHexFile(filename);
5060

51-
if (txInfo.versionHex !== "02000000") {
52-
throw new Error(`${filename} versionHex is not 02000000`);
61+
if (txInfo.versionHex !== "0200") {
62+
throw new Error(`${filename} versionHex is not 0200`);
5363
}
5464
if (txInfo.version !== 2) {
5565
throw new Error(`${filename} version is not 2`);
5666
}
67+
if (txInfo.typeHex !== "0000") {
68+
throw new Error(`${filename} typeHex is not 0000`);
69+
}
70+
if (txInfo.type !== 0) {
71+
throw new Error(`${filename} type is not 0`);
72+
}
5773

5874
if (txInfo.inputs.length !== 2) {
5975
throw new Error(`${filename} # inputs is not 2: ${txInfo.inputs.length}`);
@@ -72,6 +88,10 @@ async function test() {
7288
`${filename} locktime is not 2004296226: ${txInfo.locktime}`,
7389
);
7490
}
91+
92+
if (txInfo.extraPayloadHex !== "") {
93+
throw new Error(`${filename} extraPayload is not '' (empty string)`);
94+
}
7595
}
7696

7797
{
@@ -107,12 +127,18 @@ async function test() {
107127
let filename = "sighash-all.tx.hex";
108128
let txInfo = await parseHexFile(filename);
109129

110-
if (txInfo.versionHex !== "02000000") {
111-
throw new Error(`${filename} versionHex is not 02000000`);
130+
if (txInfo.versionHex !== "0200") {
131+
throw new Error(`${filename} versionHex is not 0200`);
112132
}
113133
if (txInfo.version !== 2) {
114134
throw new Error(`${filename} version is not 2`);
115135
}
136+
if (txInfo.typeHex !== "0000") {
137+
throw new Error(`${filename} typeHex is not 0000`);
138+
}
139+
if (txInfo.type !== 0) {
140+
throw new Error(`${filename} type is not 0`);
141+
}
116142

117143
let scriptIndex = -1;
118144
let nulls = 0;
@@ -158,12 +184,18 @@ async function test() {
158184
let filename = "sighash-any.tx.hex";
159185
let txInfo = await parseHexFile(filename);
160186

161-
if (txInfo.versionHex !== "02000000") {
162-
throw new Error(`${filename} versionHex is not 02000000`);
187+
if (txInfo.versionHex !== "0200") {
188+
throw new Error(`${filename} versionHex is not 0200`);
163189
}
164190
if (txInfo.version !== 2) {
165191
throw new Error(`${filename} version is not 2`);
166192
}
193+
if (txInfo.typeHex !== "0000") {
194+
throw new Error(`${filename} typeHex is not 0000`);
195+
}
196+
if (txInfo.type !== 0) {
197+
throw new Error(`${filename} type is not 0`);
198+
}
167199

168200
let scriptIndex = -1;
169201
let nulls = 0;

0 commit comments

Comments
 (0)