This repository was archived by the owner on Jan 31, 2025. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 22
/
Copy pathabci.go
178 lines (156 loc) · 6.52 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
166
167
168
169
170
171
172
173
174
175
176
177
178
package abci
import (
abci "github.com/cometbft/cometbft/abci/types"
"github.com/cometbft/cometbft/libs/log"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/baseapp"
"github.com/skip-mev/block-sdk/block"
"github.com/skip-mev/block-sdk/block/proposals"
"github.com/skip-mev/block-sdk/block/utils"
)
type (
// ProposalHandler is a wrapper around the ABCI++ PrepareProposal and ProcessProposal
// handlers.
ProposalHandler struct {
logger log.Logger
txDecoder sdk.TxDecoder
txEncoder sdk.TxEncoder
mempool block.Mempool
useCustomProcessProposal bool
}
)
// NewDefaultProposalHandler returns a new ABCI++ proposal handler. This proposal handler will
// iteratively call each of the lanes in the chain to prepare and process the proposal. This
// will not use custom process proposal logic.
func NewDefaultProposalHandler(
logger log.Logger,
txDecoder sdk.TxDecoder,
txEncoder sdk.TxEncoder,
mempool block.Mempool,
) *ProposalHandler {
return &ProposalHandler{
logger: logger,
txDecoder: txDecoder,
txEncoder: txEncoder,
mempool: mempool,
useCustomProcessProposal: false,
}
}
// New returns a new ABCI++ proposal handler with the ability to use custom process proposal logic.
//
// NOTE: It is highly recommended to use the default proposal handler unless you have a specific
// use case that requires custom process proposal logic.
func New(
logger log.Logger,
txDecoder sdk.TxDecoder,
txEncoder sdk.TxEncoder,
mempool block.Mempool,
useCustomProcessProposal bool,
) *ProposalHandler {
return &ProposalHandler{
logger: logger,
txDecoder: txDecoder,
txEncoder: txEncoder,
mempool: mempool,
useCustomProcessProposal: useCustomProcessProposal,
}
}
// PrepareProposalHandler prepares the proposal by selecting transactions from each lane
// according to each lane's selection logic. We select transactions in the order in which the
// lanes are configured on the chain. Note that each lane has an boundary on the number of
// bytes/gas that can be included in the proposal. By default, the default lane will not have
// a boundary on the number of bytes that can be included in the proposal and will include all
// valid transactions in the proposal (up to MaxBlockSize, MaxGasLimit).
func (h *ProposalHandler) PrepareProposalHandler() sdk.PrepareProposalHandler {
return func(ctx sdk.Context, req abci.RequestPrepareProposal) (resp abci.ResponsePrepareProposal) {
if req.Height <= 1 {
return abci.ResponsePrepareProposal{Txs: req.Txs}
}
// In the case where there is a panic, we recover here and return an empty proposal.
defer func() {
if rec := recover(); rec != nil {
h.logger.Error("failed to prepare proposal", "err", rec)
// TODO: Should we attempt to return a empty proposal here with empty proposal info?
resp = abci.ResponsePrepareProposal{Txs: make([][]byte, 0)}
}
}()
h.logger.Info(
"mempool distribution before proposal creation",
"distribution", h.mempool.GetTxDistribution(),
"height", req.Height,
)
// Get the max gas limit and max block size for the proposal.
_, maxGasLimit := proposals.GetBlockLimits(ctx)
proposal := proposals.NewProposal(h.logger, req.MaxTxBytes, maxGasLimit)
// Fill the proposal with transactions from each lane.
prepareLanesHandler := ChainPrepareLanes(h.mempool.Registry())
finalProposal, err := prepareLanesHandler(ctx, proposal)
if err != nil {
h.logger.Error("failed to prepare proposal", "err", err)
return abci.ResponsePrepareProposal{Txs: make([][]byte, 0)}
}
h.logger.Info(
"prepared proposal",
"num_txs", len(finalProposal.Txs),
"total_tx_bytes", finalProposal.Info.BlockSize,
"max_tx_bytes", finalProposal.Info.MaxBlockSize,
"total_gas_limit", finalProposal.Info.GasLimit,
"max_gas_limit", finalProposal.Info.MaxGasLimit,
"height", req.Height,
)
h.logger.Info(
"mempool distribution after proposal creation",
"distribution", h.mempool.GetTxDistribution(),
"height", req.Height,
)
return abci.ResponsePrepareProposal{
Txs: finalProposal.Txs,
}
}
}
// ProcessProposalHandler processes the proposal by verifying all transactions in the proposal
// according to each lane's verification logic. Proposals are verified similar to how they are
// constructed. After a proposal is processed, it should amount to the same proposal that was prepared.
// The proposal is verified in a greedy fashion, respecting the ordering of lanes. A lane will
// verify all transactions in the proposal that belong to the lane and pass any remaining transactions
// to the next lane in the chain.
func (h *ProposalHandler) ProcessProposalHandler() sdk.ProcessProposalHandler {
if !h.useCustomProcessProposal {
return baseapp.NoOpProcessProposal()
}
return func(ctx sdk.Context, req abci.RequestProcessProposal) (resp abci.ResponseProcessProposal) {
if req.Height <= 1 {
return abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_ACCEPT}
}
// In the case where any of the lanes panic, we recover here and return a reject status.
defer func() {
if rec := recover(); rec != nil {
h.logger.Error("failed to process proposal", "recover_err", rec)
resp = abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_REJECT}
}
}()
// Decode the transactions in the proposal. These will be verified by each lane in a greedy fashion.
decodedTxs, err := utils.GetDecodedTxs(h.txDecoder, req.Txs)
if err != nil {
h.logger.Error("failed to decode txs", "err", err)
return abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_REJECT}
}
// Build handler that will verify the partial proposals according to each lane's verification logic.
processLanesHandler := ChainProcessLanes(h.mempool.Registry())
finalProposal, err := processLanesHandler(ctx, proposals.NewProposalWithContext(ctx, h.logger), decodedTxs)
if err != nil {
h.logger.Error("failed to validate the proposal", "err", err)
return abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_REJECT}
}
h.logger.Info(
"processed proposal",
"num_txs", len(finalProposal.Txs),
"total_tx_bytes", finalProposal.Info.BlockSize,
"max_tx_bytes", finalProposal.Info.MaxBlockSize,
"total_gas_limit", finalProposal.Info.GasLimit,
"max_gas_limit", finalProposal.Info.MaxGasLimit,
"height", req.Height,
)
return abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_ACCEPT}
}
}