/
msg_server_detection.go
161 lines (146 loc) · 8.3 KB
/
msg_server_detection.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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
package keeper
import (
"context"
"encoding/json"
"strconv"
"strings"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/lavanet/lava/utils"
"github.com/lavanet/lava/x/conflict/types"
pairingfilters "github.com/lavanet/lava/x/pairing/keeper/filters"
"golang.org/x/exp/slices"
)
func DetectionIndex(msg *types.MsgDetection, epochStart uint64) string {
return msg.Creator + msg.ResponseConflict.ConflictRelayData0.Request.RelaySession.Provider + msg.ResponseConflict.ConflictRelayData1.Request.RelaySession.Provider + strconv.FormatUint(epochStart, 10)
}
func (k msgServer) Detection(goCtx context.Context, msg *types.MsgDetection) (*types.MsgDetectionResponse, error) {
ctx := sdk.UnwrapSDKContext(goCtx)
logger := k.Keeper.Logger(ctx)
clientAddr, err := sdk.AccAddressFromBech32(msg.Creator)
if err != nil {
return nil, utils.LavaFormatWarning("invalid client address", err,
utils.Attribute{Key: "client", Value: msg.Creator},
)
}
if msg.FinalizationConflict != nil && msg.ResponseConflict == nil && msg.SameProviderConflict == nil {
err := k.Keeper.ValidateFinalizationConflict(ctx, msg.FinalizationConflict, clientAddr)
if err != nil {
return nil, utils.LavaFormatWarning("Simulation: invalid finalization conflict detection", err,
utils.Attribute{Key: "client", Value: msg.Creator},
)
}
} else if msg.FinalizationConflict == nil && msg.ResponseConflict == nil && msg.SameProviderConflict != nil {
err := k.Keeper.ValidateSameProviderConflict(ctx, msg.SameProviderConflict, clientAddr)
if err != nil {
return nil, utils.LavaFormatWarning("Simulation: invalid same provider conflict detection", err,
utils.Attribute{Key: "client", Value: msg.Creator},
)
}
} else if msg.FinalizationConflict == nil && msg.ResponseConflict != nil && msg.SameProviderConflict == nil {
err := k.Keeper.ValidateResponseConflict(ctx, msg.ResponseConflict, clientAddr)
if err != nil {
return nil, utils.LavaFormatWarning("Simulation: invalid response conflict detection", err,
utils.Attribute{Key: "client", Value: msg.Creator},
)
}
// the conflict detection transaction is valid!, start a vote
// TODO: 1. start a vote, with vote ID (unique, list index isn't good because its changing, use a map)
// 2. create an event to declare vote
// 3. accept incoming commit transactions for this vote,
// 4. after vote ends, accept reveal transactions, strike down every provider that voted (only valid if there was a commit)
// 5. majority wins, minority gets penalised
epochStart, _, err := k.epochstorageKeeper.GetEpochStartForBlock(ctx, uint64(msg.ResponseConflict.ConflictRelayData0.Request.RelaySession.Epoch))
if err != nil {
return nil, utils.LavaFormatWarning("Simulation: could not get EpochStart for specific block", err,
utils.Attribute{Key: "client", Value: msg.Creator},
utils.Attribute{Key: "provider0", Value: msg.ResponseConflict.ConflictRelayData0.Request.RelaySession.Provider},
utils.Attribute{Key: "provider1", Value: msg.ResponseConflict.ConflictRelayData1.Request.RelaySession.Provider},
)
}
index := DetectionIndex(msg, epochStart)
found := k.Keeper.AllocateNewConflictVote(ctx, index)
if found {
return nil, utils.LavaFormatWarning("Simulation: conflict with is already open for this client and providers in this epoch", err,
utils.Attribute{Key: "client", Value: msg.Creator},
utils.Attribute{Key: "provider0", Value: msg.ResponseConflict.ConflictRelayData0.Request.RelaySession.Provider},
utils.Attribute{Key: "provider1", Value: msg.ResponseConflict.ConflictRelayData1.Request.RelaySession.Provider},
)
}
conflictVote := types.ConflictVote{}
conflictVote.Index = index
conflictVote.VoteState = types.StateCommit
conflictVote.VoteStartBlock = uint64(msg.ResponseConflict.ConflictRelayData0.Request.RelaySession.Epoch)
epochBlocks, err := k.epochstorageKeeper.EpochBlocks(ctx, uint64(ctx.BlockHeight()))
if err != nil {
return nil, utils.LavaFormatError("Simulation: could not get epochblocks", err,
utils.Attribute{Key: "client", Value: msg.Creator},
utils.Attribute{Key: "provider0", Value: msg.ResponseConflict.ConflictRelayData0.Request.RelaySession.Provider},
utils.Attribute{Key: "provider1", Value: msg.ResponseConflict.ConflictRelayData1.Request.RelaySession.Provider},
)
}
voteDeadline, err := k.Keeper.epochstorageKeeper.GetNextEpoch(ctx, uint64(ctx.BlockHeight())+k.VotePeriod(ctx)*epochBlocks)
if err != nil {
return nil, utils.LavaFormatError("Simulation: could not get NextEpoch", err,
utils.Attribute{Key: "client", Value: msg.Creator},
utils.Attribute{Key: "provider0", Value: msg.ResponseConflict.ConflictRelayData0.Request.RelaySession.Provider},
utils.Attribute{Key: "provider1", Value: msg.ResponseConflict.ConflictRelayData1.Request.RelaySession.Provider},
)
}
conflictVote.VoteDeadline = voteDeadline
conflictVote.ApiUrl = msg.ResponseConflict.ConflictRelayData0.Request.RelayData.ApiUrl
conflictVote.ClientAddress = msg.Creator
conflictVote.ChainID = msg.ResponseConflict.ConflictRelayData0.Request.RelaySession.SpecId
conflictVote.RequestBlock = uint64(msg.ResponseConflict.ConflictRelayData0.Request.RelayData.RequestBlock)
conflictVote.RequestData = msg.ResponseConflict.ConflictRelayData0.Request.RelayData.Data
conflictVote.FirstProvider.Account = msg.ResponseConflict.ConflictRelayData0.Request.RelaySession.Provider
conflictVote.FirstProvider.Response = msg.ResponseConflict.ConflictRelayData0.Reply.HashAllDataHash
conflictVote.SecondProvider.Account = msg.ResponseConflict.ConflictRelayData1.Request.RelaySession.Provider
conflictVote.SecondProvider.Response = msg.ResponseConflict.ConflictRelayData1.Reply.HashAllDataHash
conflictVote.Votes = []types.Vote{}
voters := k.Keeper.LotteryVoters(goCtx, epochStart, conflictVote.ChainID, []string{conflictVote.FirstProvider.Account, conflictVote.SecondProvider.Account})
for _, voter := range voters {
conflictVote.Votes = append(conflictVote.Votes, types.Vote{Address: voter, Hash: []byte{}, Result: types.NoVote})
}
metadataBytes, err := json.Marshal(msg.ResponseConflict.ConflictRelayData0.Request.RelayData.Metadata)
if err != nil {
return nil, utils.LavaFormatError("could not marshal metadata in the event", err,
utils.Attribute{Key: "client", Value: msg.Creator},
utils.Attribute{Key: "provider0", Value: msg.ResponseConflict.ConflictRelayData0.Request.RelaySession.Provider},
utils.Attribute{Key: "provider1", Value: msg.ResponseConflict.ConflictRelayData1.Request.RelaySession.Provider},
)
}
k.SetConflictVote(ctx, conflictVote)
eventData := map[string]string{"client": msg.Creator}
eventData["voteID"] = conflictVote.Index
eventData["chainID"] = conflictVote.ChainID
eventData["connectionType"] = msg.ResponseConflict.ConflictRelayData0.Request.RelayData.ConnectionType
eventData["apiURL"] = conflictVote.ApiUrl
eventData["requestData"] = string(conflictVote.RequestData)
eventData["requestBlock"] = strconv.FormatUint(conflictVote.RequestBlock, 10)
eventData["voteDeadline"] = strconv.FormatUint(conflictVote.VoteDeadline, 10)
eventData["voters"] = strings.Join(voters, ",")
eventData["apiInterface"] = msg.ResponseConflict.ConflictRelayData0.Request.RelayData.ApiInterface
eventData["metadata"] = string(metadataBytes)
utils.LogLavaEvent(ctx, logger, types.ConflictVoteDetectionEventName, eventData, "Simulation: Got a new valid conflict detection from consumer, starting new vote")
return &types.MsgDetectionResponse{}, nil
}
eventData := map[string]string{"client": msg.Creator}
utils.LogLavaEvent(ctx, logger, types.ConflictDetectionRecievedEventName, eventData, "Simulation: Got a new valid conflict detection from consumer")
return &types.MsgDetectionResponse{}, nil
}
func (k Keeper) LotteryVoters(goCtx context.Context, epoch uint64, chainID string, exemptions []string) []string {
ctx := sdk.UnwrapSDKContext(goCtx)
entries, err := k.epochstorageKeeper.GetStakeEntryForAllProvidersEpoch(ctx, chainID, epoch)
if err != nil {
return make([]string, 0)
}
freezeFilter := pairingfilters.FrozenProvidersFilter{}
frozenProviders := freezeFilter.Filter(ctx, *entries, epoch) // bool array -> true == not frozen
voters := make([]string, 0)
for i, entry := range *entries {
if !slices.Contains(exemptions, entry.Address) && frozenProviders[i] {
voters = append(voters, entry.Address)
}
}
return voters
}