Skip to content

Commit d35a1e4

Browse files
committed
feat(platform): split 'version' from 'type', add 'trailer'
1 parent ee98a2b commit d35a1e4

File tree

2 files changed

+126
-28
lines changed

2 files changed

+126
-28
lines changed

dashtx.js

Lines changed: 86 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ var DashTx = ("object" === typeof module && exports) || {};
8888
let TxUtils = {};
8989

9090
const CURRENT_VERSION = 3;
91+
const TYPE_VERSION = 0;
9192
const SATOSHIS = 100000000;
9293

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

144145
Tx._HEADER_ONLY_SIZE =
145-
4 + // version
146+
2 + // version
147+
2 + // type
146148
4; // locktime
147149

148150
Tx.HEADER_SIZE =
149-
4 + // version
151+
2 + // version
152+
2 + // type
150153
1 + // input count
151154
1 + // output count
152155
4; // locktime
@@ -303,11 +306,13 @@ var DashTx = ("object" === typeof module && exports) || {};
303306

304307
/** @type {TxInfoSigned} */
305308
let txInfoSigned = {
309+
version: txInfo.version || CURRENT_VERSION,
310+
type: txInfo.type || TYPE_VERSION,
306311
/** @type {Array<TxInputSigned>} */
307312
inputs: [],
308313
outputs: txInfo.outputs,
309-
version: txInfo.version || CURRENT_VERSION,
310314
locktime: txInfo.locktime || 0x00,
315+
extraPayload: "",
311316
transaction: "",
312317
};
313318

@@ -687,9 +692,10 @@ var DashTx = ("object" === typeof module && exports) || {};
687692
* or the largest available coins until that total is met.
688693
*/
689694
//@ts-ignore - TODO update typedefs
690-
Tx.createLegacyTx = function (coins, outputs, changeOutput) {
695+
Tx.createLegacyTx = function (coins, outputs, changeOutput, extraPayload) {
691696
// TODO bump to 4 for DIP: enforce tx hygiene
692697
let version = CURRENT_VERSION;
698+
let type = TYPE_VERSION;
693699

694700
coins = coins.slice(0);
695701
outputs = outputs.slice(0);
@@ -773,10 +779,12 @@ var DashTx = ("object" === typeof module && exports) || {};
773779

774780
let txInfo = {
775781
version,
782+
type,
776783
inputs,
777784
outputs,
778785
changeIndex,
779786
locktime,
787+
extraPayload,
780788
};
781789
// let change = txInfo.outputs[txInfo.changeIndex];
782790

@@ -1120,9 +1128,11 @@ var DashTx = ("object" === typeof module && exports) || {};
11201128
Tx.serialize = function (
11211129
{
11221130
version = CURRENT_VERSION,
1131+
type = TYPE_VERSION,
11231132
inputs,
1124-
locktime = 0x0,
11251133
outputs,
1134+
locktime = 0x0,
1135+
extraPayload = "",
11261136
/* maxFee = 10000, */
11271137
_debug = false,
11281138
},
@@ -1135,15 +1145,23 @@ var DashTx = ("object" === typeof module && exports) || {};
11351145

11361146
/** @type Array<String> */
11371147
let tx = [];
1138-
let v = TxUtils._toUint32LE(version);
1148+
let v = TxUtils._toUint16LE(version);
11391149
tx.push(v);
1150+
let t = TxUtils._toUint16LE(type);
1151+
tx.push(t);
11401152

11411153
void Tx.serializeInputs(inputs, { _tx: tx, _sep: _sep });
11421154
void Tx.serializeOutputs(outputs, { _tx: tx, _sep: _sep });
11431155

11441156
let locktimeHex = TxUtils._toUint32LE(locktime);
11451157
tx.push(locktimeHex);
11461158

1159+
if (extraPayload) {
1160+
let nTrailer = Tx.utils.toVarInt(extraPayload.length / 2);
1161+
tx.push(nTrailer);
1162+
tx.push(extraPayload);
1163+
}
1164+
11471165
let txHex = tx.join(_sep);
11481166

11491167
if (sigHashType) {
@@ -1459,10 +1477,15 @@ var DashTx = ("object" === typeof module && exports) || {};
14591477

14601478
tx.offset = 0;
14611479

1462-
tx.versionHex = hex.substr(tx.offset, 8);
1480+
tx.versionHex = hex.substr(tx.offset, 4);
14631481
let versionHexRev = Tx.utils.reverseHex(tx.versionHex);
14641482
tx.version = parseInt(versionHexRev, 16);
1465-
tx.offset += 8;
1483+
tx.offset += 4;
1484+
1485+
tx.typeHex = hex.substr(tx.offset, 4);
1486+
let typeHexRev = Tx.utils.reverseHex(tx.typeHex);
1487+
tx.type = parseInt(typeHexRev, 16);
1488+
tx.offset += 4;
14661489

14671490
let [numInputs, numInputsSize] = TxUtils._parseVarIntHex(hex, tx.offset);
14681491
tx.offset += numInputsSize;
@@ -1631,10 +1654,26 @@ var DashTx = ("object" === typeof module && exports) || {};
16311654
tx.locktime = parseInt(locktimeHexRev, 16);
16321655
tx.offset += 8;
16331656

1657+
tx.extraPayloadSizeHex = "";
1658+
/** @type {Uint8?} */
1659+
tx.extraPayloadSize = null;
1660+
tx.extraPayloadHex = "";
1661+
if (tx.type > 0) {
1662+
// TODO varint
1663+
tx.extraPayloadSizeHex = hex.substr(tx.offset, 2);
1664+
tx.extraPayloadSize = parseInt(tx.extraPayloadSizeHex, 16);
1665+
tx.offset += 2;
1666+
1667+
tx.extraPayloadHex = hex.substr(tx.offset, 2 * tx.extraPayloadSize);
1668+
tx.offset += 2 * tx.extraPayloadSize;
1669+
}
1670+
16341671
tx.sigHashTypeHex = hex.substr(tx.offset);
16351672
if (tx.sigHashTypeHex) {
1636-
tx.sigHashType = parseInt(tx.sigHashTypeHex.slice(0, 2));
1637-
hex = hex.slice(0, -8);
1673+
let firstLEIntByte = tx.sigHashTypeHex.slice(0, 2);
1674+
tx.sigHashType = parseInt(firstLEIntByte);
1675+
let fullLEIntHexSize = 8;
1676+
hex = hex.slice(0, -fullLEIntHexSize); // but the size is actually 4 bytes
16381677
}
16391678

16401679
tx.size = hex.length / 2;
@@ -1822,6 +1861,18 @@ var DashTx = ("object" === typeof module && exports) || {};
18221861
return hexLE;
18231862
};
18241863

1864+
/**
1865+
* Just assumes that all target CPUs are Little-Endian,
1866+
* which is true in practice, and much simpler.
1867+
* @param {BigInt|Number} n - 16-bit positive int to encode
1868+
*/
1869+
TxUtils._toUint16LE = function (n) {
1870+
let hexLE = TxUtils._toUint32LE(n);
1871+
// ex: 03000800 => 0300
1872+
hexLE = hexLE.slice(0, 4);
1873+
return hexLE;
1874+
};
1875+
18251876
/**
18261877
* This can handle Big-Endian CPUs, which don't exist,
18271878
* and looks too complicated.
@@ -1916,6 +1967,7 @@ if ("object" === typeof module) {
19161967

19171968
/** @typedef {Number} Float64 */
19181969
/** @typedef {Number} Uint8 */
1970+
/** @typedef {Number} Uint16 */
19191971
/** @typedef {Number} Uint32 */
19201972
/** @typedef {Number} Uint53 */
19211973
/** @typedef {String} Hex */
@@ -1952,11 +2004,13 @@ if ("object" === typeof module) {
19522004

19532005
/**
19542006
* @typedef TxInfo
2007+
* @prop {Uint16} [version]
2008+
* @prop {Uint16} [type]
19552009
* @prop {Array<TxInputForSig>} inputs
1956-
* @prop {Uint32} [locktime] - 0 by default
19572010
* @prop {Array<TxOutput>} outputs
1958-
* @prop {Uint32} [version]
1959-
* @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
19602014
* @prop {Boolean} [_debug] - bespoke debug output
19612015
*/
19622016

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

19712025
/**
19722026
* @typedef TxInfoSigned
2027+
* @prop {Uint16} version
2028+
* @prop {Uint16} type
19732029
* @prop {Array<TxInputSigned>} inputs
1974-
* @prop {Uint32} locktime - 0 by default
19752030
* @prop {Array<TxOutput>} outputs
1976-
* @prop {Uint32} version
2031+
* @prop {Uint32} locktime - 0 by default
2032+
* @prop {Hex} extraPayload - extra payload bytes
19772033
* @prop {String} transaction - signed transaction hex
19782034
* @prop {Boolean} [_debug] - bespoke debug output
19792035
*/
@@ -2126,9 +2182,12 @@ if ("object" === typeof module) {
21262182
/**
21272183
* @callback TxCreateRaw
21282184
* @param {Object} opts
2185+
* @param {Uint16} [opts.version]
2186+
* @param {Uint16} [opts.type]
21292187
* @param {Array<TxInputRaw>} opts.inputs
21302188
* @param {Array<TxOutput>} opts.outputs
2131-
* @param {Uint32} [opts.version]
2189+
* @param {Uint32} [opts.locktime]
2190+
* @param {Hex} [opts.extraPayload]
21322191
* @param {Boolean} [opts._debug] - bespoke debug output
21332192
*/
21342193

@@ -2141,9 +2200,12 @@ if ("object" === typeof module) {
21412200
/**
21422201
* @callback TxCreateSigned
21432202
* @param {Object} opts
2203+
* @param {Uint16} [opts.version]
2204+
* @param {Uint16} [opts.type]
21442205
* @param {Array<TxInputSigned>} opts.inputs
21452206
* @param {Array<TxOutput>} opts.outputs
2146-
* @param {Uint32} [opts.version]
2207+
* @param {Uint32} [opts.locktime]
2208+
* @param {Hex} [opts.extraPayload]
21472209
* @param {Boolean} [opts._debug] - bespoke debug output
21482210
* xparam {String} [opts.sigHashType] - hex, typically 01 (ALL)
21492211
*/
@@ -2251,21 +2313,25 @@ if ("object" === typeof module) {
22512313
/**
22522314
* @callback TxSerialize
22532315
* @param {Object} txInfo
2316+
* @param {Uint16} [txInfo.version]
2317+
* @param {Uint16} [txInfo.type]
22542318
* @param {Array<TxInputRaw|TxInputForSig|TxInputSigned>} txInfo.inputs
2255-
* @param {Uint32} [txInfo.locktime]
22562319
* @param {Array<TxOutput>} txInfo.outputs
2257-
* @param {Uint32} [txInfo.version]
2320+
* @param {Uint32} [txInfo.locktime]
2321+
* @param {Hex?} [txInfo.extraPayload] - extra payload
22582322
* @param {Boolean} [txInfo._debug] - bespoke debug output
22592323
* @param {Uint32} [sigHashType]
22602324
*/
22612325

22622326
/**
22632327
* @callback TxSerializeForSig
22642328
* @param {Object} txInfo
2329+
* @param {Uint16} [txInfo.version]
2330+
* @param {Uint16} [txInfo.type]
22652331
* @param {Array<TxInputRaw|TxInputForSig>} txInfo.inputs
22662332
* @param {Uint32} [txInfo.locktime]
22672333
* @param {Array<TxOutput>} txInfo.outputs
2268-
* @param {Uint32} [txInfo.version]
2334+
* @param {Hex?} [txInfo.extraPayload] - extra payload
22692335
* @param {Boolean} [txInfo._debug] - bespoke debug output
22702336
* @param {Uint32} sigHashType
22712337
*/

tests/parser.js

Lines changed: 40 additions & 8 deletions
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)