/
keeper.go
141 lines (116 loc) · 4.41 KB
/
keeper.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
package keeper
import (
"fmt"
"github.com/tendermint/tendermint/libs/log"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/sun-blockchain/harmony-cosmos-cross/x/oracle/types"
)
// Keeper maintains the link to data storage and
// exposes getter/setter methods for the various parts of the state machine
type Keeper struct {
cdc *codec.Codec // The wire codec for binary encoding/decoding.
storeKey sdk.StoreKey // Unexposed key to access store from sdk.Context
stakeKeeper types.StakingKeeper
// TODO: use this as param instead
consensusNeeded float64 // The minimum % of stake needed to sign claims in order for consensus to occur
}
// NewKeeper creates new instances of the oracle Keeper
func NewKeeper(
cdc *codec.Codec, storeKey sdk.StoreKey, stakeKeeper types.StakingKeeper, consensusNeeded float64,
) Keeper {
if consensusNeeded <= 0 || consensusNeeded > 1 {
panic(types.ErrMinimumConsensusNeededInvalid.Error())
}
return Keeper{
cdc: cdc,
storeKey: storeKey,
stakeKeeper: stakeKeeper,
consensusNeeded: consensusNeeded,
}
}
// Logger returns a module-specific logger.
func (k Keeper) Logger(ctx sdk.Context) log.Logger {
return ctx.Logger().With("module", fmt.Sprintf("x/%s", types.ModuleName))
}
// GetProphecy gets the entire prophecy data struct for a given id
func (k Keeper) GetProphecy(ctx sdk.Context, id string) (types.Prophecy, bool) {
store := ctx.KVStore(k.storeKey)
bz := store.Get([]byte(id))
if bz == nil {
return types.Prophecy{}, false
}
var dbProphecy types.DBProphecy
k.cdc.MustUnmarshalBinaryBare(bz, &dbProphecy)
deSerializedProphecy, err := dbProphecy.DeserializeFromDB()
if err != nil {
return types.Prophecy{}, false
}
return deSerializedProphecy, true
}
// setProphecy saves a prophecy with an initial claim
func (k Keeper) setProphecy(ctx sdk.Context, prophecy types.Prophecy) {
store := ctx.KVStore(k.storeKey)
serializedProphecy, err := prophecy.SerializeForDB()
if err != nil {
panic(err)
}
store.Set([]byte(prophecy.ID), k.cdc.MustMarshalBinaryBare(serializedProphecy))
}
// ProcessClaim ...
func (k Keeper) ProcessClaim(ctx sdk.Context, claim types.Claim) (types.Status, error) {
activeValidator := k.checkActiveValidator(ctx, claim.ValidatorAddress)
if !activeValidator {
return types.Status{}, types.ErrInvalidValidator
}
if claim.ID == "" {
return types.Status{}, types.ErrInvalidIdentifier
}
if claim.Content == "" {
return types.Status{}, types.ErrInvalidClaim
}
prophecy, found := k.GetProphecy(ctx, claim.ID)
if !found {
prophecy = types.NewProphecy(claim.ID)
}
switch prophecy.Status.Text {
case types.PendingStatusText:
// continue processing
default:
return types.Status{}, types.ErrProphecyFinalized
}
if prophecy.ValidatorClaims[claim.ValidatorAddress.String()] != "" {
return types.Status{}, types.ErrDuplicateMessage
}
prophecy.AddClaim(claim.ValidatorAddress, claim.Content)
prophecy = k.processCompletion(ctx, prophecy)
k.setProphecy(ctx, prophecy)
return prophecy.Status, nil
}
func (k Keeper) checkActiveValidator(ctx sdk.Context, validatorAddress sdk.ValAddress) bool {
validator, found := k.stakeKeeper.GetValidator(ctx, validatorAddress)
if !found {
return false
}
return validator.IsBonded()
}
// processCompletion looks at a given prophecy
// an assesses whether the claim with the highest power on that prophecy has enough
// power to be considered successful, or alternatively,
// will never be able to become successful due to not enough validation power being
// left to push it over the threshold required for consensus.
func (k Keeper) processCompletion(ctx sdk.Context, prophecy types.Prophecy) types.Prophecy {
highestClaim, highestClaimPower, totalClaimsPower := prophecy.FindHighestClaim(ctx, k.stakeKeeper)
totalPower := k.stakeKeeper.GetLastTotalPower(ctx)
highestConsensusRatio := float64(highestClaimPower) / float64(totalPower.Int64())
remainingPossibleClaimPower := totalPower.Int64() - totalClaimsPower
highestPossibleClaimPower := highestClaimPower + remainingPossibleClaimPower
highestPossibleConsensusRatio := float64(highestPossibleClaimPower) / float64(totalPower.Int64())
if highestConsensusRatio >= k.consensusNeeded {
prophecy.Status.Text = types.SuccessStatusText
prophecy.Status.FinalClaim = highestClaim
} else if highestPossibleConsensusRatio < k.consensusNeeded {
prophecy.Status.Text = types.FailedStatusText
}
return prophecy
}