From 4eb954ba885889c65d035d429dfad77e99c73110 Mon Sep 17 00:00:00 2001 From: mbidenaio Date: Mon, 6 Sep 2021 16:09:16 +0500 Subject: [PATCH 1/2] Introduce separate fund to reward successful reports --- blockchain/rewards.go | 63 +++++++++++++++++++++++++++++++++ config/consensus.go | 6 ++++ core/ceremony/ceremony.go | 22 +++++++----- core/ceremony/ceremony_test.go | 8 ++--- core/ceremony/reporters.go | 6 ++-- core/ceremony/reporters_test.go | 11 +++--- stats/collector/collector.go | 12 +++++++ 7 files changed, 109 insertions(+), 19 deletions(-) diff --git a/blockchain/rewards.go b/blockchain/rewards.go index 30ff310a..3294f92f 100644 --- a/blockchain/rewards.go +++ b/blockchain/rewards.go @@ -33,6 +33,7 @@ func rewardValidIdentities(appState *appstate.AppState, config *config.Consensus totalRewardD := decimal.NewFromBigInt(totalReward, 0) addSuccessfulValidationReward(appState, config, validationResults, totalRewardD, statsCollector) addFlipReward(appState, config, validationResults, totalRewardD, statsCollector) + addReportReward(appState, config, validationResults, totalRewardD, statsCollector) addInvitationReward(appState, config, validationResults, totalRewardD, seed, epochDurations, statsCollector) addFoundationPayouts(appState, config, totalRewardD, statsCollector) addZeroWalletFund(appState, config, totalRewardD, statsCollector) @@ -119,6 +120,9 @@ func addFlipReward(appState *appstate.AppState, config *config.ConsensusConf, va totalWeight += getFlipRewardCoef(f.Grade) } } + if config.ReportsRewardPercent > 0 { + continue + } for _, reporters := range validationResult.ReportersToRewardByFlip { if len(reporters) == 0 { continue @@ -163,6 +167,9 @@ func addFlipReward(appState *appstate.AppState, config *config.ConsensusConf, va collector.AfterAddStake(statsCollector, addr, stake, appState) } } + if config.ReportsRewardPercent > 0 { + return + } for i := uint32(1); i <= appState.State.ShardsNum(); i++ { shardId := common.ShardId(i) validationResult, ok := validationResults[shardId] @@ -193,6 +200,62 @@ func addFlipReward(appState *appstate.AppState, config *config.ConsensusConf, va } } +func addReportReward(appState *appstate.AppState, config *config.ConsensusConf, validationResults map[common.ShardId]*types.ValidationResults, + totalReward decimal.Decimal, statsCollector collector.StatsCollector) { + if config.ReportsRewardPercent == 0 { + return + } + rewardD := totalReward.Mul(decimal.NewFromFloat32(config.ReportsRewardPercent)) + + totalWeight := uint64(0) + + for i := uint32(1); i <= appState.State.ShardsNum(); i++ { + validationResult, ok := validationResults[common.ShardId(i)] + if !ok { + continue + } + for _, reporters := range validationResult.ReportersToRewardByFlip { + totalWeight += uint64(len(reporters)) + } + } + + if totalWeight == 0 { + return + } + + rewardShare := rewardD.Div(decimal.NewFromBigInt(new(big.Int).SetUint64(totalWeight), 0)) + + collector.SetTotalReportsReward(statsCollector, math.ToInt(rewardD), math.ToInt(rewardShare)) + + for i := uint32(1); i <= appState.State.ShardsNum(); i++ { + shardId := common.ShardId(i) + validationResult, ok := validationResults[shardId] + if !ok { + continue + } + for flipIdx, reporters := range validationResult.ReportersToRewardByFlip { + if len(reporters) == 0 { + continue + } + for _, reporter := range reporters { + reward, stake := splitReward(math.ToInt(rewardShare), reporter.NewIdentityState == uint8(state.Newbie), config) + rewardDest := reporter.Address + if delegatee := appState.State.Delegatee(reporter.Address); delegatee != nil { + rewardDest = *delegatee + } + collector.BeginEpochRewardBalanceUpdate(statsCollector, rewardDest, reporter.Address, appState) + appState.State.AddBalance(rewardDest, reward) + appState.State.AddStake(reporter.Address, stake) + collector.CompleteBalanceUpdate(statsCollector, appState) + collector.AddMintedCoins(statsCollector, reward) + collector.AddMintedCoins(statsCollector, stake) + collector.AddReportedFlipsReward(statsCollector, rewardDest, reporter.Address, shardId, flipIdx, reward, stake) + collector.AfterAddStake(statsCollector, reporter.Address, stake, appState) + } + } + } +} + func getInvitationRewardCoef(age uint16, epochHeight uint32, epochDurations []uint32, config *config.ConsensusConf) float32 { var baseCoef float32 switch age { diff --git a/config/consensus.go b/config/consensus.go index c18fda83..8cd4a388 100644 --- a/config/consensus.go +++ b/config/consensus.go @@ -31,6 +31,7 @@ type ConsensusConf struct { SuccessfulValidationRewardPercent float32 FlipRewardPercent float32 ValidInvitationRewardPercent float32 + ReportsRewardPercent float32 FoundationPayoutsPercent float32 ZeroWalletPercent float32 FirstInvitationRewardCoef float32 @@ -109,6 +110,7 @@ func init() { SuccessfulValidationRewardPercent: 0.24, FlipRewardPercent: 0.32, ValidInvitationRewardPercent: 0.32, + ReportsRewardPercent: 0, FoundationPayoutsPercent: 0.1, ZeroWalletPercent: 0.02, FirstInvitationRewardCoef: 3.0, @@ -165,6 +167,10 @@ func ApplyConsensusVersion(ver ConsensusVerson, cfg *ConsensusConf) { cfg.StartActivationDate = time.Date(2021, 05, 11, 8, 0, 0, 0, time.UTC).Unix() cfg.EndActivationDate = time.Date(2021, 05, 18, 0, 0, 0, 0, time.UTC).Unix() case ConsensusV6: + cfg.SuccessfulValidationRewardPercent = 0.2 + cfg.FlipRewardPercent = 0.35 + cfg.ValidInvitationRewardPercent = 0.18 + cfg.ReportsRewardPercent = 0.15 cfg.EnableValidationSharding = true cfg.ChangeKillTxValidation = true cfg.IncreaseGodInvitesLimit = true diff --git a/core/ceremony/ceremony.go b/core/ceremony/ceremony.go index 40da3ace..03a86ab1 100644 --- a/core/ceremony/ceremony.go +++ b/core/ceremony/ceremony.go @@ -959,7 +959,7 @@ func (vc *ValidationCeremony) ApplyNewEpoch(height uint64, appState *appstate.Ap shardValidationResults := new(types.ValidationResults) shardValidationResults.GoodInviters = make(map[common.Address]*types.InviterValidationResult) - shardValidationResults.BadAuthors, shardValidationResults.GoodAuthors, shardValidationResults.AuthorResults, flipsByAuthor, reportersToReward = vc.analyzeAuthors(flipQualification, reportersToReward, shardId) + shardValidationResults.BadAuthors, shardValidationResults.GoodAuthors, shardValidationResults.AuthorResults, flipsByAuthor, reportersToReward = vc.analyzeAuthors(flipQualification, reportersToReward, shardId, vc.config.Consensus) vc.logInfoWithInteraction("Approved candidates", "shardId", shardId, "cnt", len(approvedCandidates)) @@ -1002,7 +1002,7 @@ func (vc *ValidationCeremony) ApplyNewEpoch(height uint64, appState *appstate.Ap incSuccessfulInvites(shardValidationResults, god, identity, identityBirthday, newIdentityState, vc.epoch) setValidationResultToGoodAuthor(addr, newIdentityState, missed, shardValidationResults) setValidationResultToGoodInviter(shardValidationResults, addr, newIdentityState, identity.Invites) - reportersToReward.setValidationResult(addr, newIdentityState, missed, flipsByAuthor) + reportersToReward.setValidationResult(addr, newIdentityState, missed, flipsByAuthor, vc.config.Consensus) value := cacheValue{ state: newIdentityState, @@ -1148,8 +1148,8 @@ func getOrPutGoodInviter(validationResults *types.ValidationResults, address com return inviter, true } -func (vc *ValidationCeremony) analyzeAuthors(qualifications []FlipQualification, reportersToReward *reportersToReward, shardId common.ShardId) (badAuthors map[common.Address]types.BadAuthorReason, goodAuthors map[common.Address]*types.ValidationResult, authorResults map[common.Address]*types.AuthorResults, madeFlips map[common.Address][]int, filteredReportersToReward *reportersToReward) { - +func (vc *ValidationCeremony) analyzeAuthors(qualifications []FlipQualification, reportersToReward *reportersToReward, shardId common.ShardId, cfg *config.ConsensusConf) (badAuthors map[common.Address]types.BadAuthorReason, goodAuthors map[common.Address]*types.ValidationResult, authorResults map[common.Address]*types.AuthorResults, madeFlips map[common.Address][]int, filteredReportersToReward *reportersToReward) { + rewardAnyReport := cfg.ReportsRewardPercent > 0 badAuthors = make(map[common.Address]types.BadAuthorReason) goodAuthors = make(map[common.Address]*types.ValidationResult) authorResults = make(map[common.Address]*types.AuthorResults) @@ -1171,8 +1171,10 @@ func (vc *ValidationCeremony) analyzeAuthors(qualifications []FlipQualification, } if item.grade == types.GradeReported { badAuthors[author] = types.WrongWordsBadAuthor - if item.status != Qualified && item.status != WeaklyQualified { - reportersToReward.deleteFlip(flipIdx) + if !rewardAnyReport { + if item.status != Qualified && item.status != WeaklyQualified { + reportersToReward.deleteFlip(flipIdx) + } } } else if _, ok := badAuthors[author]; !ok { badAuthors[author] = types.QualifiedByNoneBadAuthor @@ -1209,9 +1211,11 @@ func (vc *ValidationCeremony) analyzeAuthors(qualifications []FlipQualification, for author := range badAuthors { delete(goodAuthors, author) reportersToReward.deleteReporter(author) - if _, ok := badAuthorsWithoutReport[author]; ok { - for _, flipIdx := range madeFlips[author] { - reportersToReward.deleteFlip(flipIdx) + if !rewardAnyReport { + if _, ok := badAuthorsWithoutReport[author]; ok { + for _, flipIdx := range madeFlips[author] { + reportersToReward.deleteFlip(flipIdx) + } } } } diff --git a/core/ceremony/ceremony_test.go b/core/ceremony/ceremony_test.go index db2c932c..5c4c94e4 100644 --- a/core/ceremony/ceremony_test.go +++ b/core/ceremony/ceremony_test.go @@ -439,8 +439,8 @@ func Test_analyzeAuthors(t *testing.T) { string([]byte{0x15}): auth11, string([]byte{0x16}): auth11, - }, - }} + }, + }} qualification := []FlipQualification{ {status: Qualified, grade: types.GradeD}, @@ -493,7 +493,8 @@ func Test_analyzeAuthors(t *testing.T) { reporters.addReport(21, reporter1) reporters.addReport(21, reporter2) - bad, good, authorResults, madeFlips, reporters := vc.analyzeAuthors(qualification, reporters, 0) + conf := &config.ConsensusConf{} + bad, good, authorResults, madeFlips, reporters := vc.analyzeAuthors(qualification, reporters, 0, conf) require.Contains(t, bad, auth2) require.Contains(t, bad, auth3) @@ -691,7 +692,6 @@ func Test_applyOnState(t *testing.T) { identity = appstate.State.GetIdentity(addr1) require.Equal(t, []byte{common.EncodeScore(5, 6), common.EncodeScore(1, 2)}, identity.Scores) - appstate.State.SetDelegatee(delegatee, common.Address{0x3}) applyOnState(config.ConsensusVersions[config.ConsensusV6], appstate, collector.NewStatsCollector(), addr1, cacheValue{ diff --git a/core/ceremony/reporters.go b/core/ceremony/reporters.go index ede24e8f..feb293f3 100644 --- a/core/ceremony/reporters.go +++ b/core/ceremony/reporters.go @@ -3,6 +3,7 @@ package ceremony import ( "github.com/idena-network/idena-go/blockchain/types" "github.com/idena-network/idena-go/common" + "github.com/idena-network/idena-go/config" "github.com/idena-network/idena-go/core/state" ) @@ -71,7 +72,7 @@ func (r *reportersToReward) deleteReporter(reporter common.Address) { } } -func (r *reportersToReward) setValidationResult(address common.Address, newState state.IdentityState, missed bool, flipsByAuthor map[common.Address][]int) { +func (r *reportersToReward) setValidationResult(address common.Address, newState state.IdentityState, missed bool, flipsByAuthor map[common.Address][]int, cfg *config.ConsensusConf) { if !newState.NewbieOrBetter() { r.deleteReporter(address) } else { @@ -79,7 +80,8 @@ func (r *reportersToReward) setValidationResult(address common.Address, newState reporter.NewIdentityState = uint8(newState) } } - if missed { + rewardAnyReport := cfg.ReportsRewardPercent > 0 + if missed && !rewardAnyReport { for _, flip := range flipsByAuthor[address] { r.deleteFlip(flip) } diff --git a/core/ceremony/reporters_test.go b/core/ceremony/reporters_test.go index fd402f8b..f13c4f40 100644 --- a/core/ceremony/reporters_test.go +++ b/core/ceremony/reporters_test.go @@ -2,6 +2,7 @@ package ceremony import ( "github.com/idena-network/idena-go/common" + "github.com/idena-network/idena-go/config" "github.com/idena-network/idena-go/core/state" "github.com/stretchr/testify/require" "testing" @@ -109,19 +110,21 @@ func Test_setValidationResult(t *testing.T) { addr4: {3, 4}, } - r.setValidationResult(addr1, state.Newbie, false, flipsByAuthor) + conf := &config.ConsensusConf{} + + r.setValidationResult(addr1, state.Newbie, false, flipsByAuthor, conf) require.Equal(t, uint8(state.Newbie), r.reportersByAddr[addr1].NewIdentityState) - r.setValidationResult(addr2, state.Human, false, flipsByAuthor) + r.setValidationResult(addr2, state.Human, false, flipsByAuthor, conf) require.Equal(t, uint8(state.Human), r.reportersByAddr[addr2].NewIdentityState) - r.setValidationResult(addr1, state.Killed, false, flipsByAuthor) + r.setValidationResult(addr1, state.Killed, false, flipsByAuthor, conf) require.Equal(t, 1, len(r.reportedFlipsByReporter)) require.Equal(t, 1, len(r.reportersByAddr)) require.NotContains(t, r.reportersByAddr, addr1) require.NotContains(t, r.reportedFlipsByReporter, addr1) - r.setValidationResult(addr3, state.Suspended, true, flipsByAuthor) + r.setValidationResult(addr3, state.Suspended, true, flipsByAuthor, conf) require.Equal(t, 0, len(r.reportedFlipsByReporter)) require.Equal(t, 0, len(r.reportersByAddr)) require.Equal(t, 0, len(r.reportersByFlip)) diff --git a/stats/collector/collector.go b/stats/collector/collector.go index a19d6093..1cc7ebcc 100644 --- a/stats/collector/collector.go +++ b/stats/collector/collector.go @@ -21,6 +21,7 @@ type StatsCollector interface { SetTotalReward(amount *big.Int) SetTotalValidationReward(amount *big.Int, share *big.Int) SetTotalFlipsReward(amount *big.Int, share *big.Int) + SetTotalReportsReward(amount *big.Int, share *big.Int) SetTotalInvitationsReward(amount *big.Int, share *big.Int) SetTotalFoundationPayouts(amount *big.Int) SetTotalZeroWalletFund(amount *big.Int) @@ -203,6 +204,17 @@ func SetTotalFlipsReward(c StatsCollector, amount *big.Int, share *big.Int) { c.SetTotalFlipsReward(amount, share) } +func (c *collectorStub) SetTotalReportsReward(amount *big.Int, share *big.Int) { + // do nothing +} + +func SetTotalReportsReward(c StatsCollector, amount *big.Int, share *big.Int) { + if c == nil { + return + } + c.SetTotalReportsReward(amount, share) +} + func (c *collectorStub) SetTotalInvitationsReward(amount *big.Int, share *big.Int) { // do nothing } From a9c899956a8366b87140f76f983f4fa84932c5d5 Mon Sep 17 00:00:00 2001 From: mbidenaio Date: Mon, 6 Sep 2021 23:18:13 +0500 Subject: [PATCH 2/2] Add tests --- blockchain/rewards_test.go | 24 +++-- core/ceremony/ceremony_test.go | 173 ++++++++++++++++++++++++++++++++ core/ceremony/reporters_test.go | 38 +++++++ 3 files changed, 227 insertions(+), 8 deletions(-) diff --git a/blockchain/rewards_test.go b/blockchain/rewards_test.go index ab5fc418..9ad4efe1 100644 --- a/blockchain/rewards_test.go +++ b/blockchain/rewards_test.go @@ -34,6 +34,7 @@ func Test_rewardValidIdentities(t *testing.T) { conf.FinalCommitteeReward = big.NewInt(5) config.ApplyConsensusVersion(config.ConsensusV4, conf) config.ApplyConsensusVersion(config.ConsensusV5, conf) + config.ApplyConsensusVersion(config.ConsensusV6, conf) memdb := db.NewMemDB() @@ -51,6 +52,7 @@ func Test_rewardValidIdentities(t *testing.T) { appState.State.SetState(auth3, state.Human) appState.State.SetState(auth4, state.Suspended) appState.State.SetState(badAuth, state.Newbie) + appState.State.SetShardsNum(2) appState.Commit(nil) validationResults := map[common.ShardId]*types.ValidationResults{ @@ -81,15 +83,19 @@ func Test_rewardValidIdentities(t *testing.T) { NewIdentityState: uint8(state.Verified), }, }, + }, + }, + 2: { + ReportersToRewardByFlip: map[int]map[common.Address]*types.Candidate{ 150: { reporter: &types.Candidate{ Address: reporter, NewIdentityState: uint8(state.Newbie), - }, - }, - 151: {}, }, }, + 151: {}, + }, + }, } appState.State.SetState(auth1, state.Verified) appState.State.SetBirthday(auth1, 2) @@ -110,8 +116,8 @@ func Test_rewardValidIdentities(t *testing.T) { appState.Commit(nil) - validationReward := float32(240) / 5.557298 // 4^(1/3)+1^(1/3)+2^(1/3)+5^(1/3) - flipReward := float32(400) / 25 + validationReward := float32(200) / 5.557298 // 4^(1/3)+1^(1/3)+2^(1/3)+5^(1/3) + flipReward := float32(350) / 23 godPayout := float32(100) // sum all coefficients @@ -120,7 +126,9 @@ func Test_rewardValidIdentities(t *testing.T) { // auth4: conf.ThirdInvitationRewardCoef (18) // god: conf.FirstInvitationRewardCoef + conf.SecondInvitationRewardCoef + conf.ThirdInvitationRewardCoef (3 + 9 + 18) // total: 57 - invitationReward := float32(4.2105263) // 240/57 + invitationReward := float32(3.1578947) // 180/57 + + reportReward := float32(50) // 150 / 3 reward, stake := splitAndSum(conf, false, validationReward*normalAge(3), flipReward*12.0, invitationReward*conf.SecondInvitationRewardCoef) @@ -131,7 +139,7 @@ func Test_rewardValidIdentities(t *testing.T) { require.True(t, reward.Cmp(appState.State.GetBalance(auth2)) == 0) require.True(t, stake.Cmp(appState.State.GetStakeBalance(auth2)) == 0) - reward, stake = splitAndSum(conf, false, validationReward*normalAge(1), flipReward*11.0, flipReward*0.5) + reward, stake = splitAndSum(conf, false, validationReward*normalAge(1), flipReward*11.0, reportReward) require.True(t, reward.Cmp(appState.State.GetBalance(auth3)) == 0) require.True(t, stake.Cmp(appState.State.GetStakeBalance(auth3)) == 0) @@ -144,7 +152,7 @@ func Test_rewardValidIdentities(t *testing.T) { require.True(t, reward.Cmp(appState.State.GetBalance(god)) == 0) require.True(t, stake.Cmp(appState.State.GetStakeBalance(god)) == 0) - reward, stake = splitAndSum(conf, true, flipReward, flipReward*0.5) + reward, stake = splitAndSum(conf, true, reportReward, reportReward) require.True(t, reward.Cmp(appState.State.GetBalance(reporter)) == 0) require.True(t, stake.Cmp(appState.State.GetStakeBalance(reporter)) == 0) diff --git a/core/ceremony/ceremony_test.go b/core/ceremony/ceremony_test.go index 5c4c94e4..b2fae020 100644 --- a/core/ceremony/ceremony_test.go +++ b/core/ceremony/ceremony_test.go @@ -556,6 +556,179 @@ func Test_analyzeAuthors(t *testing.T) { } +func Test_analyzeAuthors2(t *testing.T) { + vc := ValidationCeremony{} + + auth1 := common.Address{1} + auth2 := common.Address{2} + auth3 := common.Address{3} + auth4 := common.Address{4} + auth5 := common.Address{5} + auth6 := common.Address{6} + auth7 := common.Address{7} + auth8 := common.Address{8} + auth9 := common.Address{9} + auth10 := common.Address{10} + auth11 := common.Address{11} + + reporter1 := common.Address{12} + reporter2 := common.Address{13} + reporter3 := common.Address{14} + + vc.shardCandidates = map[common.ShardId]*candidatesOfShard{0: { + flips: [][]byte{{0x0}, {0x1}, {0x2}, {0x3}, {0x4}, {0x5}, {0x6}, {0x7}, {0x8}, {0x9}, {0xa}, {0xb}, {0xc}, + {0xd}, {0xe}, {0xf}, {0x10}, {0x11}, {0x12}, {0x13}, {0x14}, {0x15}, {0x16}}, + flipAuthorMap: map[string]common.Address{ + string([]byte{0x0}): auth1, + string([]byte{0x1}): auth1, + string([]byte{0x2}): auth1, + + string([]byte{0x3}): auth2, + string([]byte{0x4}): auth2, + + string([]byte{0x5}): auth3, + string([]byte{0x6}): auth3, + + string([]byte{0x7}): auth4, + string([]byte{0x8}): auth4, + + string([]byte{0x9}): auth5, + + string([]byte{0xa}): auth6, + string([]byte{0xb}): auth6, + string([]byte{0xc}): auth6, + + string([]byte{0xd}): auth7, + string([]byte{0xe}): auth7, + + string([]byte{0xf}): auth8, + string([]byte{0x10}): auth8, + + string([]byte{0x11}): auth9, + string([]byte{0x12}): auth9, + + string([]byte{0x13}): auth10, + string([]byte{0x14}): auth10, + + string([]byte{0x15}): auth11, + string([]byte{0x16}): auth11, + }, + }} + + qualification := []FlipQualification{ + {status: Qualified, grade: types.GradeD}, + {status: WeaklyQualified, grade: types.GradeC}, + {status: NotQualified, grade: types.GradeD}, + + {status: QualifiedByNone, grade: types.GradeD}, + {status: Qualified, grade: types.GradeD}, + + {status: WeaklyQualified, grade: types.GradeReported}, + {status: Qualified, grade: types.GradeD}, + + {status: NotQualified, grade: types.GradeD}, + {status: NotQualified, grade: types.GradeD}, + + {status: QualifiedByNone, grade: types.GradeD}, + + {status: Qualified, grade: types.GradeD}, + {status: WeaklyQualified, grade: types.GradeD}, + {status: Qualified, grade: types.GradeD}, + + {status: NotQualified, grade: types.GradeReported}, + {status: Qualified, grade: types.GradeA}, + + {status: Qualified, grade: types.GradeReported}, + {status: NotQualified, grade: types.GradeA}, + + {status: QualifiedByNone, grade: types.GradeReported}, + {status: Qualified, grade: types.GradeA}, + + {status: NotQualified, grade: types.GradeReported}, + {status: NotQualified, grade: types.GradeC}, + + {status: WeaklyQualified, grade: types.GradeReported}, + {status: QualifiedByNone, grade: types.GradeA}, + } + reporters := newReportersToReward() + reporters.addReport(5, reporter1) + reporters.addReport(5, auth2) + reporters.addReport(13, reporter1) + reporters.addReport(13, reporter2) + reporters.addReport(15, reporter1) + reporters.addReport(15, reporter3) + reporters.addReport(15, auth2) + reporters.addReport(17, reporter1) + reporters.addReport(17, reporter2) + reporters.addReport(17, reporter3) + reporters.addReport(19, reporter2) + reporters.addReport(19, reporter3) + reporters.addReport(21, reporter1) + reporters.addReport(21, reporter2) + + conf := &config.ConsensusConf{ + ReportsRewardPercent: 0.01, + } + bad, good, authorResults, madeFlips, reporters := vc.analyzeAuthors(qualification, reporters, 0, conf) + + require.Contains(t, bad, auth2) + require.Contains(t, bad, auth3) + require.Contains(t, bad, auth4) + require.Contains(t, bad, auth5) + require.Contains(t, bad, auth7) + require.Contains(t, bad, auth8) + require.Contains(t, bad, auth9) + require.Contains(t, bad, auth10) + require.NotContains(t, bad, auth1) + require.NotContains(t, bad, auth6) + + require.Contains(t, good, auth1) + require.Equal(t, 2, len(good[auth1].FlipsToReward)) + require.Equal(t, types.GradeD, good[auth1].FlipsToReward[0].Grade) + require.Equal(t, []byte{0x0}, good[auth1].FlipsToReward[0].Cid) + require.Equal(t, types.GradeC, good[auth1].FlipsToReward[1].Grade) + require.Equal(t, []byte{0x1}, good[auth1].FlipsToReward[1].Cid) + + require.True(t, authorResults[auth1].HasOneNotQualifiedFlip) + require.False(t, authorResults[auth1].AllFlipsNotQualified) + require.False(t, authorResults[auth1].HasOneReportedFlip) + + require.False(t, authorResults[auth6].HasOneNotQualifiedFlip) + require.False(t, authorResults[auth6].AllFlipsNotQualified) + require.False(t, authorResults[auth6].HasOneReportedFlip) + + require.False(t, authorResults[auth3].HasOneNotQualifiedFlip) + require.False(t, authorResults[auth3].AllFlipsNotQualified) + require.True(t, authorResults[auth3].HasOneReportedFlip) + + require.True(t, authorResults[auth4].HasOneNotQualifiedFlip) + require.True(t, authorResults[auth4].AllFlipsNotQualified) + require.False(t, authorResults[auth4].HasOneReportedFlip) + + require.False(t, authorResults[auth9].HasOneNotQualifiedFlip) + require.False(t, authorResults[auth9].AllFlipsNotQualified) + require.True(t, authorResults[auth9].HasOneReportedFlip) + + require.Equal(t, 11, len(madeFlips)) + + require.Equal(t, 6, len(reporters.reportersByFlip)) + require.Equal(t, 1, len(reporters.reportersByFlip[5])) + require.Equal(t, reporter1, reporters.reportersByFlip[5][reporter1].Address) + require.Equal(t, 2, len(reporters.reportersByFlip[15])) + require.Equal(t, reporter1, reporters.reportersByFlip[15][reporter1].Address) + require.Equal(t, reporter3, reporters.reportersByFlip[15][reporter3].Address) + + require.Equal(t, 3, len(reporters.reportersByAddr)) + require.Equal(t, reporter1, reporters.reportersByAddr[reporter1].Address) + require.Equal(t, reporter2, reporters.reportersByAddr[reporter2].Address) + require.Equal(t, reporter3, reporters.reportersByAddr[reporter3].Address) + + require.Equal(t, 3, len(reporters.reportedFlipsByReporter)) + require.Equal(t, 5, len(reporters.reportedFlipsByReporter[reporter1])) + require.Equal(t, 4, len(reporters.reportedFlipsByReporter[reporter2])) + require.Equal(t, 3, len(reporters.reportedFlipsByReporter[reporter3])) +} + func Test_incSuccessfulInvites(t *testing.T) { epoch := uint16(5) god := common.Address{0x1} diff --git a/core/ceremony/reporters_test.go b/core/ceremony/reporters_test.go index f13c4f40..d988c334 100644 --- a/core/ceremony/reporters_test.go +++ b/core/ceremony/reporters_test.go @@ -129,3 +129,41 @@ func Test_setValidationResult(t *testing.T) { require.Equal(t, 0, len(r.reportersByAddr)) require.Equal(t, 0, len(r.reportersByFlip)) } + +func Test_setValidationResult2(t *testing.T) { + r := newReportersToReward() + addr1 := common.Address{1} + addr2 := common.Address{2} + addr3 := common.Address{3} + addr4 := common.Address{4} + + r.addReport(2, addr1) + r.addReport(3, addr1) + r.addReport(2, addr2) + + flipsByAuthor := map[common.Address][]int{ + addr3: {1, 2}, + addr4: {3, 4}, + } + + conf := &config.ConsensusConf{ + ReportsRewardPercent: 0.01, + } + + r.setValidationResult(addr1, state.Newbie, false, flipsByAuthor, conf) + require.Equal(t, uint8(state.Newbie), r.reportersByAddr[addr1].NewIdentityState) + + r.setValidationResult(addr2, state.Human, false, flipsByAuthor, conf) + require.Equal(t, uint8(state.Human), r.reportersByAddr[addr2].NewIdentityState) + + r.setValidationResult(addr1, state.Killed, false, flipsByAuthor, conf) + require.Equal(t, 1, len(r.reportedFlipsByReporter)) + require.Equal(t, 1, len(r.reportersByAddr)) + require.NotContains(t, r.reportersByAddr, addr1) + require.NotContains(t, r.reportedFlipsByReporter, addr1) + + r.setValidationResult(addr3, state.Suspended, true, flipsByAuthor, conf) + require.Equal(t, 1, len(r.reportedFlipsByReporter)) + require.Equal(t, 1, len(r.reportersByAddr)) + require.Equal(t, 1, len(r.reportersByFlip)) +}