diff --git a/contracts/BSCValidatorSet.sol b/contracts/BSCValidatorSet.sol index 958f3917..c84fc420 100644 --- a/contracts/BSCValidatorSet.sol +++ b/contracts/BSCValidatorSet.sol @@ -39,6 +39,8 @@ contract BSCValidatorSet is IBSCValidatorSet, System, IParamSubscriber, IApplica uint32 public constant ERROR_LEN_OF_VAL_MISMATCH = 103; uint32 public constant ERROR_RELAYFEE_TOO_LARGE = 104; + uint256 public constant INIT_NUM_OF_CABINETS = 21; + uint256 public constant EPOCH = 200; /*********************** state of the contract **************************/ Validator[] public currentValidatorSet; @@ -67,6 +69,10 @@ contract BSCValidatorSet is IBSCValidatorSet, System, IParamSubscriber, IApplica // Corresponds strictly to currentValidatorSet // validatorExtraSet[index] = the `ValidatorExtra` info of currentValidatorSet[index] ValidatorExtra[] public validatorExtraSet; + // BEP-131 candidate validator + uint256 public numOfCabinets; + uint256 public maxNumOfCandidates; + uint256 public maxNumOfWorkingCandidates; struct Validator{ address consensusAddress; @@ -354,7 +360,46 @@ contract BSCValidatorSet is IBSCValidatorSet, System, IParamSubscriber, IApplica return CODE_OK; } - function getValidators() public view returns (address[] memory) { + function shuffle(address[] memory validators, uint256 epochNumber, uint startIdx, uint offset, uint limit, uint modNumber) internal pure { + for (uint i = 0; i 0) { + uint256 epochNumber = block.number / EPOCH; + shuffle(validators, epochNumber, _numOfCabinets-_maxNumOfWorkingCandidates, 0, _maxNumOfWorkingCandidates, _numOfCabinets); + shuffle(validators, epochNumber, _numOfCabinets-_maxNumOfWorkingCandidates, _numOfCabinets-_maxNumOfWorkingCandidates, + _maxNumOfWorkingCandidates, validators.length-_numOfCabinets+_maxNumOfWorkingCandidates); + } + address[] memory miningValidators = new address[](_numOfCabinets); + for (uint i=0;i<_numOfCabinets;i++) { + miningValidators[i] = validators[i]; + } + return miningValidators; + } + + function getValidators() public view returns(address[] memory) { uint n = currentValidatorSet.length; uint valid = 0; for (uint i = 0;i 0, "the maintainSlashScale must be greater than 0"); maintainSlashScale = newMaintainSlashScale; + } else if (Memory.compareStrings(key, "maxNumOfWorkingCandidates")) { + require(value.length == 32, "length of maxNumOfWorkingCandidates mismatch"); + uint256 newMaxNumOfWorkingCandidates = BytesToTypes.bytesToUint256(32, value); + require(newMaxNumOfWorkingCandidates <= maxNumOfCandidates, "the maxNumOfWorkingCandidates must be not greater than maxNumOfCandidates"); + maxNumOfWorkingCandidates = newMaxNumOfWorkingCandidates; + } else if (Memory.compareStrings(key, "maxNumOfCandidates")) { + require(value.length == 32, "length of maxNumOfCandidates mismatch"); + uint256 newMaxNumOfCandidates = BytesToTypes.bytesToUint256(32, value); + maxNumOfCandidates = newMaxNumOfCandidates; + if (maxNumOfWorkingCandidates > maxNumOfCandidates) { + maxNumOfWorkingCandidates = maxNumOfCandidates; + } + } else if (Memory.compareStrings(key, "numOfCabinets")) { + require(value.length == 32, "length of numOfCabinets mismatch"); + uint256 newNumOfCabinets = BytesToTypes.bytesToUint256(32, value); + require(newNumOfCabinets > 0, "the numOfCabinets must be greater than 0"); + require(newNumOfCabinets <= MAX_NUM_OF_VALIDATORS, "the numOfCabinets must be less than MAX_NUM_OF_VALIDATORS"); + numOfCabinets = newNumOfCabinets; } else { require(false, "unknown param"); } @@ -620,13 +683,13 @@ contract BSCValidatorSet is IBSCValidatorSet, System, IParamSubscriber, IApplica // remove the validator from currentValidatorSet delete currentValidatorSetMap[validator]; - // It is ok that the validatorSet is not in order. - if (index != currentValidatorSet.length - 1) { - currentValidatorSet[index] = currentValidatorSet[currentValidatorSet.length - 1]; - validatorExtraSet[index] = validatorExtraSet[currentValidatorSet.length - 1]; - - currentValidatorSetMap[currentValidatorSet[index].consensusAddress] = index + 1; + // remove felony validator + for (uint i = index;i < (currentValidatorSet.length-1);i++) { + currentValidatorSet[i] = currentValidatorSet[i+1]; + validatorExtraSet[i] = validatorExtraSet[i+1]; + currentValidatorSetMap[currentValidatorSet[i].consensusAddress] = i+1; } + currentValidatorSet.pop(); validatorExtraSet.pop(); @@ -693,6 +756,9 @@ contract BSCValidatorSet is IBSCValidatorSet, System, IParamSubscriber, IApplica function _exitMaintenance(address validator, uint index) private returns (bool isFelony){ uint256 workingValidatorCount = getValidators().length; + if (workingValidatorCount>numOfCabinets) { + workingValidatorCount = numOfCabinets; + } if (maintainSlashScale == 0 || workingValidatorCount == 0 || numOfMaintaining == 0) { // should not happen, still protect return false; diff --git a/contracts/BSCValidatorSet.template b/contracts/BSCValidatorSet.template index 237c9fa6..4bf64317 100644 --- a/contracts/BSCValidatorSet.template +++ b/contracts/BSCValidatorSet.template @@ -39,6 +39,8 @@ contract BSCValidatorSet is IBSCValidatorSet, System, IParamSubscriber, IApplica uint32 public constant ERROR_LEN_OF_VAL_MISMATCH = 103; uint32 public constant ERROR_RELAYFEE_TOO_LARGE = 104; + uint256 public constant INIT_NUM_OF_CABINETS = 21; + uint256 public constant EPOCH = 200; /*********************** state of the contract **************************/ Validator[] public currentValidatorSet; @@ -67,6 +69,10 @@ contract BSCValidatorSet is IBSCValidatorSet, System, IParamSubscriber, IApplica // Corresponds strictly to currentValidatorSet // validatorExtraSet[index] = the `ValidatorExtra` info of currentValidatorSet[index] ValidatorExtra[] public validatorExtraSet; + // BEP-131 candidate validator + uint256 public numOfCabinets; + uint256 public maxNumOfCandidates; + uint256 public maxNumOfWorkingCandidates; struct Validator{ address consensusAddress; @@ -354,7 +360,46 @@ contract BSCValidatorSet is IBSCValidatorSet, System, IParamSubscriber, IApplica return CODE_OK; } - function getValidators() public view returns (address[] memory) { + function shuffle(address[] memory validators, uint256 epochNumber, uint startIdx, uint offset, uint limit, uint modNumber) internal pure { + for (uint i = 0; i 0) { + uint256 epochNumber = block.number / EPOCH; + shuffle(validators, epochNumber, _numOfCabinets-_maxNumOfWorkingCandidates, 0, _maxNumOfWorkingCandidates, _numOfCabinets); + shuffle(validators, epochNumber, _numOfCabinets-_maxNumOfWorkingCandidates, _numOfCabinets-_maxNumOfWorkingCandidates, + _maxNumOfWorkingCandidates, validators.length-_numOfCabinets+_maxNumOfWorkingCandidates); + } + address[] memory miningValidators = new address[](_numOfCabinets); + for (uint i=0;i<_numOfCabinets;i++) { + miningValidators[i] = validators[i]; + } + return miningValidators; + } + + function getValidators() public view returns(address[] memory) { uint n = currentValidatorSet.length; uint valid = 0; for (uint i = 0;i 0, "the maintainSlashScale must be greater than 0"); maintainSlashScale = newMaintainSlashScale; + } else if (Memory.compareStrings(key, "maxNumOfWorkingCandidates")) { + require(value.length == 32, "length of maxNumOfWorkingCandidates mismatch"); + uint256 newMaxNumOfWorkingCandidates = BytesToTypes.bytesToUint256(32, value); + require(newMaxNumOfWorkingCandidates <= maxNumOfCandidates, "the maxNumOfWorkingCandidates must be not greater than maxNumOfCandidates"); + maxNumOfWorkingCandidates = newMaxNumOfWorkingCandidates; + } else if (Memory.compareStrings(key, "maxNumOfCandidates")) { + require(value.length == 32, "length of maxNumOfCandidates mismatch"); + uint256 newMaxNumOfCandidates = BytesToTypes.bytesToUint256(32, value); + maxNumOfCandidates = newMaxNumOfCandidates; + if (maxNumOfWorkingCandidates > maxNumOfCandidates) { + maxNumOfWorkingCandidates = maxNumOfCandidates; + } + } else if (Memory.compareStrings(key, "numOfCabinets")) { + require(value.length == 32, "length of numOfCabinets mismatch"); + uint256 newNumOfCabinets = BytesToTypes.bytesToUint256(32, value); + require(newNumOfCabinets > 0, "the numOfCabinets must be greater than 0"); + require(newNumOfCabinets <= MAX_NUM_OF_VALIDATORS, "the numOfCabinets must be less than MAX_NUM_OF_VALIDATORS"); + numOfCabinets = newNumOfCabinets; } else { require(false, "unknown param"); } @@ -620,12 +683,11 @@ contract BSCValidatorSet is IBSCValidatorSet, System, IParamSubscriber, IApplica // remove the validator from currentValidatorSet delete currentValidatorSetMap[validator]; - // It is ok that the validatorSet is not in order. - if (index != currentValidatorSet.length - 1) { - currentValidatorSet[index] = currentValidatorSet[currentValidatorSet.length - 1]; - validatorExtraSet[index] = validatorExtraSet[currentValidatorSet.length - 1]; - - currentValidatorSetMap[currentValidatorSet[index].consensusAddress] = index + 1; + // remove felony validator + for (uint i = index;i < (currentValidatorSet.length-1);i++) { + currentValidatorSet[i] = currentValidatorSet[i+1]; + validatorExtraSet[i] = validatorExtraSet[i+1]; + currentValidatorSetMap[currentValidatorSet[i].consensusAddress] = i+1; } currentValidatorSet.pop(); validatorExtraSet.pop(); @@ -693,6 +755,9 @@ contract BSCValidatorSet is IBSCValidatorSet, System, IParamSubscriber, IApplica function _exitMaintenance(address validator, uint index) private returns (bool isFelony){ uint256 workingValidatorCount = getValidators().length; + if (workingValidatorCount > numOfCabinets) { + workingValidatorCount = numOfCabinets; + } if (maintainSlashScale == 0 || workingValidatorCount == 0 || numOfMaintaining == 0) { // should not happen, still protect return false; diff --git a/test/BSCValidatorSet.js b/test/BSCValidatorSet.js index aed19f04..f51c425e 100644 --- a/test/BSCValidatorSet.js +++ b/test/BSCValidatorSet.js @@ -600,6 +600,141 @@ contract('BSCValidatorSet', (accounts) => { }); }); +contract('BSCValidatorSet', (accounts) => { + it('test set maxNumOfWorkingCandidates greater than maxNumOfCandidates', async () => { + const validatorSetInstance = await BSCValidatorSet.deployed(); + const relayer = accounts[2]; + const relayerInstance = await RelayerHub.deployed(); + await relayerInstance.register({from: relayer, value: 1e20}); + const crossChain = await CrossChain.deployed(); + const govHub = await GovHub.deployed(); + await govHub.updateContractAddr(BSCValidatorSet.address, SlashIndicator.address, SystemReward.address, LightClient.address, MockTokenHub.address, RelayerIncentivize.address, RelayerHub.address, GovHub.address, TokenManager.address, crossChain.address); + + // should fail + govChannelSeq = await crossChain.channelReceiveSequenceMap(GOV_CHANNEL_ID) + govValue = "0x0000000000000000000000000000000000000000000000000000000000000002";// 2; + govPackageBytes = serializeGovPack("maxNumOfWorkingCandidates", govValue, validatorSetInstance.address); + await crossChain.handlePackage(Buffer.concat([buildSyncPackagePrefix(2e16), (govPackageBytes)]), proof, merkleHeight, govChannelSeq, GOV_CHANNEL_ID, {from: relayer}); + except = await validatorSetInstance.maxNumOfWorkingCandidates.call(); + assert.equal(web3.utils.toBN(except).eq(web3.utils.toBN(0)), true, "wrong maxNumOfWorkingCandidates"); + }); +}); + +contract('BSCValidatorSet', (accounts) => { + it('test set maxNumOfCandidates less than maxNumOfWorkingCandidates', async () => { + const validatorSetInstance = await BSCValidatorSet.deployed(); + const relayer = accounts[2]; + const relayerInstance = await RelayerHub.deployed(); + await relayerInstance.register({from: relayer, value: 1e20}); + const crossChain = await CrossChain.deployed(); + const govHub = await GovHub.deployed(); + await govHub.updateContractAddr(BSCValidatorSet.address, SlashIndicator.address, SystemReward.address, LightClient.address, MockTokenHub.address, RelayerIncentivize.address, RelayerHub.address, GovHub.address, TokenManager.address, crossChain.address); + + // set maxNumOfCandidates to 20 + govChannelSeq = await crossChain.channelReceiveSequenceMap(GOV_CHANNEL_ID) + govValue = "0x0000000000000000000000000000000000000000000000000000000000000014";// 20; + govPackageBytes = serializeGovPack("maxNumOfCandidates", govValue, validatorSetInstance.address); + await crossChain.handlePackage(Buffer.concat([buildSyncPackagePrefix(2e16), (govPackageBytes)]), proof, merkleHeight, govChannelSeq, GOV_CHANNEL_ID, {from: relayer}); + + except = await validatorSetInstance.maxNumOfCandidates.call(); + assert.equal(web3.utils.toBN(except).eq(web3.utils.toBN(20)), true, "wrong maxNumOfCandidates"); + + // set maxNumOfWorkingCandidates to 10 + govChannelSeq = await crossChain.channelReceiveSequenceMap(GOV_CHANNEL_ID) + govValue = "0x000000000000000000000000000000000000000000000000000000000000000A";// 10; + govPackageBytes = serializeGovPack("maxNumOfWorkingCandidates", govValue, validatorSetInstance.address); + await crossChain.handlePackage(Buffer.concat([buildSyncPackagePrefix(2e16), (govPackageBytes)]), proof, merkleHeight, govChannelSeq, GOV_CHANNEL_ID, {from: relayer}); + + except = await validatorSetInstance.maxNumOfWorkingCandidates.call(); + assert.equal(web3.utils.toBN(except).eq(web3.utils.toBN(10)), true, "wrong maxNumOfWorkingCandidates"); + + // set maxNumOfCandidates to 5 + govChannelSeq = await crossChain.channelReceiveSequenceMap(GOV_CHANNEL_ID) + govValue = "0x0000000000000000000000000000000000000000000000000000000000000005";// 5; + govPackageBytes = serializeGovPack("maxNumOfCandidates", govValue, validatorSetInstance.address); + await crossChain.handlePackage(Buffer.concat([buildSyncPackagePrefix(2e16), (govPackageBytes)]), proof, merkleHeight, govChannelSeq, GOV_CHANNEL_ID, {from: relayer}); + + except = await validatorSetInstance.maxNumOfCandidates.call(); + assert.equal(web3.utils.toBN(except).eq(web3.utils.toBN(5)), true, "wrong maxNumOfCandidates"); + except = await validatorSetInstance.maxNumOfWorkingCandidates.call(); + assert.equal(web3.utils.toBN(except).eq(web3.utils.toBN(5)), true, "wrong maxNumOfWorkingCandidates"); + }); +}); + +contract('BSCValidatorSet', (accounts) => { + it('test getMiningValidators with 41 validators', async () => { + const validatorSetInstance = await BSCValidatorSet.deployed(); + const relayer = accounts[2]; + const relayerInstance = await RelayerHub.deployed(); + await relayerInstance.register({from: relayer, value: 1e20}); + const crossChain = await CrossChain.deployed(); + const govHub = await GovHub.deployed(); + await govHub.updateContractAddr(BSCValidatorSet.address, SlashIndicator.address, SystemReward.address, LightClient.address, MockTokenHub.address, RelayerIncentivize.address, RelayerHub.address, GovHub.address, TokenManager.address, crossChain.address); + + let relayerAccount = accounts[8]; + let newValidators = []; + for (let i = 0; i < 41; i++) { + newValidators.push(web3.eth.accounts.create().address) + } + let packageBytes = validatorUpdateRlpEncode(newValidators, + newValidators, newValidators); + await validatorSetInstance.handleSynPackage(STAKE_CHANNEL_ID, packageBytes, {from: relayerAccount}); + + // set numOfCabinets to 21 + let govChannelSeq = await crossChain.channelReceiveSequenceMap(GOV_CHANNEL_ID); + let govValue = "0x0000000000000000000000000000000000000000000000000000000000000015";// 21; + let govPackageBytes = serializeGovPack("numOfCabinets", govValue, validatorSetInstance.address); + await crossChain.handlePackage(Buffer.concat([buildSyncPackagePrefix(2e16), (govPackageBytes)]), proof, merkleHeight, govChannelSeq, GOV_CHANNEL_ID, {from: relayer}); + + let except = await validatorSetInstance.numOfCabinets.call(); + assert.equal(web3.utils.toBN(except).eq(web3.utils.toBN(21)), true, "wrong numOfCabinets"); + + // without candidate validators + let maxNumOfWorkingCandidates = 2; + let numOfCabinets = 21; + let validators = await validatorSetInstance.getValidators.call(); + let miningValidators = await validatorSetInstance.getMiningValidators.call(); + assert.deepEqual(validators.slice(0,numOfCabinets), miningValidators, "wrong validators"); + + // set maxNumOfCandidates to 20 + govChannelSeq = await crossChain.channelReceiveSequenceMap(GOV_CHANNEL_ID) + govValue = "0x0000000000000000000000000000000000000000000000000000000000000014";// 20; + govPackageBytes = serializeGovPack("maxNumOfCandidates", govValue, validatorSetInstance.address); + await crossChain.handlePackage(Buffer.concat([buildSyncPackagePrefix(2e16), (govPackageBytes)]), proof, merkleHeight, govChannelSeq, GOV_CHANNEL_ID, {from: relayer}); + + except = await validatorSetInstance.maxNumOfCandidates.call(); + assert.equal(web3.utils.toBN(except).eq(web3.utils.toBN(20)), true, "wrong maxNumOfCandidates"); + + // set maxNumOfWorkingCandidates to 2 + govChannelSeq = await crossChain.channelReceiveSequenceMap(GOV_CHANNEL_ID) + govValue = "0x0000000000000000000000000000000000000000000000000000000000000002";// 2; + govPackageBytes = serializeGovPack("maxNumOfWorkingCandidates", govValue, validatorSetInstance.address); + await crossChain.handlePackage(Buffer.concat([buildSyncPackagePrefix(2e16), (govPackageBytes)]), proof, merkleHeight, govChannelSeq, GOV_CHANNEL_ID, {from: relayer}); + + except = await validatorSetInstance.maxNumOfWorkingCandidates.call(); + assert.equal(web3.utils.toBN(except).eq(web3.utils.toBN(2)), true, "wrong maxNumOfWorkingCandidates"); + + if ((validators.length - numOfCabinets) < maxNumOfWorkingCandidates){ + maxNumOfWorkingCandidates = validators.length - numOfCabinets; + } + + miningValidators = await validatorSetInstance.getMiningValidators.call(); + let exceptValues = validators.slice(0,numOfCabinets); + let outValidator = miningValidators.filter((addr)=>{ + return !exceptValues.includes(addr); + }); + for (var j=0;j { await validatorSetInstance.deposit(validator, {from: systemAccount, value: 2e18 }); amount = await validatorSetInstance.getIncoming.call(validator); - assert.equal(amount.toString(),web3.utils.toBN(2e18).toString()) + assert.equal(amount.toString(),web3.utils.toBN(2e18).toString(), "case1: incoming of account1 is wrong") for (let i =1; i<=150; i++){ await slashInstance.slash(validator, { from: systemAccount }); } let res= (await slashInstance.getSlashIndicator.call(validator)); - assert.equal(res[1].toNumber(),0); + assert.equal(res[1].toNumber(),0, "case1: slash indicator of account1 is wrong"); amount = await validatorSetInstance.getIncoming.call(validator); - assert.equal(amount.toNumber(),0); + assert.equal(amount.toNumber(),0, "case1: incoming of account1 is wrong"); amount = await validatorSetInstance.getIncoming.call(secondValidator); - assert.equal(amount.toString(),web3.utils.toBN(1e18).toString()); + assert.equal(amount.toString(),web3.utils.toBN(1e18).toString(), "case1: incoming of account2 is wrong"); amount = await validatorSetInstance.getIncoming.call(thirdValidator); - assert.equal(amount.toString(),web3.utils.toBN(1e18).toString()); - let consensusAddres = await validatorSetInstance.getValidators.call(); - assert.equal(consensusAddres.length,2); - assert.equal(consensusAddres[0],thirdValidator); - assert.equal(consensusAddres[1],secondValidator); + assert.equal(amount.toString(),web3.utils.toBN(1e18).toString(), "case1: incoming of account3 is wrong"); + let consensusAddress = await validatorSetInstance.getValidators.call(); + assert.equal(consensusAddress.length,2, "case1: length of validators should be 2"); + assert.equal(consensusAddress[0],secondValidator, "case1: index 0 of validators should be account2"); + assert.equal(consensusAddress[1],thirdValidator, "case1: index 1 of validators should be account3"); packageBytes = validatorUpdateRlpEncode([validator,secondValidator,thirdValidator], [validator,secondValidator,thirdValidator],[validator,secondValidator,thirdValidator]); @@ -187,23 +187,23 @@ contract('felony SlashIndicator', (accounts) => { await validatorSetInstance.deposit(secondValidator, {from: systemAccount, value: 2e18 }); amount = await validatorSetInstance.getIncoming.call(secondValidator); - assert.equal(amount.toString(),web3.utils.toBN(2e18).toString()) + assert.equal(amount.toString(),web3.utils.toBN(2e18).toString(), "case2: incoming of account2 is wrong") for (let i =1; i<=150; i++){ await slashInstance.slash(secondValidator, { from: systemAccount }); } res= (await slashInstance.getSlashIndicator.call(secondValidator)); - assert.equal(res[1].toNumber(),0); + assert.equal(res[1].toNumber(),0, "case2: slash indicator of account2 is wrong"); amount = await validatorSetInstance.getIncoming.call(secondValidator); - assert.equal(amount.toNumber(),0); + assert.equal(amount.toNumber(),0, "case2: incoming of account2 is wrong"); amount = await validatorSetInstance.getIncoming.call(validator); - assert.equal(amount.toString(),web3.utils.toBN(1e18).toString()); + assert.equal(amount.toString(),web3.utils.toBN(1e18).toString(), "case2: incoming of account1 is wrong"); amount = await validatorSetInstance.getIncoming.call(thirdValidator); - assert.equal(amount.toString(),web3.utils.toBN(1e18).toString()); - consensusAddres = await validatorSetInstance.getValidators.call(); - assert.equal(consensusAddres.length,2); - assert.equal(consensusAddres[0],validator); - assert.equal(consensusAddres[1],thirdValidator); + assert.equal(amount.toString(),web3.utils.toBN(1e18).toString(), "case2: incoming of account3 is wrong"); + consensusAddress = await validatorSetInstance.getValidators.call(); + assert.equal(consensusAddress.length,2, "case2: length of validators should be 2"); + assert.equal(consensusAddress[0],validator, "case2: index 0 of validators should be account1"); + assert.equal(consensusAddress[1],thirdValidator, "case2: index 1 of validators should be account3"); packageBytes = validatorUpdateRlpEncode([validator,secondValidator,thirdValidator], [validator,secondValidator,thirdValidator],[validator,secondValidator,thirdValidator]); @@ -211,23 +211,23 @@ contract('felony SlashIndicator', (accounts) => { await validatorSetInstance.deposit(thirdValidator, {from: systemAccount, value: 2e18 }); amount = await validatorSetInstance.getIncoming.call(thirdValidator); - assert.equal(amount.toString(),web3.utils.toBN(2e18).toString()) + assert.equal(amount.toString(),web3.utils.toBN(2e18).toString(), "case3: incoming of account3 is wrong") for (let i =1; i<=150; i++){ await slashInstance.slash(thirdValidator, { from: systemAccount }); } res= (await slashInstance.getSlashIndicator.call(thirdValidator)); - assert.equal(res[1].toNumber(),0); + assert.equal(res[1].toNumber(),0, "case3: slash indicator of account3 is wrong"); amount = await validatorSetInstance.getIncoming.call(thirdValidator); - assert.equal(amount.toNumber(),0); + assert.equal(amount.toNumber(),0, "case3: incoming of account3 is wrong"); amount = await validatorSetInstance.getIncoming.call(validator); - assert.equal(amount.toString(),web3.utils.toBN(1e18).toString()); + assert.equal(amount.toString(),web3.utils.toBN(1e18).toString(), "case3: incoming of account1 is wrong"); amount = await validatorSetInstance.getIncoming.call(secondValidator); - assert.equal(amount.toString(),web3.utils.toBN(1e18).toString()); - consensusAddres = await validatorSetInstance.getValidators.call(); - assert.equal(consensusAddres.length,2); - assert.equal(consensusAddres[0],validator); - assert.equal(consensusAddres[1],secondValidator); + assert.equal(amount.toString(),web3.utils.toBN(1e18).toString(), "case3: incoming of account2 is wrong"); + consensusAddress = await validatorSetInstance.getValidators.call(); + assert.equal(consensusAddress.length,2, "case3: length of validators should be 2"); + assert.equal(consensusAddress[0],validator, "case3: index 0 of validators should be account1"); + assert.equal(consensusAddress[1],secondValidator, "case3: index 0 of validators should be account2"); }); });