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  });