-
Notifications
You must be signed in to change notification settings - Fork 53
/
abci.go
165 lines (139 loc) · 5.15 KB
/
abci.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
162
163
164
165
package keyshare
import (
"fmt"
"github.com/Fairblock/fairyring/blockbuster"
"github.com/Fairblock/fairyring/blockbuster/utils"
sdk "github.com/cosmos/cosmos-sdk/types"
)
// PrepareLane will attempt to select the keyshare transactions that are valid
// and include them in the proposal. It will return an empty partial proposal
// if no valid keyshare transactions are found.
func (l *KeyShareLane) PrepareLane(
ctx sdk.Context,
proposal blockbuster.BlockProposal,
maxTxBytes int64,
next blockbuster.PrepareLanesHandler,
) (blockbuster.BlockProposal, error) {
// Define all of the info we need to select transactions for the partial proposal.
var (
keyshareTxs [][]byte
txsToRemove = make(map[sdk.Tx]struct{}, 0)
)
// Attempt to select the valid keyshare txs
keyshareTxIterator := l.Mempool.Select(ctx, nil)
selectKeyshareTxLoop:
for ; keyshareTxIterator != nil; keyshareTxIterator = keyshareTxIterator.Next() {
cacheCtx, write := ctx.CacheContext()
tmpKeyshareTx := keyshareTxIterator.Tx()
keyshareTxBz, hash, err := utils.GetTxHashStr(l.Cfg.TxEncoder, tmpKeyshareTx)
if err != nil {
txsToRemove[tmpKeyshareTx] = struct{}{}
continue selectKeyshareTxLoop
}
// if the transaction is already in the (partial) block proposal, we skip it.
if proposal.Contains(keyshareTxBz) {
continue selectKeyshareTxLoop
}
keyshareTxSize := int64(len(keyshareTxBz))
if keyshareTxSize <= maxTxBytes {
// Verify the keyshare transaction
if err := l.VerifyTx(cacheCtx, tmpKeyshareTx); err != nil {
l.Logger().Info(
"failed to verify aggregate keyshare tx",
"tx_hash", hash,
"err", err,
)
txsToRemove[tmpKeyshareTx] = struct{}{}
continue selectKeyshareTxLoop
}
// Build the partial proposal by selecting the keyshare transaction
keyshareInfo, err := l.GetKeyShareInfo(tmpKeyshareTx)
if keyshareInfo == nil || err != nil {
txsToRemove[tmpKeyshareTx] = struct{}{}
continue selectKeyshareTxLoop
}
// At this point, and all the keyshare transactions are valid.
// So we select them bid and also mark these transactions as seen and
// update the total size selected thus far.
keyshareTxs = append(keyshareTxs, keyshareTxBz)
// Write the cache context to the original context when we know we have a
// valid top of block bundle.
write()
} else {
l.Cfg.Logger.Info(
"failed to select keyshare tx for lane; tx size is too large",
"tx_size", keyshareTxSize,
"max_size", maxTxBytes,
)
}
}
// Remove all transactions that were invalid during the creation of the partial proposal.
if err := utils.RemoveTxsFromLane(txsToRemove, l.Mempool); err != nil {
return proposal, err
}
// Update the proposal with the selected transactions. This will only return an error
// if the invarient checks are not passed. In the case when this errors, the original proposal
// will be returned (without the selected transactions from this lane).
if err := proposal.UpdateProposal(l, keyshareTxs); err != nil {
return proposal, err
}
return next(ctx, proposal)
}
// ProcessLane will ensure that block proposals that include transactions from
// the keyshare lane are valid.
func (l *KeyShareLane) ProcessLane(ctx sdk.Context, txs []sdk.Tx, next blockbuster.ProcessLanesHandler) (sdk.Context, error) {
var countKeyshareTxs = 0
for _, keyshareTx := range txs {
if !l.Match(keyshareTx) {
return next(ctx, txs[countKeyshareTxs:])
}
_, err := l.GetKeyShareInfo(keyshareTx)
if err != nil {
return ctx, fmt.Errorf("failed to get keyshare info for lane %s: %w", l.Name(), err)
}
if err := l.VerifyTx(ctx, keyshareTx); err != nil {
return ctx, fmt.Errorf("invalid keyshare tx: %w", err)
}
countKeyshareTxs = countKeyshareTxs + 1
}
return next(ctx, txs[countKeyshareTxs:])
}
// ProcessLaneBasic ensures that if keyshare transactions are present in a proposal,
// - they are the first transaction in the partial proposal
// - there are no other aggregate keyshare transactions in the proposal
func (l *KeyShareLane) ProcessLaneBasic(txs []sdk.Tx) error {
// If there are keyshare transaction, they must be the first transactions in the block proposal.
for index, keyshareTx := range txs {
if !l.Match(keyshareTx) {
for _, tx := range txs[index:] {
if l.Match(tx) {
return fmt.Errorf("misplaced keyshare transactions in lane %s", l.Name())
}
}
}
}
return nil
}
// VerifyTx will verify that the keyshare transaction is valid.
// It will return an error if the transaction is invalid.
func (l *KeyShareLane) VerifyTx(ctx sdk.Context, keyshareTx sdk.Tx) error {
_, err := l.GetKeyShareInfo(keyshareTx)
if err != nil {
return fmt.Errorf("failed to get keyshare info: %w", err)
}
// verify the keyshare transaction
_, err = l.verifyTx(ctx, keyshareTx)
if err != nil {
return fmt.Errorf("invalid keyshare tx; failed to execute ante handler: %w", err)
}
return nil
}
// verifyTx will execute the ante handler on the transaction and return the
// resulting context and error.
func (l *KeyShareLane) verifyTx(ctx sdk.Context, tx sdk.Tx) (sdk.Context, error) {
if l.Cfg.AnteHandler != nil {
newCtx, err := l.Cfg.AnteHandler(ctx, tx, false)
return newCtx, err
}
return ctx, nil
}