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 a block validity rule that all PFB transactions must have valid signatures #1300

Merged
merged 24 commits into from
Jan 31, 2023
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
fe08277
feat!: check signatures for PFB txs during processproposal
evan-forbes Jan 22, 2023
f14f1c7
feat: add new testing function to create txs using the app
evan-forbes Jan 25, 2023
799a065
fix: update remaining tests and cache context
evan-forbes Jan 26, 2023
b8b5235
chore: revert uneccessary test helper
evan-forbes Jan 26, 2023
53b0bd2
chore: add extra unit test and clean up
evan-forbes Jan 26, 2023
827faa3
Merge branch 'main' into evan/pfb-sig-check
evan-forbes Jan 26, 2023
7c1fafe
chore: use final version of the sdk
evan-forbes Jan 26, 2023
7eae040
feat!: account for multiple txs from the same signer in both Prepare …
evan-forbes Jan 27, 2023
520faf7
chore: add appropriate state access now that we are verifying signatu…
evan-forbes Jan 27, 2023
28cb6fa
chore: add appropriate state to remaining prepare proposal tests
evan-forbes Jan 27, 2023
8c8239e
feat!: catch panic when filtering txs
evan-forbes Jan 28, 2023
697bdd5
refactor: combine tests so that the old ones use the new required state
evan-forbes Jan 29, 2023
4db2681
chore: add test for filtering in prepareproposal
evan-forbes Jan 29, 2023
3af3536
Merge branch 'main' into evan/pfb-sig-check
evan-forbes Jan 29, 2023
9999b61
chore: add a replace statement for nmt since we haven't downgraded ac…
evan-forbes Jan 29, 2023
539f384
chore: update names
evan-forbes Jan 29, 2023
2db0ee9
chore: linter
evan-forbes Jan 29, 2023
8192fc0
chore: add test case to ensure panic is caught
evan-forbes Jan 29, 2023
f45d4a9
chore: docs wording
evan-forbes Jan 30, 2023
47b36a8
chore: docs
evan-forbes Jan 30, 2023
d508e9e
chore: use consistent naming for antehandlers
evan-forbes Jan 30, 2023
c50e7b8
chore: use seq instead of is
evan-forbes Jan 30, 2023
b40bb7a
Merge branch 'main' into evan/pfb-sig-check
evan-forbes Jan 30, 2023
7f157b9
Merge branch 'main' into evan/pfb-sig-check
evan-forbes Jan 31, 2023
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
1 change: 1 addition & 0 deletions app/estimate_square_size_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ func Test_estimateSquareSize_MultiBlob(t *testing.T) {
enc.TxConfig.TxEncoder(),
signer,
tt.getBlobSizes(),
0, 0,
)
normalTxs, blobTxs := separateTxs(enc.TxConfig, shares.TxsToBytes(txs))
resSquareSize, resStart := estimateSquareSize(normalTxs, blobTxs)
Expand Down
66 changes: 66 additions & 0 deletions app/parse_txs.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ package app

import (
"github.com/cosmos/cosmos-sdk/client"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/tendermint/tendermint/libs/log"
core "github.com/tendermint/tendermint/proto/tendermint/types"
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
coretypes "github.com/tendermint/tendermint/types"
)

Expand All @@ -20,3 +23,66 @@ func separateTxs(txcfg client.TxConfig, rawTxs [][]byte) ([][]byte, []core.BlobT
}
return normalTxs, blobTxs
}

// filterStdTxs applies the provided antehandler to each transaction and removes
// transactions that return an error. Panics are caught by the checkTxValidity
// function used to apply the ante handler.
func filterStdTxs(logger log.Logger, dec sdk.TxDecoder, ctx sdk.Context, handler sdk.AnteHandler, txs [][]byte) ([][]byte, sdk.Context) {
Comment on lines +27 to +30
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't add unit tests for these, since those tests are essentially identical to the TestPrepareProposalFiltering, in both what we test and structure of the test itself

n := 0
var err error
for _, tx := range txs {
ctx, err = checkTxValidity(logger, dec, ctx, handler, tx)
// either the transaction is invalid (ie incorrect nonce) and we
// simply want to remove this tx, or we're catching a panic from one
// of the anteHanders which is logged.
if err != nil {
continue
}
txs[n] = tx
n++

}

return txs[:n], ctx
}

// filterStdTxsWith applies the provided antehandler to each transaction
evan-forbes marked this conversation as resolved.
Show resolved Hide resolved
// and removes transactions that return an error. Panics are caught by the checkTxValidity
// function used to apply the ante handler.
func filterBlobTxs(logger log.Logger, dec sdk.TxDecoder, ctx sdk.Context, handler sdk.AnteHandler, txs []tmproto.BlobTx) ([]tmproto.BlobTx, sdk.Context) {
n := 0
var err error
for _, tx := range txs {
ctx, err = checkTxValidity(logger, dec, ctx, handler, tx.Tx)
// either the transaction is invalid (ie incorrect nonce) and we
// simply want to remove this tx, or we're catching a panic from one
// of the anteHanders which is logged.
if err != nil {
continue
}
txs[n] = tx
n++

}

return txs[:n], ctx
}

func checkTxValidity(logger log.Logger, dec sdk.TxDecoder, ctx sdk.Context, handler sdk.AnteHandler, tx []byte) (sdk.Context, error) {
// catch panics from anteHandlers
defer func() {
if r := recover(); r != nil {
err := recoverHandler(r)
if err != nil {
logger.Error(err.Error())
}
}
}()

sdkTx, err := dec(tx)
if err != nil {
return ctx, err
}

return handler(ctx, sdkTx, false)
}
16 changes: 16 additions & 0 deletions app/prepare_proposal.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,22 @@ func (app *App) PrepareProposal(req abci.RequestPrepareProposal) abci.ResponsePr
// the txs is maintained.
normalTxs, blobTxs := separateTxs(app.txConfig, req.BlockData.Txs)

sdkCtx, err := app.NewProcessProposalQueryContext()
if err != nil {
panic(err)
}

// increment the sequences of the standard cosmos-sdk transactions. Panics
// from the anteHandler are caught and logged.
isHandler := incrementSequenceAnteHandler(&app.AccountKeeper)
normalTxs, sdkCtx = filterStdTxs(app.Logger(), app.txConfig.TxDecoder(), sdkCtx, isHandler, normalTxs)
evan-forbes marked this conversation as resolved.
Show resolved Hide resolved

// check the signatures and increment the sequences of the blob transations,
// and filter out any that fail. Panics from the anteHandler are caught and
// logged.
svHandler := sigVerifyAnteHandler(&app.AccountKeeper, app.txConfig)
blobTxs, _ = filterBlobTxs(app.Logger(), app.txConfig.TxDecoder(), sdkCtx, svHandler, blobTxs)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here is where we add stateful checks to prepare proposal. notice that we are incrementing the nonce of the normal txs first before we are checking the signatures of the PFB txs.


// estimate the square size. This estimation errs on the side of larger
// squares but can only return values within the min and max square size.
squareSize, nonreservedStart := estimateSquareSize(normalTxs, blobTxs)
Expand Down
36 changes: 35 additions & 1 deletion app/process_proposal.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,22 @@ func (app *App) ProcessProposal(req abci.RequestProcessProposal) abci.ResponsePr
}
}

// iterate over all of the MsgPayForBlobs transactions and ensure that their
// create the anteHanders that are used to check the validity of
// transactions. We verify the signatures of PFB containing txs using the
// sigVerifyAnterHandler, and simply increase the nonce of all other
// transactions.
sigVerifyAnteHander := sigVerifyAnteHandler(&app.AccountKeeper, app.txConfig)
incrementSequenceAnteHandler := incrementSequenceAnteHandler(&app.AccountKeeper)

sdkCtx, err := app.NewProcessProposalQueryContext()
if err != nil {
logInvalidPropBlockError(app.Logger(), req.Header, "failure to load query context", err)
return abci.ResponseProcessProposal{
Result: abci.ResponseProcessProposal_REJECT,
}
}

// iterate over all of the MsgPayForBlob transactions and ensure that their
// commitments are subtree roots of the data root.
for _, rawTx := range req.BlockData.Txs {
tx := rawTx
Expand All @@ -105,6 +120,17 @@ func (app *App) ProcessProposal(req abci.RequestProcessProposal) abci.ResponsePr

pfb, has := hasPFB(sdkTx.GetMsgs())
if !has {
// we need to increment the sequence for every transaction so that
// the signature check below is accurate. this error only gets hit
// if the account in question doens't exist.
sdkCtx, err = incrementSequenceAnteHandler(sdkCtx, sdkTx, false)
if err != nil {
logInvalidPropBlockError(app.Logger(), req.Header, "failure to incrememnt sequence", err)
return abci.ResponseProcessProposal{
Result: abci.ResponseProcessProposal_REJECT,
}
}

// we do not need to perform further checks on this transaction,
// since it has no PFB
continue
Expand Down Expand Up @@ -149,6 +175,14 @@ func (app *App) ProcessProposal(req abci.RequestProcessProposal) abci.ResponsePr
}
}
}

sdkCtx, err = sigVerifyAnteHander(sdkCtx, sdkTx, true)
if err != nil {
logInvalidPropBlockError(app.Logger(), req.Header, "invalid PFB signature", err)
return abci.ResponseProcessProposal{
Result: abci.ResponseProcessProposal_REJECT,
}
}
}

return abci.ResponseProcessProposal{
Expand Down
74 changes: 44 additions & 30 deletions app/test/fuzz_abci_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ import (
"github.com/celestiaorg/celestia-app/app"
"github.com/celestiaorg/celestia-app/app/encoding"
"github.com/celestiaorg/celestia-app/testutil"
"github.com/celestiaorg/celestia-app/testutil/blobfactory"
"github.com/stretchr/testify/require"
abci "github.com/tendermint/tendermint/abci/types"
tmrand "github.com/tendermint/tendermint/libs/rand"
core "github.com/tendermint/tendermint/proto/tendermint/types"
coretypes "github.com/tendermint/tendermint/types"
)
Expand All @@ -25,54 +25,68 @@ func TestPrepareProposalConsistency(t *testing.T) {
t.Skip("skipping TestPrepareProposalConsistency in short mode.")
}
encConf := encoding.MakeConfig(app.ModuleEncodingRegisters...)
testApp, _ := testutil.SetupTestAppWithGenesisValSet()
accounts := make([]string, 1100) // 1000 for creating blob txs, 100 for creating send txs
for i := range accounts {
accounts[i] = tmrand.Str(20)
}

testApp, kr := testutil.SetupTestAppWithGenesisValSet(accounts...)

type test struct {
name string
count, blobCount, size int
}
tests := []test{
{"many small single share single blob transactions", 10000, 1, 400},
{"many small single share single blob transactions", 1000, 1, 400},
Comment on lines -35 to +40
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reduced the length of this first test because it takes so long that sometimes we don't even run it once. changing this means that, at least in this test, are no longer testing for the weird things that can happen when using a bunch of small PFBs occasionally. We might still want to do that in the future, but I'm less worried now that we are limiting the number of txs to 5000.

{"one hundred normal sized single blob transactions", 100, 1, 400000},
{"many single share multi-blob transactions", 1000, 100, 400},
{"one hundred normal sized multi-blob transactions", 100, 4, 400000},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
timer := time.After(time.Second * 30)
timer := time.After(time.Second * 20)
for {
select {
case <-timer:
return
default:
ProcessRandomProposal(t, tt.count, tt.size, tt.blobCount, encConf, testApp)
txs := testutil.RandBlobTxsWithAccounts(
t,
testApp,
encConf.TxConfig.TxEncoder(),
kr,
tt.size,
tt.count,
true,
"",
accounts[:tt.count],
)
// create 100 send transactions
sendTxs := testutil.SendTxsWithAccounts(
t,
testApp,
encConf.TxConfig.TxEncoder(),
kr,
1000,
accounts[0],
accounts[len(accounts)-100:],
"",
)
Comment on lines +64 to +74
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this refactor was needed cause now we need access to state to query for account info before we can create the txs

txs = append(txs, sendTxs...)
resp := testApp.PrepareProposal(abci.RequestPrepareProposal{
BlockData: &core.Data{
Txs: coretypes.Txs(txs).ToSliceOfBytes(),
},
})
res := testApp.ProcessProposal(abci.RequestProcessProposal{
BlockData: resp.BlockData,
Header: core.Header{
DataHash: resp.BlockData.Hash,
},
})
require.Equal(t, abci.ResponseProcessProposal_ACCEPT, res.Result)
}
}
})
}
}

func ProcessRandomProposal(
t *testing.T,
count,
maxSize int,
maxBlobCount int,
cfg encoding.Config,
capp *app.App,
) {
txs := blobfactory.RandBlobTxsRandomlySized(cfg.TxConfig.TxEncoder(), count, maxSize, maxBlobCount)
sendTxs := blobfactory.GenerateManyRawSendTxs(cfg.TxConfig, count)
txs = append(txs, sendTxs...)
resp := capp.PrepareProposal(abci.RequestPrepareProposal{
BlockData: &core.Data{
Txs: coretypes.Txs(txs).ToSliceOfBytes(),
},
})
res := capp.ProcessProposal(abci.RequestProcessProposal{
BlockData: resp.BlockData,
Header: core.Header{
DataHash: resp.BlockData.Hash,
},
})
require.Equal(t, abci.ResponseProcessProposal_ACCEPT, res.Result)
}
6 changes: 3 additions & 3 deletions app/test/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ func (s *IntegrationTestSuite) SetupSuite() {
}
s.T().Log("setting up integration test suite")

numAccounts := 100
numAccounts := 120
s.accounts = make([]string, numAccounts)
for i := 0; i < numAccounts; i++ {
s.accounts[i] = tmrand.Str(20)
Expand Down Expand Up @@ -115,7 +115,7 @@ func (s *IntegrationTestSuite) TestMaxBlockSize() {
3,
false,
s.cfg.ChainID,
s.accounts[:20],
s.accounts[20:40],
)
}

Expand All @@ -132,7 +132,7 @@ func (s *IntegrationTestSuite) TestMaxBlockSize() {
8,
true,
s.cfg.ChainID,
s.accounts[:80],
s.accounts[40:120],
)
}

Expand Down
Loading