-
Notifications
You must be signed in to change notification settings - Fork 1
/
index.js
435 lines (361 loc) · 15 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
/**
* @file Gnosis Security2G MVP
* @author Thomas Haller <[email protected]>
* @license GPLv3
* @version 0.1
*/
const Pcsc = require('pcsclite');
const Web3 = require('web3');
const express = require('express');
const app = express();
const path = require('path');
const Security2GoCard = require('./web3-s2g.js');
const gnosisSafe = require('./gnosisSafe.js');
const pcsc = Pcsc();
const cardSigner = new Security2GoCard.MinervaCardSigner();
const web3Options = {
transactionConfirmationBlocks: 1,
defaultGasPrice: '100000000000',
transactionSigner: cardSigner,
};
// const web3_address = 'ws://ws.tau1.artis.network';
const localWebserverListeningPort = 3000;
const web3Address = 'https://rpc.tau1.artis.network';
// const web3Address = 'http://127.0.0.1:9545/';
// const web3Address = 'https://rpc.sigma1.artis.network';
const web3 = new Web3(web3Address, null, web3Options);
cardSigner.web3 = web3;
let currentData = {};
// it is possible to copy the "state data" out from the UI
// and use it here for further testing.
// this are only examples -
// since you need to use the same smart cards in order to continue from this states.
// const debugState_high5_multiSigSetup = {
// currentGnosisSafeAddress: '0x79c9e5C29e22fB665Dee3F0e726ccEBA3eF07ead',
// state: 'multiSigSetup',
// collectedSafeAddresses: ['0x4abb023f60997bfbeeadb38e0caabd8b623bf2d3',
// '0x1b629f37aed1576c2e979aff68d2983f0ab13479',
// '0x0774fe042bc23d01599b5a92b97b3125a4cb20ee',
// '0xb222330ca92307d639b0bed948fe4f3577fc500b',
// '0xe856a0cad6368c541cf11d9d9c8554b156fa40fd'],
// lastError: '',
// multisigPayoutAddress: '',
// multisigCollected: {},
// multisigTransactionHash: '',
// };
// states:
// deploy -> deploying -> deployed (R) -> collectingMultiSigAddresses -> setupSafe -> settingUpSafe -> SafeReady (R)
// -> SafeFundingSetup -> SafeFunding -> SafeFunded (R) -> MultiSigSetup -> MultiSigCollecting -> MultiSigSending
// -> MultisigSuccess.
// (R) => Remove card to progress to next state
// deploy: waits for a card that is used to sign off the deploy transaction.
const STATE_DEPLOY = 'deploy';
// deploying: a new safe is about to be deployed.
const STATE_DEPLOYING = 'deploying';
// a new safe just got deployed, user need to continue with setup the addresses for multisig.
const STATE_DEPLOYED = 'deployed';
const STATE_COLLECTINGMULTISIGADDRESSES = 'collectingMultiSigAddresses';
// system collects now the card addresses for setting up a safe.
const STATE_SETUPSAFE = 'setupSafe';
const STATE_SETTINGUPSAFE = 'settingUpSafe';
const STATE_SAFEREADY = 'safeReady';
const STATE_SAFEFUNDINGSETUP = 'safeFundingSetup';
const STATE_SAFEFUNDING = 'safeFunding';
const STATE_SAFEFUNDED = 'safeFunded';
const STATE_MULTISIGSETUP = 'multiSigSetup';
const STATE_MULTISIGSETUPFINISHED = 'multiSigSetupFinished';
const STATE_MULTISIGCOLLECTING = 'multiSigCollecting';
const STATE_MULTISIGCOLLECTED = 'multiSigCollected';
const STATE_MULTISIGSENDING = 'multiSigSending';
const STATE_MULTISIGSUCCESS = 'multisigSuccess';
currentData.currentGnosisSafeAddress = '';
currentData.state = STATE_DEPLOY;
// array of '0xabc..890' string with the addresses that should get added to the safe.
currentData.collectedSafeAddresses = [];
currentData.lastError = '';
currentData.multisigPayoutAddress = '';
currentData.multisigCollected = {}; // map with the safeAddress as index and a signature as value.
// Transaction object that is used to send out funds to multisigPayoutAddress
currentData.multisigTransaction = undefined;
currentData.multisigTransactionHash = '';
// address of the first account that deployed the gnosis safe.
// this field is just been used as a gimmick so we only have "Laying Cards" as User Input Interface.
// without that field we have at leased to click one time a button to signal "continue and setup safe".
currentData.initialDeployerAddress = '';
// currentData = debugState;
function getNumberOfRequiredSignatures(numberOfSignatures) {
if (numberOfSignatures > 2) {
return numberOfSignatures - 1;
}
return numberOfSignatures;
}
/* eslint-disable camelcase */
async function setupSafe(card) {
console.log('setting up safe');
currentData.state = STATE_SETTINGUPSAFE;
const setupSafeResult = await gnosisSafe.setupSafe(
web3, currentData.currentGnosisSafeAddress, currentData.collectedSafeAddresses,
getNumberOfRequiredSignatures(currentData.collectedSafeAddresses.length), card,
);
if (setupSafeResult) {
console.log('setting up safe done!');
currentData.state = STATE_SAFEREADY;
}
}
async function state_collectingMultisigAddresses(card) {
if (!currentData.currentGnosisSafeAddress) {
console.error('INVALID STATE: no SafeAddress found in the state Setup');
return;
}
const cardAddress = await card.getAddress(1);
// we interpete laying the "initial deployer address" card as
// "all multisig cards became added, now continue with gnosis safe initialisation"
if (cardAddress === currentData.initialDeployerAddress) {
currentData.state = STATE_SETUPSAFE;
setupSafe(card);
return;
}
if (currentData.collectedSafeAddresses.indexOf(cardAddress) === -1) {
console.log(`Setup Safe: new multi sig enabled address: ${cardAddress}`);
currentData.collectedSafeAddresses.push(cardAddress);
} else {
console.log(`Card allready known:${cardAddress}`);
}
}
async function state_deploy(card) {
currentData.state = STATE_DEPLOYING;
const initialDeployerAddress = await card.getAddress(1);
const deployedSafe = await gnosisSafe.deployNewSafe(web3, card);
console.log('deployedSafe=>');
console.log(deployedSafe.address);
currentData.state = STATE_DEPLOYED;
currentData.currentGnosisSafeAddress = deployedSafe.address;
currentData.collectedSafeAddresses = [];
currentData.initialDeployerAddress = initialDeployerAddress;
return deployedSafe;
}
async function state_safeFundingSetup(card) {
// Prepare the raw transaction information
const tx = {
gasPrice: web3.utils.numberToHex('100000000000'),
gas: web3.utils.numberToHex('100000'),
// value: web3.utils.toWei('1'),
value: web3.utils.toHex(web3.utils.toWei('1')),
to: currentData.currentGnosisSafeAddress,
};
console.log(`tx: ${JSON.stringify(tx, null, 2)}`);
// console.log('to-transfer: ' + tx.value);
currentData.state = STATE_SAFEFUNDING;
await card.signAndSendTransaction(web3, tx, 1);
currentData.state = STATE_SAFEFUNDED;
}
async function state_multisigCollecting(card) {
const address = await card.getAddress(1);
if (currentData.multisigCollected[address] === undefined) {
if (currentData.collectedSafeAddresses.indexOf(address) === -1) {
throw Error(`Card ${address} is not allowed to unlock this safe`);
}
console.log(`collecting multisig form ${address}`);
const signedTx = await card.getSignatureFromHash(currentData.multisigTransactionHash, 1);
console.log('got signed Transaction');
// console.log(signedTx);
currentData.multisigCollected[address] = signedTx;
} else {
// overwrite existing ??
console.log('There is already a signature existing for this address.');
}
const numOfCollectedMultisigTxs = Object.keys(currentData.multisigCollected).length;
console.log(`numOfCollectedMultisigTxs : ${numOfCollectedMultisigTxs}`);
if (numOfCollectedMultisigTxs === getNumberOfRequiredSignatures(currentData.collectedSafeAddresses.length)) {
// we now have all signatures, move forward.
currentData.state = STATE_MULTISIGCOLLECTED;
} else {
console.log(
`waiting for further transactions.${numOfCollectedMultisigTxs} /
${getNumberOfRequiredSignatures(currentData.collectedSafeAddresses.length)}`,
);
}
}
async function state_multisigCollected(card) {
// we now have all signatures, move forward.
currentData.state = STATE_MULTISIGSENDING;
await gnosisSafe.sendMultisigTransaction(web3,
card, currentData.currentGnosisSafeAddress, currentData.multisigTransaction, currentData.multisigTransactionHash,
currentData.multisigCollected);
currentData.state = STATE_MULTISIGSUCCESS;
}
async function state_multisigSetup(card) {
const address = await card.getAddress(1);
console.log(`Multisig Setup target:${address}`);
currentData.multisigPayoutAddress = address;
const gnosisSafeTX = await gnosisSafe.createGnosisSafeTransaction(
web3, currentData.currentGnosisSafeAddress, currentData.multisigPayoutAddress,
web3.utils.toHex(web3.utils.toWei('1')),
);
console.log('gnosis safe TX:', gnosisSafeTX);
const txHash = await gnosisSafe.getGnosisSafeTransactionHash(
web3, currentData.currentGnosisSafeAddress, gnosisSafeTX,
);
console.log('txHash:', txHash);
currentData.multisigTransactionHash = txHash;
currentData.multisigTransaction = gnosisSafeTX;
currentData.multisigCollected = {};
currentData.state = STATE_MULTISIGSETUPFINISHED;
}
/* eslint-enable camelcase */
app.get('/', (req, res) => {
res.sendFile(path.join(`${__dirname}/index.html`));
});
app.get('/index_client.js', (req, res) => {
console.log('getting index_client.js');
res.sendFile(path.join(`${__dirname}/index_client.js`));
});
app.get('/currentData.json', (req, res) => {
// console.log('getting currentData')
res.send(JSON.stringify(currentData));
});
app.get('/settingUpSafe', (req, res) => {
if (currentData.state === STATE_COLLECTINGMULTISIGADDRESSES) {
if (currentData.collectedSafeAddresses.length === 0) {
currentData.lastError = 'There is at minimum 1 address required to initialize a gnosis safe';
} else {
currentData.state = STATE_SETUPSAFE;
}
}
res.send(JSON.stringify(currentData));
});
app.get('/deployNewGnosisSafe', async (req, res) => {
console.log('deployNewGnosisSafe');
// const newSafeAddress = deployNewSafe();
currentData.currentGnosisSafeAddress = undefined;
currentData.state = STATE_DEPLOY;
// array of '0xabc..890' string with the addresses that should get added to the safe.
currentData.collectedSafeAddresses = [];
currentData.lastError = '';
res.send(JSON.stringify(currentData));
});
app.get('/logo_*.png', async (req, res) => {
res.sendFile(path.join(__dirname, req.path));
});
app.listen(localWebserverListeningPort);
// var web3 = new Web3('http://127.0.0.1:9545/');
function newCard(reader) {
const card = new Security2GoCard.Security2GoCard(reader);
card.log_debug_signing = true;
card.log_debug_web3 = true;
cardSigner.card = card;
return card;
}
pcsc.on('reader', (reader) => {
console.log('New reader detected', reader.name);
// console.log(reader);
if (reader.name.startsWith('Identive Identive CLOUD 4500 F Dual Interface Reader [CLOUD 4700 F Contact Reader]')) {
console.log('ignoring that reader.');
return;
}
reader.on('error', function onReaderError(err) {
console.log('Error(', this.name, '):', err.message);
});
reader.on('status', async function onReaderStatus(status) {
console.log('Status(', this.name, '):', status);
/* check what has changed */
const changes = this.state ^ status.state;
if (changes) {
if ((changes & this.SCARD_STATE_EMPTY) && (status.state & this.SCARD_STATE_EMPTY)) {
console.log('card removed');/* card removed */
currentData.lastError = '';
reader.disconnect(reader.SCARD_LEAVE_CARD, (err) => {
if (err) {
console.log(err);
} else {
console.log('disconnected');
}
});
if (currentData.state === STATE_DEPLOYING) {
// it can happen that a deploying does not become a success
// in that case wwe switch back to Deploy.
// example reason: Connecting Error:Error: SCardConnect error: Card is unpowered.(0x80100067)
currentData.state = STATE_DEPLOY;
} else if (currentData.state === STATE_DEPLOYED) {
currentData.state = STATE_COLLECTINGMULTISIGADDRESSES;
} else if (currentData.state === STATE_SAFEREADY) {
currentData.state = STATE_SAFEFUNDINGSETUP;
} else if (currentData.state === STATE_SETTINGUPSAFE) {
currentData.state = STATE_SETUPSAFE;
} else if (currentData.state === STATE_SAFEFUNDED) {
currentData.state = STATE_MULTISIGSETUP;
// SafeFunded (R) -> MultiSigSetup
} else if (currentData.state === STATE_MULTISIGSETUPFINISHED) {
currentData.state = STATE_MULTISIGCOLLECTING;
} else if (currentData.state === STATE_MULTISIGSUCCESS) {
// after a success we can initiate a new payout.
currentData.multisigPayoutAddress = '';
currentData.multisigTransaction = undefined;
currentData.multisigTransactionHash = undefined;
currentData.multisigCollected = {};
currentData.state = STATE_SAFEFUNDINGSETUP;
}
} else if ((changes & this.SCARD_STATE_PRESENT) && (status.state & this.SCARD_STATE_PRESENT)) {
const stateBackup = Object.assign({}, currentData);
currentData.lastError = '';
try {
const card = newCard(reader);
if (currentData.state === STATE_COLLECTINGMULTISIGADDRESSES) {
await state_collectingMultisigAddresses(card);
} else if (currentData.state === STATE_DEPLOY) {
await state_deploy(card);
} else if (currentData.state === STATE_SETUPSAFE) {
await setupSafe(card);
} else if (currentData.state === STATE_SAFEFUNDINGSETUP) {
await state_safeFundingSetup(card);
} else if (currentData.state === STATE_MULTISIGSETUP) {
console.log('MULTISIG Setup');
await state_multisigSetup(card);
} else if (currentData.state === STATE_MULTISIGCOLLECTING) {
try {
await state_multisigCollecting(card);
} catch (error) {
currentData.multisigCollected = {};
currentData.lastError = `error:${error}`;
currentData.state = STATE_MULTISIGCOLLECTING;
}
} else if (currentData.state === STATE_MULTISIGCOLLECTED) {
await state_multisigCollected(card);
} else {
console.error(`state not implemented yet: ${currentData.state}`);
}
} catch (err) {
console.error('CAUGHT ERROR:');
currentData = Object.assign({}, stateBackup);
currentData.lastError = `error:${err}`;
// _currentData.state = 'error';
console.error(JSON.stringify(err));
console.error(currentData.lastError);
console.error(err.stack);
}
// doSomeTests(reader);
}
}
});
reader.on('end', function onReaderEnd() {
console.log('Reader', this.name, 'removed');
});
});
pcsc.on('error', (err) => {
console.log('PCSC error', err.message);
});
function printCurrentData() {
console.log('currentState:');
console.log(currentData);
}
function printState() {
console.log('printState:');
console.log(currentData.state);
}
printCurrentData();
printState();
console.log(`connected to ${web3Address}`);
console.log(
'System Ready, waiting for reader. if no reader shows up - '
+ 'sudo systemctl restart pcscd - and restart this project might help !',
);