/
keeper.go
140 lines (125 loc) · 4.68 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
package keeper
import (
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/x/bank"
"github.com/cosmos/cosmos-sdk/x/staking"
"github.com/swishlabsco/cosmos-ethereum-bridge/x/oracle/types"
sdk "github.com/cosmos/cosmos-sdk/types"
)
// Keeper maintains the link to data storage and exposes getter/setter methods for the various parts of the state machine
type Keeper struct {
coinKeeper bank.Keeper
stakeKeeper staking.Keeper
storeKey sdk.StoreKey // Unexposed key to access store from sdk.Context
cdc *codec.Codec // The wire codec for binary encoding/decoding.
codespace sdk.CodespaceType
consensusNeeded float64
}
// NewKeeper creates new instances of the oracle Keeper
func NewKeeper(stakeKeeper staking.Keeper, storeKey sdk.StoreKey, cdc *codec.Codec, codespace sdk.CodespaceType, consensusNeeded float64) (Keeper, sdk.Error) {
if consensusNeeded <= 0 || consensusNeeded > 1 {
return Keeper{}, types.ErrMinimumConsensusNeededInvalid(codespace)
}
return Keeper{
stakeKeeper: stakeKeeper,
storeKey: storeKey,
cdc: cdc,
codespace: codespace,
consensusNeeded: consensusNeeded,
}, nil
}
// Codespace returns the codespace
func (k Keeper) Codespace() sdk.CodespaceType {
return k.codespace
}
// GetProphecy gets the entire prophecy data struct for a given id
func (k Keeper) GetProphecy(ctx sdk.Context, id string) (types.Prophecy, sdk.Error) {
if id == "" {
return types.NewEmptyProphecy(), types.ErrInvalidIdentifier(k.Codespace())
}
store := ctx.KVStore(k.storeKey)
if !store.Has([]byte(id)) {
return types.NewEmptyProphecy(), types.ErrProphecyNotFound(k.Codespace())
}
bz := store.Get([]byte(id))
var dbProphecy types.DBProphecy
k.cdc.MustUnmarshalBinaryBare(bz, &dbProphecy)
deSerializedProphecy, err := dbProphecy.DeserializeFromDB()
if err != nil {
return types.NewEmptyProphecy(), types.ErrInternalDB(k.Codespace(), err)
}
return deSerializedProphecy, nil
}
// saveProphecy saves a prophecy with an initial claim
func (k Keeper) saveProphecy(ctx sdk.Context, prophecy types.Prophecy) sdk.Error {
if prophecy.ID == "" {
return types.ErrInvalidIdentifier(k.Codespace())
}
if len(prophecy.ClaimValidators) <= 0 {
return types.ErrNoClaims(k.Codespace())
}
store := ctx.KVStore(k.storeKey)
serializedProphecy, err := prophecy.SerializeForDB()
if err != nil {
return types.ErrInternalDB(k.Codespace(), err)
}
store.Set([]byte(prophecy.ID), k.cdc.MustMarshalBinaryBare(serializedProphecy))
return nil
}
func (k Keeper) ProcessClaim(ctx sdk.Context, id string, validator sdk.ValAddress, claim string) (types.Status, sdk.Error) {
activeValidator := k.checkActiveValidator(ctx, validator)
if !activeValidator {
return types.Status{}, types.ErrInvalidValidator(k.Codespace())
}
if claim == "" {
return types.Status{}, types.ErrInvalidClaim(k.Codespace())
}
prophecy, err := k.GetProphecy(ctx, id)
if err == nil {
if prophecy.Status.StatusText == types.SuccessStatusText || prophecy.Status.StatusText == types.FailedStatusText {
return types.Status{}, types.ErrProphecyFinalized(k.Codespace())
}
if prophecy.ValidatorClaims[validator.String()] != "" {
return types.Status{}, types.ErrDuplicateMessage(k.Codespace())
}
prophecy.AddClaim(validator, claim)
} else {
if err.Code() != types.CodeProphecyNotFound {
return types.Status{}, err
}
prophecy = types.NewProphecy(id)
prophecy.AddClaim(validator, claim)
}
prophecy = k.processCompletion(ctx, prophecy)
err = k.saveProphecy(ctx, prophecy)
if err != nil {
return types.Status{}, err
}
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
}
bondStatus := validator.GetStatus()
if bondStatus != sdk.Bonded {
return false
}
return true
}
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.StatusText = types.SuccessStatusText
prophecy.Status.FinalClaim = highestClaim
} else if highestPossibleConsensusRatio <= k.consensusNeeded {
prophecy.Status.StatusText = types.FailedStatusText
}
return prophecy
}