|
1014 | 1014 | return f.toFixed(d);
|
1015 | 1015 | }
|
1016 | 1016 |
|
1017 |
| - async function connectToPeer(height, evonode) { |
| 1017 | + async function connectToPeer(evonode, height) { |
1018 | 1018 | if (App.peers[evonode.host]) {
|
1019 | 1019 | return App.peers[evonode.host];
|
1020 | 1020 | }
|
|
1040 | 1040 |
|
1041 | 1041 | let senddsqBytes = DashJoin.packers.senddsq({ network: network });
|
1042 | 1042 | console.log('[REQ: %csenddsq%c]', 'color: $55daba', 'color: inherit');
|
1043 |
| - wsc.send(senddsqBytes); |
| 1043 | + p2p.send(senddsqBytes); |
1044 | 1044 |
|
1045 | 1045 | void p2p.createSubscriber(['dsq'], async function (evstream) {
|
1046 | 1046 | let msg = await evstream.once('dsq');
|
|
1069 | 1069 | }
|
1070 | 1070 | wsc.addEventListener('error', cleanup);
|
1071 | 1071 |
|
1072 |
| - App.peers[evonode.host] = { p2p }; |
| 1072 | + App.peers[evonode.host] = p2p; |
1073 | 1073 | return App.peers[evonode.host];
|
1074 | 1074 | }
|
1075 | 1075 |
|
| 1076 | + // 0. 'dsq' broadcast puts a node in the local in-memory pool |
| 1077 | + // 1. 'dsa' requests to be allowed to join a session |
| 1078 | + // 2. 'dssu' accepts |
| 1079 | + // + 'dsq' marks ready (in either order) |
| 1080 | + // 3. 'dsi' signals desired coins in and out |
| 1081 | + // 4. 'dsf' accepts specific coins in and out |
| 1082 | + // 5. 'dss' sends signed inputs paired to trusted outputs |
| 1083 | + // 6. 'dssu' updates status |
| 1084 | + // + 'dsc' confirms the tx will broadcast soon |
| 1085 | + async function createCoinJoinSession( |
| 1086 | + evonode, // { host } |
| 1087 | + inputs, // [{address, txid, pubKeyHash, ...getPrivateKeyInfo }] |
| 1088 | + outputs, // [{ pubKeyHash, satoshis }] |
| 1089 | + collateralTxes, // (for dsa and dsi) any 2 txes having fees >=0.00010000 more than necessary |
| 1090 | + ) { |
| 1091 | + let p2p = App.peers[evonode.host]; |
| 1092 | + if (!p2p) { |
| 1093 | + throw new Error(`'${evonode.host}' is not connected`); |
| 1094 | + } |
| 1095 | + |
| 1096 | + let denomination = inputs[0].satoshis; |
| 1097 | + for (let input of inputs) { |
| 1098 | + if (input.satoshis !== denomination) { |
| 1099 | + let msg = `utxo.satoshis (${input.satoshis}) must match requested denomination ${denomination}`; |
| 1100 | + throw new Error(msg); |
| 1101 | + } |
| 1102 | + } |
| 1103 | + for (let output of outputs) { |
| 1104 | + if (!output.sateshis) { |
| 1105 | + output.satoshis = denomination; |
| 1106 | + continue; |
| 1107 | + } |
| 1108 | + if (output.satoshis !== denomination) { |
| 1109 | + let msg = `output.satoshis (${output.satoshis}) must match requested denomination ${denomination}`; |
| 1110 | + throw new Error(msg); |
| 1111 | + } |
| 1112 | + } |
| 1113 | + |
| 1114 | + // todo: pick a smaller size that matches the dss |
| 1115 | + let message = new Uint8Array(DashP2P.PAYLOAD_SIZE_MAX); |
| 1116 | + let evstream = p2p.createSubscriber(['dssu', 'dsq', 'dsf', 'dsc']); |
| 1117 | + |
| 1118 | + { |
| 1119 | + let collateralTx = collateralTxes.shift(); |
| 1120 | + let dsaBytes = DashJoin.packers.dsa({ |
| 1121 | + network, |
| 1122 | + message, |
| 1123 | + denomination, |
| 1124 | + collateralTx, |
| 1125 | + }); |
| 1126 | + p2p.send(dsaBytes); |
| 1127 | + for (;;) { |
| 1128 | + let msg = await evstream.once(); |
| 1129 | + |
| 1130 | + if (msg.command === 'dsq') { |
| 1131 | + let dsq = DashJoin.parsers.dsq(msg.payload); |
| 1132 | + if (dsq.denomination !== denomination) { |
| 1133 | + continue; |
| 1134 | + } |
| 1135 | + if (!dsq.ready) { |
| 1136 | + continue; |
| 1137 | + } |
| 1138 | + break; |
| 1139 | + } |
| 1140 | + |
| 1141 | + if (msg.command === 'dssu') { |
| 1142 | + let dssu = DashJoin.parsers.dssu(msg.payload); |
| 1143 | + if (dssu.state === 'ERROR') { |
| 1144 | + evstream.close(); |
| 1145 | + throw new Error(); |
| 1146 | + } |
| 1147 | + } |
| 1148 | + } |
| 1149 | + } |
| 1150 | + |
| 1151 | + let dsfTxRequest; |
| 1152 | + { |
| 1153 | + let collateralTx = collateralTxes.shift(); |
| 1154 | + let dsiBytes = DashJoin.packers.dsi({ |
| 1155 | + network, |
| 1156 | + message, |
| 1157 | + inputs, |
| 1158 | + collateralTx, |
| 1159 | + outputs, |
| 1160 | + }); |
| 1161 | + p2p.send(dsiBytes); |
| 1162 | + let msg = await evstream.once('dsf'); |
| 1163 | + let dsf = DashJoin.parsers.dsf(msg.payload); |
| 1164 | + |
| 1165 | + dsfTxRequest = DashTx.parseUnknown(dsf.transaction_unsigned); |
| 1166 | + makeSelectedInputsSignable(dsfTxRequest, inputs); |
| 1167 | + let txSigned = await dashTx.hashAndSignAll(dsfTxRequest); |
| 1168 | + |
| 1169 | + let signedInputs = []; |
| 1170 | + for (let input of txSigned.inputs) { |
| 1171 | + if (!input?.signature) { |
| 1172 | + continue; |
| 1173 | + } |
| 1174 | + signedInputs.push(input); |
| 1175 | + } |
| 1176 | + assertSelectedOutputs(dsfTxRequest, outputs, inputs.length); |
| 1177 | + |
| 1178 | + let dssBytes = DashJoin.packers.dss({ |
| 1179 | + network: network, |
| 1180 | + message: message, |
| 1181 | + inputs: signedInputs, |
| 1182 | + }); |
| 1183 | + p2p.send(dssBytes); |
| 1184 | + } |
| 1185 | + |
| 1186 | + return dsfTxRequest; |
| 1187 | + } |
| 1188 | + |
| 1189 | + function makeSelectedInputsSignable(txRequest, inputs) { |
| 1190 | + // let selected = []; |
| 1191 | + |
| 1192 | + for (let input of inputs) { |
| 1193 | + if (!input.publicKey) { |
| 1194 | + let msg = `coin '${input.address}:${input.txid}:${input.outputIndex}' is missing 'input.publicKey'`; |
| 1195 | + throw new Error(msg); |
| 1196 | + } |
| 1197 | + for (let sighashInput of txRequest.inputs) { |
| 1198 | + if (sighashInput.txid !== input.txid) { |
| 1199 | + continue; |
| 1200 | + } |
| 1201 | + if (sighashInput.outputIndex !== input.outputIndex) { |
| 1202 | + continue; |
| 1203 | + } |
| 1204 | + |
| 1205 | + let sigHashType = DashTx.SIGHASH_ALL | DashTx.SIGHASH_ANYONECANPAY; //jshint ignore:line |
| 1206 | + |
| 1207 | + sighashInput.index = input.index; |
| 1208 | + sighashInput.address = input.address; |
| 1209 | + sighashInput.satoshis = input.satoshis; |
| 1210 | + sighashInput.pubKeyHash = input.pubKeyHash; |
| 1211 | + // sighashInput.script = input.script; |
| 1212 | + sighashInput.publicKey = input.publicKey; |
| 1213 | + sighashInput.sigHashType = sigHashType; |
| 1214 | + // sighashInputs.push({ |
| 1215 | + // txId: input.txId || input.txid, |
| 1216 | + // txid: input.txid || input.txId, |
| 1217 | + // outputIndex: input.outputIndex, |
| 1218 | + // pubKeyHash: input.pubKeyHash, |
| 1219 | + // sigHashType: input.sigHashType, |
| 1220 | + // }); |
| 1221 | + |
| 1222 | + // selected.push(input); |
| 1223 | + break; |
| 1224 | + } |
| 1225 | + } |
| 1226 | + |
| 1227 | + // return selected; |
| 1228 | + } |
| 1229 | + |
| 1230 | + function assertSelectedOutputs(txRequest, outputs, count) { |
| 1231 | + let _count = 0; |
| 1232 | + for (let output of outputs) { |
| 1233 | + for (let sighashOutput of txRequest.outputs) { |
| 1234 | + if (sighashOutput.pubKeyHash !== output.pubKeyHash) { |
| 1235 | + continue; |
| 1236 | + } |
| 1237 | + if (sighashOutput.satoshis !== output.satoshis) { |
| 1238 | + continue; |
| 1239 | + } |
| 1240 | + |
| 1241 | + _count += 1; |
| 1242 | + } |
| 1243 | + } |
| 1244 | + |
| 1245 | + if (count !== _count) { |
| 1246 | + let msg = `expected ${count} matching outputs but found found ${_count}`; |
| 1247 | + throw new Error(msg); |
| 1248 | + } |
| 1249 | + } |
| 1250 | + |
1076 | 1251 | async function main() {
|
1077 | 1252 | if (network === `testnet`) {
|
1078 | 1253 | let $testnets = $$('[data-network=testnet]');
|
|
1104 | 1279 | };
|
1105 | 1280 | App.peers = {};
|
1106 | 1281 |
|
1107 |
| - void (await connectToPeer(App._chaininfo.blocks, App._evonode)); |
| 1282 | + void (await connectToPeer(App._evonode, App._chaininfo.blocks)); |
| 1283 | + |
| 1284 | + // collateral, denominated |
| 1285 | + // let collateralTxInfo = await getCollateralTx(); |
| 1286 | + // // let keys = await getPrivateKeys(collateralTxInfo.inputs); |
| 1287 | + // // let txInfoSigned = await dashTx.hashAndSignAll(collateralTxInfo, keys); |
| 1288 | + // let txInfoSigned = await dashTx.hashAndSignAll(collateralTxInfo); |
| 1289 | + // let collateralTx = DashTx.utils.hexToBytes(txInfoSigned.transaction); |
| 1290 | + |
| 1291 | + // await createCoinJoinSession( |
| 1292 | + // App.evonode, |
| 1293 | + // // inputs, // [{address, txid, pubKeyHash, ...getPrivateKeyInfo }] |
| 1294 | + // // outputs, // [{ pubKeyHash, satoshis }] |
| 1295 | + // // dsaCollateralTx, // any tx with fee >= 0.00010000 |
| 1296 | + // // dsiCollateralTx, // any tx with fee >= 0.00010000 |
| 1297 | + // ); |
1108 | 1298 | }
|
1109 | 1299 |
|
1110 | 1300 | main().catch(function (err) {
|
|
0 commit comments