-
Notifications
You must be signed in to change notification settings - Fork 36
/
cpayload.go
157 lines (130 loc) · 4.38 KB
/
cpayload.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
package keeper
import (
"bytes"
"context"
"sort"
"github.com/omni-network/omni/halo/attest/types"
"github.com/omni-network/omni/lib/errors"
"github.com/omni-network/omni/lib/log"
"github.com/omni-network/omni/lib/xchain"
evmenginetypes "github.com/omni-network/omni/octane/evmengine/types"
abci "github.com/cometbft/cometbft/abci/types"
cmtproto "github.com/cometbft/cometbft/proto/tendermint/types"
"github.com/cosmos/cosmos-sdk/baseapp"
sdk "github.com/cosmos/cosmos-sdk/types"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
"github.com/cosmos/gogoproto/proto"
)
var _ evmenginetypes.VoteExtensionProvider = (*Keeper)(nil)
func (k *Keeper) PrepareVotes(ctx context.Context, commit abci.ExtendedCommitInfo) ([]sdk.Msg, error) {
sdkCtx := sdk.UnwrapSDKContext(ctx)
if err := baseapp.ValidateVoteExtensions(sdkCtx, k.skeeper, sdkCtx.BlockHeight(), sdkCtx.ChainID(), commit); err != nil {
log.Error(ctx, "Cannot include invalid vote extensions in payload", err, "height", sdkCtx.BlockHeight())
return nil, nil
}
msg, err := votesFromLastCommit(
ctx,
k.windowCompare,
k.portalRegistry.SupportedChain,
commit,
)
if err != nil {
return nil, err
}
return []sdk.Msg{msg}, nil
}
type windowCompareFunc func(context.Context, xchain.ChainVersion, uint64) (int, error)
type supportedChainFunc func(context.Context, uint64) (bool, error)
// votesFromLastCommit returns the aggregated votes contained in vote extensions
// of the last local commit.
func votesFromLastCommit(
ctx context.Context,
windowCompare windowCompareFunc,
supportedChain supportedChainFunc,
info abci.ExtendedCommitInfo,
) (*types.MsgAddVotes, error) {
var allVotes []*types.Vote
for _, vote := range info.Votes {
if vote.BlockIdFlag != cmtproto.BlockIDFlagCommit {
continue // Skip non block votes
}
votes, ok, err := votesFromExtension(vote.VoteExtension)
if err != nil {
return nil, err
} else if !ok {
continue
}
var selected []*types.Vote
for _, v := range votes.Votes {
if ok, err := supportedChain(ctx, v.BlockHeader.ChainId); err != nil {
return nil, err
} else if !ok {
// Skip votes for unsupported chains.
continue
}
cmp, err := windowCompare(ctx, v.BlockHeader.XChainVersion(), v.BlockHeader.Offset)
if err != nil {
return nil, err
} else if cmp != 0 {
// Skip votes that are not in the current window anymore.
continue
}
selected = append(selected, v)
}
allVotes = append(allVotes, selected...)
}
return &types.MsgAddVotes{
Authority: authtypes.NewModuleAddress(types.ModuleName).String(),
Votes: aggregateVotes(allVotes),
}, nil
}
// aggregateVotes aggregates the provided attestations by block header.
func aggregateVotes(votes []*types.Vote) []*types.AggVote {
uniqueAggs := make(map[[32]byte]*types.AggVote)
for _, vote := range votes {
key := vote.UniqueKey()
agg, ok := uniqueAggs[key]
if !ok {
agg = &types.AggVote{
BlockHeader: vote.BlockHeader,
AttestationRoot: vote.AttestationRoot,
}
}
agg.Signatures = append(agg.Signatures, vote.Signature)
uniqueAggs[key] = agg
}
return sortAggregates(flattenAggs(uniqueAggs))
}
// flattenAggs returns the values of the provided map.
func flattenAggs(aggsByHeader map[[32]byte]*types.AggVote) []*types.AggVote {
aggs := make([]*types.AggVote, 0, len(aggsByHeader))
for _, agg := range aggsByHeader {
aggs = append(aggs, agg)
}
return aggs
}
// sortAggregates returns the provided aggregates in a deterministic order.
// Note the provided slice is also sorted in-place.
func sortAggregates(aggs []*types.AggVote) []*types.AggVote {
sort.Slice(aggs, func(i, j int) bool {
if aggs[i].BlockHeader.Offset != aggs[j].BlockHeader.Offset {
return aggs[i].BlockHeader.Offset < aggs[j].BlockHeader.Offset
}
if aggs[i].BlockHeader.ChainId != aggs[j].BlockHeader.ChainId {
return aggs[i].BlockHeader.ChainId < aggs[j].BlockHeader.ChainId
}
return bytes.Compare(aggs[i].BlockHeader.Hash, aggs[j].BlockHeader.Hash) < 0
})
return aggs
}
// votesFromExtension returns the attestations contained in the vote extension, or false if none or an error.
func votesFromExtension(voteExtension []byte) (*types.Votes, bool, error) {
if len(voteExtension) == 0 {
return nil, false, nil
}
resp := new(types.Votes)
if err := proto.Unmarshal(voteExtension, resp); err != nil {
return nil, false, errors.Wrap(err, "decode vote extension")
}
return resp, true, nil
}