@@ -3,13 +3,13 @@ var DashJoin = ('object' === typeof module && exports) || {};
3
3
'use strict' ;
4
4
5
5
let DashP2P = window . DashP2P || require ( 'dashp2p' ) ;
6
+ let DashTx = window . DashTx || require ( 'dashtx' ) ;
6
7
7
8
const DV_LITTLE_ENDIAN = true ;
8
9
9
10
const DENOM_LOWEST = 100001 ;
10
11
const PREDENOM_MIN = DENOM_LOWEST + 193 ;
11
12
const COLLATERAL = 10000 ; // DENOM_LOWEST / 10
12
- const PAYLOAD_SIZE_MAX = 4 * 1024 * 1024 ;
13
13
14
14
let STANDARD_DENOMINATIONS_MAP = {
15
15
// 0.00100001
@@ -24,11 +24,25 @@ var DashJoin = ('object' === typeof module && exports) || {};
24
24
0b00000001 : 1000010000 ,
25
25
} ;
26
26
27
+ // Note: "mask" may be a misnomer. The spec seems to be more of an ID,
28
+ // but the implementation makes it look more like a mask...
29
+ let STANDARD_DENOMINATION_MASKS = {
30
+ // 0.00100001
31
+ 100001 : 0b00010000 ,
32
+ // 0.01000010
33
+ 1000010 : 0b00001000 ,
34
+ // 0.10000100
35
+ 10000100 : 0b00000100 ,
36
+ // 1.00001000
37
+ 100001000 : 0b00000010 ,
38
+ // 10.00010000
39
+ 1000010000 : 0b00000001 ,
40
+ } ;
41
+
27
42
// https://github.com/dashpay/dash/blob/v19.x/src/coinjoin/coinjoin.h#L39
28
43
// const COINJOIN_ENTRY_MAX_SIZE = 9; // real
29
44
// const COINJOIN_ENTRY_MAX_SIZE = 2; // just for testing right now
30
45
31
- DashJoin . PAYLOAD_SIZE_MAX = PAYLOAD_SIZE_MAX ;
32
46
DashJoin . DENOM_LOWEST = DENOM_LOWEST ;
33
47
DashJoin . COLLATERAL = COLLATERAL ;
34
48
DashJoin . PREDENOM_MIN = PREDENOM_MIN ;
@@ -59,56 +73,177 @@ var DashJoin = ('object' === typeof module && exports) || {};
59
73
return 0 ;
60
74
} ;
61
75
76
+ Sizes . DSQ = 142 ;
77
+ Sizes . SENDDSQ = 1 ; // 1-byte bool
78
+ Sizes . DENOM = 4 ; // 32-bit uint
79
+ Sizes . PROTX = 32 ;
80
+ Sizes . TIME = 8 ; // 64-bit uint
81
+ Sizes . READY = 1 ; // 1-byte bool
82
+ Sizes . SIG = 97 ;
83
+
84
+ // Sizes.DSSU = 16;
85
+ // Sizes.SESSION_ID = 4;
86
+
62
87
/**
63
88
* Turns on or off DSQ messages (necessary for CoinJoin, but off by default)
64
89
* @param {Object } opts
65
90
* @param {NetworkName } opts.network - "mainnet", "testnet", etc
66
91
* @param {Uint8Array? } [opts.message]
67
92
* @param {Boolean? } [opts.send]
68
93
*/
69
- Packers . senddsq = function ( {
94
+ Packers . senddsq = function ( { network = 'mainnet' , message, send = true } ) {
95
+ const command = 'senddsq' ;
96
+ let [ bytes , payload ] = DashP2P . packers . _alloc ( message , Sizes . SENDDSQ ) ;
97
+
98
+ let sendByte = [ 0x01 ] ;
99
+ if ( ! send ) {
100
+ sendByte = [ 0x00 ] ;
101
+ }
102
+ payload . set ( sendByte , 0 ) ;
103
+
104
+ void DashP2P . packers . message ( { network, command, bytes } ) ;
105
+ return bytes ;
106
+ } ;
107
+
108
+ /**
109
+ * Request to be allowed to join a CoinJoin pool. This may join an existing
110
+ * session - such as one already broadcast by a dsq - or may create a new one.
111
+ * @param {Object } opts
112
+ * @param {NetworkName } opts.network - "mainnet", "testnet", etc
113
+ * @param {Uint8Array? } [opts.message]
114
+ * @param {Uint32 } opts.denomination
115
+ * @param {Uint8Array } opts.collateralTx
116
+ */
117
+ Packers . dsa = function ( {
70
118
network = 'mainnet' ,
71
- message = null ,
72
- send = true ,
119
+ message,
120
+ denomination,
121
+ collateralTx,
73
122
} ) {
74
- const command = 'senddsq' ;
75
- const SENDDSQ_SIZE = 1 ; // 1-byte bool
123
+ const command = 'dsa' ;
124
+ let dsaSize = Sizes . DENOM + collateralTx . length ;
125
+ let [ bytes , payload ] = DashP2P . packers . _alloc ( message , dsaSize ) ;
126
+
127
+ //@ts -ignore - numbers can be used as map keys
128
+ let denomMask = STANDARD_DENOMINATION_MASKS [ denomination ] ;
129
+ if ( ! denomMask ) {
130
+ throw new Error (
131
+ `contact your local Dash representative to vote for denominations of '${ denomination } '` ,
132
+ ) ;
133
+ }
134
+
135
+ let dv = new DataView ( payload . buffer ) ;
136
+ let offset = 0 ;
137
+
138
+ dv . setUint32 ( offset , denomMask , DV_LITTLE_ENDIAN ) ;
139
+ offset += Sizes . DENOM ;
140
+
141
+ payload . set ( collateralTx , offset ) ;
142
+
143
+ void DashP2P . packers . message ( { network, command, bytes } ) ;
144
+ return bytes ;
145
+ } ;
146
+
147
+ /**
148
+ * @param {Object } opts
149
+ * @param {NetworkName } opts.network - "mainnet", "testnet", etc
150
+ * @param {Uint8Array? } [opts.message]
151
+ * @param {Array<import('dashtx').TxInput> } opts.inputs
152
+ * @param {Array<import('dashtx').TxOutput> } opts.outputs
153
+ * @param {Uint8Array } opts.collateralTx
154
+ */
155
+ Packers . dsi = function ( {
156
+ network = 'mainnet' ,
157
+ message,
158
+ inputs,
159
+ collateralTx,
160
+ outputs,
161
+ } ) {
162
+ const command = 'dsi' ;
163
+
164
+ let neutered = [ ] ;
165
+ for ( let input of inputs ) {
166
+ let _input = {
167
+ txId : input . txId || input . txid ,
168
+ txid : input . txid || input . txId ,
169
+ outputIndex : input . outputIndex ,
170
+ } ;
171
+ neutered . push ( _input ) ;
172
+ }
173
+
174
+ let inputsHex = DashTx . serializeInputs ( inputs ) ;
175
+ let inputHex = inputsHex . join ( '' ) ;
176
+ let outputsHex = DashTx . serializeOutputs ( outputs ) ;
177
+ let outputHex = outputsHex . join ( '' ) ;
76
178
77
- if ( ! message ) {
78
- let dsqSize = DashP2P . sizes . HEADER_SIZE + SENDDSQ_SIZE ;
79
- message = new Uint8Array ( dsqSize ) ;
179
+ let dsiSize = collateralTx . length ;
180
+ dsiSize += inputHex . length / 2 ;
181
+ dsiSize += outputHex . length / 2 ;
182
+
183
+ let [ bytes , payload ] = DashP2P . packers . _alloc ( message , dsiSize ) ;
184
+
185
+ let offset = 0 ;
186
+ {
187
+ let j = 0 ;
188
+ for ( let i = 0 ; i < inputHex . length ; i += 2 ) {
189
+ let end = i + 2 ;
190
+ let hex = inputHex . slice ( i , end ) ;
191
+ payload [ j ] = parseInt ( hex , 16 ) ;
192
+ j += 1 ;
193
+ }
194
+ offset += inputHex . length / 2 ;
80
195
}
81
196
82
- let payload = message . subarray ( DashP2P . sizes . HEADER_SIZE ) ;
83
- if ( send ) {
84
- payload . set ( [ 0x01 ] , 0 ) ;
85
- } else {
86
- payload . set ( [ 0x00 ] , 0 ) ;
197
+ payload . set ( collateralTx , offset ) ;
198
+ offset += collateralTx . length ;
199
+
200
+ {
201
+ let outputsPayload = payload . subarray ( offset ) ;
202
+ let j = 0 ;
203
+ for ( let i = 0 ; i < outputHex . length ; i += 2 ) {
204
+ let end = i + 2 ;
205
+ let hex = outputHex . slice ( i , end ) ;
206
+ outputsPayload [ j ] = parseInt ( hex , 16 ) ;
207
+ j += 1 ;
208
+ }
209
+ offset += outputHex . length / 2 ;
87
210
}
88
211
89
- void DashP2P . packers . message ( { network, command, bytes : message } ) ;
90
- return message ;
91
- // return { message, payload };
212
+ void DashP2P . packers . message ( { network, command, bytes } ) ;
213
+ return bytes ;
92
214
} ;
93
215
94
- Sizes . DSQ_SIZE = 142 ;
95
- // DSQ stuff??
96
- Sizes . DENOM = 4 ;
97
- Sizes . PROTX = 32 ;
98
- Sizes . TIME = 8 ;
99
- Sizes . READY = 1 ;
100
- Sizes . SIG = 97 ;
101
- //
216
+ /**
217
+ * @param {Object } opts
218
+ * @param {Uint8Array? } [opts.message]
219
+ * @param {NetworkName } opts.network - "mainnet", "testnet", etc
220
+ * @param {Array<import('dashtx').CoreUtxo> } [opts.inputs]
221
+ */
222
+ Packers . dss = function ( { network = 'mainnet' , message, inputs } ) {
223
+ const command = 'dss' ;
224
+
225
+ if ( ! inputs ?. length ) {
226
+ // TODO make better
227
+ throw new Error ( 'you must provide some inputs' ) ;
228
+ }
229
+
230
+ let txInputsHex = DashTx . serializeInputs ( inputs ) ;
231
+ let txInputHex = txInputsHex . join ( '' ) ;
102
232
103
- // Sizes.DSSU_SIZE = 16;
104
- // Sizes.SESSION_ID_SIZE = 4;
233
+ let dssSize = txInputHex . length / 2 ;
234
+ let [ bytes , payload ] = DashP2P . packers . _alloc ( message , dssSize ) ;
235
+ void DashP2P . utils . hexToPayload ( txInputHex , payload ) ;
236
+
237
+ void DashP2P . packers . message ( { network, command, bytes } ) ;
238
+ return bytes ;
239
+ } ;
105
240
106
241
/**
107
242
* @param {Uint8Array } bytes
108
243
*/
109
244
Parsers . dsq = function ( bytes ) {
110
- if ( bytes . length !== Sizes . DSQ_SIZE ) {
111
- let msg = `developer error: 'dsq' must be ${ Sizes . DSQ_SIZE } bytes, but received ${ bytes . length } ` ;
245
+ if ( bytes . length !== Sizes . DSQ ) {
246
+ let msg = `developer error: 'dsq' must be ${ Sizes . DSQ } bytes, but received ${ bytes . length } ` ;
112
247
throw new Error ( msg ) ;
113
248
}
114
249
let dv = new DataView ( bytes . buffer ) ;
@@ -160,40 +295,8 @@ var DashJoin = ('object' === typeof module && exports) || {};
160
295
return dsqMessage ;
161
296
} ;
162
297
163
- Utils . hexToBytes = function ( hex ) {
164
- let bufLen = hex . length / 2 ;
165
- let u8 = new Uint8Array ( bufLen ) ;
166
-
167
- let i = 0 ;
168
- let index = 0 ;
169
- let lastIndex = hex . length - 2 ;
170
- for ( ; ; ) {
171
- if ( i > lastIndex ) {
172
- break ;
173
- }
174
-
175
- let h = hex . slice ( i , i + 2 ) ;
176
- let b = parseInt ( h , 16 ) ;
177
- u8 [ index ] = b ;
178
-
179
- i += 2 ;
180
- index += 1 ;
181
- }
182
-
183
- return u8 ;
184
- } ;
185
-
186
- Utils . bytesToHex = function ( u8 ) {
187
- /** @type {Array<String> } */
188
- let hex = [ ] ;
189
-
190
- u8 . forEach ( function ( b ) {
191
- let h = b . toString ( 16 ) . padStart ( 2 , '0' ) ;
192
- hex . push ( h ) ;
193
- } ) ;
194
-
195
- return hex . join ( '' ) ;
196
- } ;
298
+ // Utils.hexToBytes = DashTx.utils.hexToBytes;
299
+ // Utils.bytesToHex = DashTx.utils.bytesToHex;
197
300
198
301
Utils . _evonodeMapToList = function ( evonodesMap ) {
199
302
console . log ( '[debug] get evonode list...' ) ;
0 commit comments