-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathwarrant_canary.test.js
421 lines (321 loc) · 14.7 KB
/
warrant_canary.test.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
const WarrantCanary = artifacts.require("WarrantCanary");
const truffleAssert = require('truffle-assertions');
/*
* uncomment accounts to access the test accounts made available by the
* Ethereum client
* See docs: https://www.trufflesuite.com/docs/truffle/testing/writing-tests-in-javascript
*/
function convertArrayToSmallNumber(IDsOwned) {
for (i = 0; i < IDsOwned.length ; i++) {
IDsOwned[i] = IDsOwned[i].toNumber();
}
}
function calculateNextContractAddress(account_, nonce_) {
// This function calculates the next address where a contract will be deployed
// adapted from: https://ethereum.stackexchange.com/questions/760/how-is-the-address-of-an-ethereum-contract-computed/761#761
const rlp = require('rlp');
const keccak = require('keccak');
var nonce = nonce_;
var sender = account_;
var input_arr = [ sender, nonce ];
var rlp_encoded = rlp.encode(input_arr);
var contract_address_long = keccak('keccak256').update(rlp_encoded).digest('hex');
var contract_address = contract_address_long.substring(24); //Trim the first 24 characters.
// console.log("next contract address: " + contract_address);
return contract_address;
}
contract("WarrantCanary", function (accounts) {
let instance;
let createTx;
const purpose = "test the contract."
let expirationTime;
let nextContractAddress;
const fundsAdded = web3.utils.toWei('0.1', 'ether');
const fundsWithdrawn = web3.utils.toWei('0.09', 'ether');
const excessFundsAdded = web3.utils.toWei('0.254', 'ether');
const zeroAddress = '0x0000000000000000000000000000000000000000';
beforeEach(async () => {
instance = await WarrantCanary.new();
expirationTime = Math.floor(Date.now() / 1000); // unix timestamp now // await web3.eth.getBlockTimestamp();
createTx = await instance.createWarrantCanary(expirationTime, purpose, zeroAddress);
// nextContractAddress = calculateNextContractAddress(accounts[0], await web3.eth.getTransactionCount(accounts[0]));
});
it("Creating Warrant Canary and store necessary information.", async () => {
assert.equal(createTx.logs[0].event, "LogCreated",
"creating a warrant canary should emit a Created event.",
);
let stateofWC = await instance.warrantCanaries.call(0);
// console.log("Purpose: " + result.purpose);
assert.equal(stateofWC.purpose, purpose,
"The purpose is not stored correctly.",
);
assert.equal(stateofWC.expirationTime, expirationTime,
"The expirationTime is not stored properly.",
);
assert.equal(stateofWC.warrantCanaryOwner, accounts[0],
"The owner of the warrant Canary is not set properly.",
);
});
it("Test that lastUpdatedInBlock state is set properly.", async () => {
let stateofWC = await instance.warrantCanaries.call(0);
blocknumberNow = await web3.eth.getBlockNumber();
assert.equal(stateofWC.lastUpdatedInBlock, blocknumberNow,
"The lastUpdatedInBLocknumber is wrong.",
);
await instance.changeTrustedThirdParty(0, accounts[5]);
stateofWC = await instance.warrantCanaries.call(0);
blocknumberNow = await web3.eth.getBlockNumber();
assert.equal(stateofWC.lastUpdatedInBlock, blocknumberNow,
"The lastUpdatedInBLocknumber is not updated when changing trusted third party.",
);
await instance.updateExpiration(0, blocknumberNow + 10);;
stateofWC = await instance.warrantCanaries.call(0);
blocknumberNow = await web3.eth.getBlockNumber();
assert.equal(stateofWC.lastUpdatedInBlock, blocknumberNow,
"The lastUpdatedInBLocknumber is not updated when changing the expiration.",
);
});
it("Testing Ownable library", async () => {
let owner = await instance.owner();
assert.equal(owner, accounts[0], "The owner of the contract has not been set properly.")
await instance.transferOwnership(accounts[1])
owner = await instance.owner();
assert.equal(owner, accounts[1], "The owner of the contract has not been set properly.")
await instance.renounceOwnership({from: accounts[1]});
owner = await instance.owner();
assert.equal(owner, 0, "The owner of the contract has not been set properly.")
});
it("Testing Pausable library.", async () => {
await instance.changeTrustedThirdParty(0, accounts[1]);
await truffleAssert.reverts(
instance.pauseContract({ from: accounts[1] }),
truffleAssert.ErrorType.REVERT,
"Only owner should be able to pause the contract."
);
await instance.pauseContract();
await truffleAssert.reverts(
instance.createWarrantCanary(expirationTime, purpose, zeroAddress),
truffleAssert.ErrorType.REVERT,
"Contract is pause so adding funds should fail."
);
await truffleAssert.reverts(
instance.addFunds(0, { value: fundsAdded }),
truffleAssert.ErrorType.REVERT,
"Contract is paused so adding funds should fail."
);
await truffleAssert.passes(
instance.withdrawAllFunds(0),
"Contract is paused withdrawing should still work."
);
await truffleAssert.reverts(
instance.unpauseContract({ from: accounts[1] }),
truffleAssert.ErrorType.REVERT,
"Only owner should be able unpause the contract."
);
await instance.unpauseContract();
await truffleAssert.passes(
instance.addFunds(0, { value: fundsAdded }),
"Contract is unpaused again, so adding funds should be possible."
);
await instance.withdrawAllFunds(0);
});
it("Testing adding and withdrawing funds.", async () => {
await truffleAssert.reverts(
instance.addFunds(0, { value: fundsAdded }),
truffleAssert.ErrorType.REVERT,
"Trusted Third Party has not been set, so transaction should revert."
);
await instance.changeTrustedThirdParty(0, accounts[5]);
const addFundsTx = await instance.addFunds(0, { value: fundsAdded });
const withdrawTx = await instance.withdrawSomeFunds(0, fundsWithdrawn);
await truffleAssert.reverts(
instance.withdrawSomeFunds(0, fundsAdded),
truffleAssert.ErrorType.REVERT,
"Trying to withdraw more funds than enclosed should revert."
);
assert.equal(addFundsTx.logs[0].event, "LogFundsAdded",
"adding funds should emit an event.",
);
assert.equal(withdrawTx.logs[0].event, "LogFundsWithdrawn",
"Withdrawing funds should emit an event.",
);
let stateofWC = await instance.warrantCanaries.call(0);
// console.log("added: " + fundsAdded + " withdrawn: " + fundsWithdrawn + " enclosed: " + result.enclosedFunds);
assert.equal(stateofWC.enclosedFunds, fundsAdded - fundsWithdrawn,
"Enclosed Funds does not equal added minus withdrawn funds."
)
truffleAssert.eventEmitted(
await instance.withdrawAllFunds(0),
"LogFundsWithdrawn"
);
stateofWC = await instance.warrantCanaries.call(0);
assert.equal(stateofWC.enclosedFunds, 0,
"Withdrawing everything does not remove all funds."
);
await truffleAssert.reverts(
instance.send(fundsAdded, {from: accounts[0]}),
truffleAssert.ErrorType.REVERT,
"Contract should not be allowed to receive plain ETH without calling a function."
);
});
it("Testing trusted third party their access to funds.", async () => {
await instance.changeTrustedThirdParty(0, accounts[5]);
const addFundsTx = await instance.addFunds(0, { value: fundsAdded });
await truffleAssert.reverts(
instance.withdrawSomeFunds(0, 10, { from: accounts[1] }),
truffleAssert.ErrorType.REVERT,
"Only owner or trusted third party are allowed to withdraw funds."
);
await truffleAssert.reverts(
instance.changeTrustedThirdParty(0, zeroAddress),
truffleAssert.ErrorType.REVERT,
"There are enclosed funds, so the trusted third party address cannot be changed to 0x0."
);
await truffleAssert.reverts(
instance.changeTrustedThirdParty(0, accounts[0]),
truffleAssert.ErrorType.REVERT,
"Should not be able to set trusted third party to warrant canary owner."
);
await truffleAssert.reverts(
instance.createWarrantCanary(expirationTime, purpose, accounts[0]),
truffleAssert.ErrorType.REVERT,
"Should not be able create a warrant canary with trusted third party the same as owner."
);
truffleAssert.eventEmitted(
await instance.changeTrustedThirdParty(0, accounts[1]),
"LogChangedTrustedThirdParty"
);
await truffleAssert.passes(
instance.withdrawAllFunds(0, { from: accounts[1] }),
"account 1 is now the trusted third party, so the transaction should pass."
);
stateofWC = await instance.warrantCanaries.call(0);
assert.equal(stateofWC.enclosedFunds, 0,
"Withdrawing everything does not remove all funds."
);
await truffleAssert.passes(
instance.changeTrustedThirdParty(0, zeroAddress),
"Setting the trusted third party to 0x0. should be possible as there are no funds enclosed. "
);
// Test that changing trusted third party properly updates the IDsTrusted mappings
let idToChange = 1;
await instance.createWarrantCanary(expirationTime, purpose, accounts[3]);
IDsTrusted = await instance.getIDsTrusted(accounts[3]);
convertArrayToSmallNumber(IDsTrusted);
// console.log("before: " + IDsTrusted);
assert(IDsTrusted.indexOf(idToChange) !== -1, "ID not in trusted warrant canary IDs.");
await instance.changeTrustedThirdParty(idToChange, accounts[4])
IDsTrusted = await instance.getIDsTrusted(accounts[3]);
convertArrayToSmallNumber(IDsTrusted);
// console.log("after old: " + IDsTrusted);
assert(IDsTrusted.indexOf(idToChange) === -1, "ID is still in trusted warrant canary IDs.");
IDsTrusted = await instance.getIDsTrusted(accounts[4]);
convertArrayToSmallNumber(IDsTrusted);
// console.log("after new: " + IDsTrusted);
assert(IDsTrusted.indexOf(idToChange) !== -1, "ID has not been added to trusted warrant canary IDs of the new address.");
});
it("Testing expiration block for third party access to funds.", async () => {
await instance.changeTrustedThirdParty(0, accounts[1]);
const addFundsTx = await instance.addFunds(0, { value: fundsAdded });
let currentTime = Math.floor(Date.now() / 1000); //await web3.eth.getBlockNumber();
// set expiration into the future
truffleAssert.eventEmitted(
await instance.updateExpiration(0, currentTime + 10000 ),
"LogExpirationUpdated"
);
await truffleAssert.reverts(
instance.withdrawAllFunds(0, { from: accounts[1] }),
truffleAssert.ErrorType.REVERT,
"Expiration Block is in the Future. Third party should not be able to withdraw."
);
currentBlockNumber = await web3.eth.getBlockNumber();
await instance.updateExpiration(0, currentTime);
await truffleAssert.passes(
instance.withdrawAllFunds(0, { from: accounts[1] }),
"Warrant Canary expired, so the transaction should pass."
);
stateofWC = await instance.warrantCanaries.call(0);
assert.equal(stateofWC.enclosedFunds, 0,
"Withdrawing everything does not remove all funds."
);
await truffleAssert.reverts(
instance.updateExpiration(0, currentTime + 1, {from: accounts[1]}),
truffleAssert.ErrorType.REVERT,
"Only the Warrant Canary owner should be able to change the expiration Time."
);
});
it("Tests that a warrant canary can be deleted.", async () => {
let idToDelete = 3;
let numberOfCanaries = 5; // total number after following for loop
// Add a few more warrant canaries with the same owner:
for (i = 1; i < numberOfCanaries; i++) {
await instance.createWarrantCanary(expirationTime, purpose, accounts[i]);
}
const addFundsTx = await instance.addFunds(idToDelete, { value: fundsAdded });
let IDsOwned = await instance.getIDsOwned(accounts[0]);
convertArrayToSmallNumber(IDsOwned);
let IDsTrusted = await instance.getIDsTrusted(accounts[idToDelete]);
convertArrayToSmallNumber(IDsTrusted);
// Make sure owner and trusted party are set properly
assert(IDsOwned.indexOf(idToDelete) !== -1, "ID not in owned warrant canary IDs.");
assert(IDsTrusted.indexOf(idToDelete) !== -1, "ID not in trusted warrant canary IDs.");
await truffleAssert.reverts(
instance.deleteWarrantCanary(idToDelete),
truffleAssert.ErrorType.REVERT,
"There are still funds enclosed. Warrant Canary cannot be deleted."
);
await instance.withdrawAllFunds(idToDelete);
truffleAssert.eventEmitted(
await instance.deleteWarrantCanary(idToDelete),
"LogDeleted"
);
stateofWC = await instance.warrantCanaries.call(idToDelete);
assert.equal(stateofWC.warrantCanaryOwner, 0,
"Warrant Canary has not been deleted."
);
IDsOwned = await instance.getIDsOwned(accounts[0]);
convertArrayToSmallNumber(IDsOwned);
IDsTrusted = await instance.getIDsTrusted(accounts[idToDelete]);
convertArrayToSmallNumber(IDsTrusted);
// Make sure that the IDs have been deleted in respective the mappings
assert(IDsOwned.indexOf(idToDelete) === -1, "ID is still in owned warrant canary IDs.");
assert(IDsTrusted.indexOf(idToDelete) === -1, "ID is still in trusted warrant canary IDs.");
// Test to improve branching coverage with address 0
truffleAssert.eventEmitted(
await instance.deleteWarrantCanary(0),
"LogDeleted"
);
});
it("Preparing Blockchain for the next test.", async () => {
// This is to prepare the blockchain for the next test (preload the next deploy address with ETH)
nextContractAddress = calculateNextContractAddress(accounts[0], await web3.eth.getTransactionCount(accounts[0]));
await web3.eth.sendTransaction(
{
from: accounts[1],
to: nextContractAddress,
value: excessFundsAdded,
gasLimit: 90000
}
);
assert.equal(await web3.eth.getBalance(nextContractAddress), excessFundsAdded,
"The funds have not been added to the expected contract address.");
});
it("Test if owner can withdraw excess funds.", async () => {
numberOfCanaries = 5;
assert.equal(await web3.eth.getBalance(instance.address), excessFundsAdded,
"The contract address is empty.");
// Add a few more warrant canaries with the same owner and add some funds:
for (i = 1; i < numberOfCanaries; i++) {
await instance.createWarrantCanary(expirationTime, purpose, accounts[1], { value: fundsAdded });
}
let addressBalanceBefore = await web3.eth.getBalance(instance.address);
await instance.retrieveExcessFunds();
let addressBalanceAfter = await web3.eth.getBalance(instance.address);
assert(addressBalanceBefore - addressBalanceAfter == excessFundsAdded,
"Owner could not retrieve excess funds.");
// get ETH back
for (i = 1; i < numberOfCanaries; i++) {
await instance.withdrawAllFunds(i);
}
});
});