github.com/codingfuture/orig-energi3@v0.8.4/energi/contracts/test/GenericProposalV1.spec.js (about) 1 // Copyright 2019 The Energi Core Authors 2 // This file is part of the Energi Core library. 3 // 4 // The Energi Core library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The Energi Core library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the Energi Core library. If not, see <http://www.gnu.org/licenses/>. 16 17 // Energi Governance system is the fundamental part of Energi Core. 18 19 'use strict'; 20 21 const GenericProposalV1 = artifacts.require('GenericProposalV1'); 22 const ITreasury = artifacts.require('ITreasury'); 23 const MasternodeTokenV1 = artifacts.require('MasternodeTokenV1'); 24 const MasternodeRegistryV1 = artifacts.require('MasternodeRegistryV1'); 25 26 const common = require('./common'); 27 28 contract("GenericProposalV1", async accounts => { 29 let mntoken; 30 let mnregistry; 31 let treasury; 32 33 before(async () => { 34 const mntoken_orig = await MasternodeTokenV1.deployed(); 35 mntoken = await MasternodeTokenV1.at(await mntoken_orig.proxy()); 36 37 const mnregistry_orig = await MasternodeRegistryV1.deployed(); 38 mnregistry = await MasternodeRegistryV1.at(await mnregistry_orig.proxy()); 39 40 const treasury_proxy = await mnregistry.treasury_proxy(); 41 treasury = await ITreasury.at(treasury_proxy); 42 }); 43 44 describe('Primary', () => { 45 const { fromAscii, toBN, toWei } = web3.utils; 46 47 const collateral1 = toBN(toWei('30000', 'ether')); 48 const collateral2 = toBN(toWei('20000', 'ether')); 49 const collateral3 = toBN(toWei('10000', 'ether')); 50 51 const owner1 = accounts[0]; 52 const owner2 = accounts[1]; 53 const owner3 = accounts[2]; 54 const not_owner = accounts[3]; 55 56 const masternode1 = accounts[9]; 57 const masternode2 = accounts[8]; 58 const masternode3 = accounts[7]; 59 60 const ip1 = toBN(0x12345678); 61 const ip2 = toBN(0x87654321); 62 const ip3 = toBN(0x43218765); 63 64 const enode_common = '123456789012345678901234567890' 65 const enode1 = [fromAscii(enode_common + '11'), fromAscii(enode_common + '11')]; 66 const enode2 = [fromAscii(enode_common + '11'), fromAscii(enode_common + '22')]; 67 const enode3 = [fromAscii(enode_common + '11'), fromAscii(enode_common + '33')]; 68 69 before(async () => { 70 await mntoken.depositCollateral({ 71 from: owner1, 72 value: collateral1, 73 }); 74 await mntoken.depositCollateral({ 75 from: owner2, 76 value: collateral2, 77 }); 78 await mntoken.depositCollateral({ 79 from: owner3, 80 value: collateral3, 81 }); 82 83 mnregistry.announce(masternode1, ip1, enode1, {from: owner1}); 84 }); 85 86 after(async () => { 87 await mntoken.withdrawCollateral(collateral1, { 88 from: owner1, 89 }); 90 await mntoken.withdrawCollateral(collateral2, { 91 from: owner2, 92 }); 93 await mntoken.withdrawCollateral(collateral3, { 94 from: owner3, 95 }); 96 }); 97 98 it('should refuse wrong quorum percent', async () => { 99 try { 100 await GenericProposalV1.new( 101 mnregistry.address, 102 0, 103 60, 104 not_owner 105 ); 106 assert.fail('It must fail'); 107 } catch (e) { 108 assert.match(e.message, /Quorum min/); 109 } 110 111 await GenericProposalV1.new( 112 mnregistry.address, 113 1, 114 60, 115 not_owner 116 ); 117 118 await GenericProposalV1.new( 119 mnregistry.address, 120 100, 121 60, 122 not_owner 123 ); 124 125 try { 126 await GenericProposalV1.new( 127 mnregistry.address, 128 101, 129 60, 130 not_owner 131 ); 132 assert.fail('It must fail'); 133 } catch (e) { 134 assert.match(e.message, /Quorum max/); 135 } 136 }); 137 138 it('should refuse on lack of quorum', async () => { 139 mnregistry.announce(masternode1, ip1, enode1, {from: owner1}); 140 mnregistry.announce(masternode2, ip2, enode2, {from: owner2}); 141 mnregistry.announce(masternode3, ip3, enode3, {from: owner3}); 142 mnregistry.denounce(masternode1, {from: owner1}); 143 mnregistry.denounce(masternode2, {from: owner2}); 144 145 try { 146 await GenericProposalV1.new( 147 mnregistry.address, 148 51, 149 60, 150 not_owner 151 ); 152 assert.fail('It must fail'); 153 } catch (e) { 154 assert.match(e.message, /Active weight < 1\/2 ever weight/); 155 } 156 }); 157 158 it('should accept half-way', async () => { 159 mnregistry.announce(masternode1, ip1, enode1, {from: owner1}); 160 mnregistry.announce(masternode2, ip2, enode2, {from: owner2}); 161 mnregistry.announce(masternode3, ip3, enode3, {from: owner3}); 162 163 const proposal = await GenericProposalV1.new( 164 mnregistry.address, 165 51, 166 60, 167 not_owner 168 ); 169 170 expect(await proposal.canVote(owner1)).true; 171 await proposal.voteAccept({from: owner1}); 172 expect(await proposal.canVote(owner1)).false; 173 expect(await proposal.isAccepted()).false; 174 expect(await proposal.isFinished()).false; 175 expect((await proposal.accepted_weight()).toString()) 176 .eql(collateral1.toString()); 177 178 expect(await proposal.canVote(owner3)).true; 179 await proposal.voteAccept({from: owner3}); 180 expect(await proposal.canVote(owner3)).false; 181 expect(await proposal.isAccepted()).true; 182 expect(await proposal.isFinished()).true; 183 expect((await proposal.accepted_weight()).toString()) 184 .eql(collateral1.add(collateral3).toString()); 185 186 expect(await proposal.canVote(owner2)).true; 187 await proposal.voteAccept({from: owner2}); 188 expect(await proposal.canVote(owner2)).false; 189 expect(await proposal.isAccepted()).true; 190 expect(await proposal.isFinished()).true; 191 expect((await proposal.accepted_weight()).toString()) 192 .eql(collateral1.add(collateral2).add(collateral3).toString()); 193 }); 194 195 it('should reject half-way', async () => { 196 mnregistry.announce(masternode1, ip1, enode1, {from: owner1}); 197 mnregistry.announce(masternode2, ip2, enode2, {from: owner2}); 198 mnregistry.announce(masternode3, ip3, enode3, {from: owner3}); 199 200 const proposal = await GenericProposalV1.new( 201 mnregistry.address, 202 51, 203 60, 204 not_owner 205 ); 206 207 await proposal.voteReject({from: owner1}); 208 expect(await proposal.isAccepted()).false; 209 expect(await proposal.isFinished()).false; 210 expect((await proposal.rejected_weight()).toString()) 211 .eql(collateral1.toString()); 212 213 await proposal.voteReject({from: owner3}); 214 expect(await proposal.isAccepted()).false; 215 expect(await proposal.isFinished()).true; 216 expect((await proposal.rejected_weight()).toString()) 217 .eql(collateral1.add(collateral3).toString()); 218 219 await proposal.voteReject({from: owner2}); 220 expect(await proposal.isAccepted()).false; 221 expect(await proposal.isFinished()).true; 222 expect((await proposal.rejected_weight()).toString()) 223 .eql(collateral1.add(collateral2).add(collateral3).toString()); 224 }); 225 226 it('should not accept half-way', async () => { 227 mnregistry.announce(masternode1, ip1, enode1, {from: owner1}); 228 mnregistry.announce(masternode2, ip2, enode2, {from: owner2}); 229 mnregistry.announce(masternode3, ip3, enode3, {from: owner3}); 230 231 const proposal = await GenericProposalV1.new( 232 mnregistry.address, 233 51, 234 60, 235 not_owner 236 ); 237 238 expect(await proposal.canVote(owner1)).true; 239 await proposal.voteAccept({from: owner1}); 240 expect(await proposal.canVote(owner1)).false; 241 expect(await proposal.isAccepted()).false; 242 expect(await proposal.isFinished()).false; 243 expect((await proposal.accepted_weight()).toString()) 244 .eql(collateral1.toString()); 245 246 expect(await proposal.canVote(owner3)).true; 247 await proposal.voteReject({from: owner3}); 248 expect(await proposal.canVote(owner3)).false; 249 expect(await proposal.isAccepted()).false; 250 expect(await proposal.isFinished()).false; 251 expect((await proposal.accepted_weight()).toString()) 252 .eql(collateral1.toString()); 253 expect((await proposal.rejected_weight()).toString()) 254 .eql(collateral3.toString()); 255 256 await proposal.voteAccept({from: owner2}); 257 expect(await proposal.isAccepted()).true; 258 expect(await proposal.isFinished()).true; 259 expect((await proposal.accepted_weight()).toString()) 260 .eql(collateral1.add(collateral2).toString()); 261 expect((await proposal.rejected_weight()).toString()) 262 .eql(collateral3.toString()); 263 264 }); 265 266 it('should not accept half-way and refuse vote after deadline', async () => { 267 mnregistry.announce(masternode1, ip1, enode1, {from: owner1}); 268 mnregistry.announce(masternode2, ip2, enode2, {from: owner2}); 269 mnregistry.announce(masternode3, ip3, enode3, {from: owner3}); 270 271 const proposal = await GenericProposalV1.new( 272 mnregistry.address, 273 5, 274 60, 275 not_owner 276 ); 277 278 expect(await proposal.canVote(owner1)).true; 279 await proposal.voteAccept({from: owner1}); 280 expect(await proposal.canVote(owner1)).false; 281 expect(await proposal.isAccepted()).false; 282 expect(await proposal.isFinished()).false; 283 expect((await proposal.accepted_weight()).toString()) 284 .eql(collateral1.toString()); 285 286 expect(await proposal.canVote(owner3)).true; 287 await proposal.voteReject({from: owner3}); 288 expect(await proposal.canVote(owner3)).false; 289 expect(await proposal.isAccepted()).false; 290 expect(await proposal.isFinished()).false; 291 expect((await proposal.accepted_weight()).toString()) 292 .eql(collateral1.toString()); 293 expect((await proposal.rejected_weight()).toString()) 294 .eql(collateral3.toString()); 295 296 expect(await proposal.canVote(owner2)).true; 297 await common.moveTime(web3, 70); 298 expect(await proposal.canVote(owner2)).false; 299 300 try { 301 await proposal.voteAccept({from: owner2}); 302 assert.fail('It must fail'); 303 } catch (e) { 304 assert.match(e.message, /Finished/); 305 } 306 307 expect(await proposal.isAccepted()).true; 308 expect(await proposal.isFinished()).true; 309 expect((await proposal.accepted_weight()).toString()) 310 .eql(collateral1.toString()); 311 expect((await proposal.rejected_weight()).toString()) 312 .eql(collateral3.toString()); 313 314 }); 315 316 it('should not accept half-way and refuse by quorum', async () => { 317 mnregistry.announce(masternode1, ip1, enode1, {from: owner1}); 318 mnregistry.announce(masternode2, ip2, enode2, {from: owner2}); 319 mnregistry.announce(masternode3, ip3, enode3, {from: owner3}); 320 321 const proposal = await GenericProposalV1.new( 322 mnregistry.address, 323 90, 324 60, 325 not_owner 326 ); 327 328 expect(await proposal.canVote(owner1)).true; 329 await proposal.voteAccept({from: owner1}); 330 expect(await proposal.canVote(owner1)).false; 331 expect(await proposal.isAccepted()).false; 332 expect(await proposal.isFinished()).false; 333 expect((await proposal.accepted_weight()).toString()) 334 .eql(collateral1.toString()); 335 336 expect(await proposal.canVote(owner3)).true; 337 await proposal.voteReject({from: owner3}); 338 expect(await proposal.canVote(owner3)).false; 339 expect(await proposal.isAccepted()).false; 340 expect(await proposal.isFinished()).false; 341 expect((await proposal.accepted_weight()).toString()) 342 .eql(collateral1.toString()); 343 expect((await proposal.rejected_weight()).toString()) 344 .eql(collateral3.toString()); 345 346 expect(await proposal.canVote(owner2)).true; 347 await proposal.voteAccept({from: owner2}); 348 expect(await proposal.canVote(owner2)).false; 349 expect(await proposal.isAccepted()).false; 350 expect(await proposal.isFinished()).false; 351 await common.moveTime(web3, 70); 352 expect(await proposal.isFinished()).true; 353 expect((await proposal.accepted_weight()).toString()) 354 .eql(collateral1.add(collateral2).toString()); 355 expect((await proposal.rejected_weight()).toString()) 356 .eql(collateral3.toString()); 357 }); 358 359 it('should refuse vote of not eligible Masternode', async () => { 360 mnregistry.announce(masternode1, ip1, enode1, {from: owner1}); 361 362 const proposal = await GenericProposalV1.new( 363 mnregistry.address, 364 51, 365 60, 366 not_owner 367 ); 368 369 mnregistry.announce(masternode2, ip2, enode2, {from: owner2}); 370 expect(await proposal.canVote(owner2)).false; 371 372 try { 373 await proposal.voteAccept({from: owner2}); 374 assert.fail('It must fail'); 375 } catch (e) { 376 assert.match(e.message, /Not eligible/); 377 } 378 379 expect(await proposal.canVote(owner1)).true; 380 await proposal.voteAccept({from: owner1}); 381 expect(await proposal.canVote(owner1)).false; 382 }); 383 384 it('should refuse already voted Masternode', async () => { 385 mnregistry.announce(masternode1, ip1, enode1, {from: owner1}); 386 387 const proposal = await GenericProposalV1.new( 388 mnregistry.address, 389 51, 390 60, 391 not_owner 392 ); 393 394 expect(await proposal.canVote(owner1)).true; 395 await proposal.voteAccept({from: owner1}); 396 expect(await proposal.canVote(owner1)).false; 397 398 try { 399 await proposal.voteAccept({from: owner1}); 400 assert.fail('It must fail'); 401 } catch (e) { 402 assert.match(e.message, /Already voted/); 403 } 404 }); 405 406 it('should refuse withdraw() unless accepted', async () => { 407 const proposal = await GenericProposalV1.new( 408 mnregistry.address, 409 51, 410 60, 411 not_owner 412 ); 413 414 try { 415 await proposal.withdraw({from: not_owner}); 416 assert.fail('It must fail'); 417 } catch (e) { 418 assert.match(e.message, /Not accepted/); 419 } 420 }); 421 422 it('should withdraw()', async () => { 423 const bal_before = await web3.eth.getBalance(not_owner); 424 425 mnregistry.announce(masternode1, ip1, enode1, {from: owner1}); 426 const proposal = await GenericProposalV1.new( 427 mnregistry.address, 428 1, 429 60, 430 not_owner 431 ); 432 await proposal.voteAccept({from: owner1}); 433 await common.moveTime(web3, 70); 434 435 await proposal.setFee({ from: owner1, value: toWei('2', 'ether')}); 436 await proposal.setFee({ from: owner1, value: toWei('3', 'ether')}); 437 438 await proposal.withdraw({from: owner1}); 439 const bal_after = await web3.eth.getBalance(not_owner); 440 expect(toBN(bal_after).sub(toBN(bal_before)).toString()) 441 .equal(toBN(toWei('5', 'ether')).toString()); 442 }); 443 444 it('should refuse destroy() unless parent', async () => { 445 const proposal = await GenericProposalV1.new( 446 mnregistry.address, 447 1, 448 60, 449 not_owner 450 ); 451 452 try { 453 await proposal.destroy({from: not_owner}); 454 assert.fail('It must fail'); 455 } catch (e) { 456 assert.match(e.message, /Only parent/); 457 } 458 }); 459 460 it('should destroy()', async () => { 461 const bal_before = await web3.eth.getBalance(not_owner); 462 463 mnregistry.announce(masternode1, ip1, enode1, {from: owner1}); 464 const proposal = await GenericProposalV1.new( 465 mnregistry.address, 466 1, 467 60, 468 not_owner 469 ); 470 await proposal.voteAccept({from: owner1}); 471 await common.moveTime(web3, 70); 472 473 await proposal.setFee({ from: owner1, value: toWei('5', 'ether') }); 474 475 await proposal.destroy({from: owner1}); 476 const bal_after = await web3.eth.getBalance(not_owner); 477 expect(toBN(bal_after).sub(toBN(bal_before)).toString()) 478 .equal(toBN(toWei('5', 'ether')).toString()); 479 }); 480 481 it('should refuse collect() unless rejected', async () => { 482 mnregistry.announce(masternode1, ip1, enode1, {from: owner1}); 483 484 const proposal = await GenericProposalV1.new( 485 mnregistry.address, 486 1, 487 60, 488 not_owner 489 ); 490 491 try { 492 await proposal.collect({from: not_owner}); 493 assert.fail('It must fail'); 494 } catch (e) { 495 assert.match(e.message, /Not collectable/); 496 } 497 498 await proposal.voteAccept({from: owner1}); 499 await common.moveTime(web3, 70); 500 501 try { 502 await proposal.collect({from: not_owner}); 503 assert.fail('It must fail'); 504 } catch (e) { 505 assert.match(e.message, /Not collectable/); 506 } 507 }); 508 509 it('should collect()', async () => { 510 expect(await mnregistry.treasury_proxy()).equal(treasury.address); 511 512 const proposal = await GenericProposalV1.new( 513 mnregistry.address, 514 1, 515 60, 516 not_owner 517 ); 518 519 await proposal.setFee({ from: owner1, value: toWei('5', 'ether') }); 520 521 await common.moveTime(web3, 70); 522 523 const bal_before = await treasury.balance(); 524 525 try { 526 await proposal.collect({from: not_owner}); 527 assert.fail('It must fail'); 528 } catch (e) { 529 assert.match(e.message, /Only parent/); 530 } 531 532 await proposal.collect(); 533 534 const bal_after = await treasury.balance(); 535 expect(toBN(bal_after).sub(toBN(bal_before)).toString()) 536 .equal(toBN(toWei('5', 'ether')).toString()); 537 }); 538 539 it('should refuse payments', async () => { 540 const proposal = await GenericProposalV1.new( 541 mnregistry.address, 542 1, 543 60, 544 not_owner 545 ); 546 547 try { 548 await proposal.send(toWei('1', 'ether'), {from: not_owner}); 549 assert.fail('It must fail'); 550 } catch (e) { 551 assert.match(e.message, /Not allowed/); 552 } 553 }); 554 }); 555 });