Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add options + retaining oracle data in proposals [BLO-840] #90

Merged
merged 5 commits into from
Feb 8, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions abci/proposals/options.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package proposals

// Option is a function that enables optional configuration of the ProposalHandler.
type Option func(*ProposalHandler)

// RetainOracleDataInWrappedProposalHandler returns an Option that configs the ProposalHandler
// to pass the injected extend-commit-info to the wrapped proposal handler.
func RetainOracleDataInWrappedProposalHandler() Option {
nivasan1 marked this conversation as resolved.
Show resolved Hide resolved
return func(p *ProposalHandler) {
p.retainOracleDataInWrappedHandler = true
}
}
31 changes: 25 additions & 6 deletions abci/proposals/proposals.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,15 @@ type ProposalHandler struct {
// currencyPairStrategy is the strategy used to determine the price information
// from a given oracle vote extension.
currencyPairStrategy currencypair.CurrencyPairStrategy

// metrics is responsible for reporting / aggregating consensus-specific
// metrics for this validator.
metrics servicemetrics.Metrics

// retainOracleDataInWrappedHandler is a flag that determines whether the
// proposal handler should pass the injected extended commit info to the
// wrapped proposal handler.
retainOracleDataInWrappedHandler bool
}

// NewProposalHandler returns a new ProposalHandler.
Expand All @@ -64,8 +70,9 @@ func NewProposalHandler(
extendedCommitInfoCodec codec.ExtendedCommitCodec,
currencyPairStrategy currencypair.CurrencyPairStrategy,
metrics servicemetrics.Metrics,
opts ...Option,
) *ProposalHandler {
return &ProposalHandler{
handler := &ProposalHandler{
logger: logger,
prepareProposalHandler: prepareProposalHandler,
processProposalHandler: processProposalHandler,
Expand All @@ -75,6 +82,13 @@ func NewProposalHandler(
currencyPairStrategy: currencyPairStrategy,
metrics: metrics,
}

// apply options
for _, opt := range opts {
opt(handler)
}

return handler
}

// PrepareProposalHandler returns a PrepareProposalHandler that will be called
Expand Down Expand Up @@ -151,11 +165,9 @@ func (h *ProposalHandler) PrepareProposalHandler() sdk.PrepareProposalHandler {

return &cometabci.ResponsePrepareProposal{Txs: make([][]byte, 0)}, err
}
// Inject our VE Tx to the req Txs, we want to do this before h.prepareProposalHandler(ctx, req) so that
// the wrapped application can have access to the injected VE tx.
// Adjust req.MaxTxBytes to account for extInfoBzSize so that the wrapped-proposal handler does not reap too many txs from the mempool
extInfoBzSize := int64(len(extInfoBz))
if extInfoBzSize < req.MaxTxBytes {
req.Txs = append([][]byte{extInfoBz}, req.Txs...)
// Reserve bytes for our VE Tx
req.MaxTxBytes -= extInfoBzSize
} else {
Expand All @@ -164,6 +176,11 @@ func (h *ProposalHandler) PrepareProposalHandler() sdk.PrepareProposalHandler {
"MaxTxBytes", req.MaxTxBytes)
extInfoBz = []byte{}
}

// determine whether the wrapped prepare proposal handler should retain the extended commit info
if h.retainOracleDataInWrappedHandler {
req.Txs = append([][]byte{extInfoBz}, req.Txs...) // prepend the VE Tx
}
}

// Build the proposal. Get the duration that the wrapped prepare proposal handler executed for.
Expand Down Expand Up @@ -308,8 +325,10 @@ func (h *ProposalHandler) ProcessProposalHandler() sdk.ProcessProposalHandler {
// observe the size of the extended commit info
h.metrics.ObserveMessageSize(servicemetrics.ExtendedCommit, len(extCommitBz))

// Process the transactions in the proposal with the oracle data removed.
req.Txs = req.Txs[slinkyabci.NumInjectedTxs:]
// Remove the extended commit info from the proposal if required
if !h.retainOracleDataInWrappedHandler {
req.Txs = req.Txs[slinkyabci.NumInjectedTxs:]
}
}

// call the wrapped process-proposal
Expand Down
105 changes: 105 additions & 0 deletions abci/proposals/proposals_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -529,6 +529,111 @@ func (s *ProposalsTestSuite) TestPrepareProposal() {
}
}

func (s *ProposalsTestSuite) TestPrepareProposalRetainOracleData() {
// If retain option is given we feed oracle-data into prepare / process
s.Run("test RetainOracleDataInWrappedProposalHandler", func() {
codec := codec.NewDefaultExtendedCommitCodec()

extendedCommit := cometabci.ExtendedCommitInfo{
Round: 1,
}
bz, err := codec.Encode(extendedCommit)
s.Require().NoError(err)

handler := proposals.NewProposalHandler(
log.NewNopLogger(),
func(ctx sdk.Context, rpp *cometabci.RequestPrepareProposal) (*cometabci.ResponsePrepareProposal, error) {
// assert that the oracle data is retained
s.Require().Equal(bz, rpp.Txs[types.OracleInfoIndex])
return &cometabci.ResponsePrepareProposal{}, nil
},
func(ctx sdk.Context, rpp *cometabci.RequestProcessProposal) (*cometabci.ResponseProcessProposal, error) {
// assert that the oracle data is retained
s.Require().Equal(bz, rpp.Txs[types.OracleInfoIndex])
return &cometabci.ResponseProcessProposal{}, nil
},
ve.NoOpValidateVoteExtensions,
nil,
codec,
nil,
servicemetrics.NewNopMetrics(),
proposals.RetainOracleDataInWrappedProposalHandler(),
)

// enable VE
s.ctx = testutils.CreateBaseSDKContext(s.T())
s.ctx = testutils.UpdateContextWithVEHeight(s.ctx, 3)
s.ctx = s.ctx.WithBlockHeight(4)

// prepare proposal
req := &cometabci.RequestPrepareProposal{
LocalLastCommit: extendedCommit,
MaxTxBytes: 100, // arbitrary
}

_, err = handler.PrepareProposalHandler()(s.ctx, req)
s.Require().NoError(err)

// process proposal
req2 := &cometabci.RequestProcessProposal{
Txs: [][]byte{bz},
}
_, err = handler.ProcessProposalHandler()(s.ctx, req2)
s.Require().NoError(err)
})

// Otherwise, we don't
s.Run("test that oracle-data is not passed if not RetainOracleDataInWrappedProposalHandler", func() {
codec := codec.NewDefaultExtendedCommitCodec()

extendedCommit := cometabci.ExtendedCommitInfo{
Round: 1,
}
bz, err := codec.Encode(extendedCommit)
s.Require().NoError(err)

handler := proposals.NewProposalHandler(
log.NewNopLogger(),
func(ctx sdk.Context, rpp *cometabci.RequestPrepareProposal) (*cometabci.ResponsePrepareProposal, error) {
// assert that the oracle data is retained
s.Require().Len(rpp.Txs, 0)
return &cometabci.ResponsePrepareProposal{}, nil
},
func(ctx sdk.Context, rpp *cometabci.RequestProcessProposal) (*cometabci.ResponseProcessProposal, error) {
// assert that the oracle data is retained
s.Require().Len(rpp.Txs, 0)
return &cometabci.ResponseProcessProposal{}, nil
},
ve.NoOpValidateVoteExtensions,
nil,
codec,
nil,
servicemetrics.NewNopMetrics(),
)

// enable VE
s.ctx = testutils.CreateBaseSDKContext(s.T())
s.ctx = testutils.UpdateContextWithVEHeight(s.ctx, 3)
s.ctx = s.ctx.WithBlockHeight(4)

// prepare proposal
req := &cometabci.RequestPrepareProposal{
LocalLastCommit: extendedCommit,
MaxTxBytes: 100, // arbitrary
}

_, err = handler.PrepareProposalHandler()(s.ctx, req)
s.Require().NoError(err)

// process proposal
req2 := &cometabci.RequestProcessProposal{
Txs: [][]byte{bz},
}
_, err = handler.ProcessProposalHandler()(s.ctx, req2)
s.Require().NoError(err)
})
}

func (s *ProposalsTestSuite) TestProcessProposal() {
testCases := []struct {
name string
Expand Down
Loading