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: expose InitChain tx responses #1941

Merged
merged 32 commits into from
Jul 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
7be8225
feat: expose InitChain response
n0izn0iz Apr 17, 2024
667f8bd
Merge branch 'master' into expose-genesis-response
n0izn0iz Apr 17, 2024
9107f45
tmp: start try to store results like in ApplyBlock
n0izn0iz Jun 10, 2024
8bcee48
chore: Merge remote-tracking branch 'origin/master' into expose-genes…
n0izn0iz Jun 10, 2024
8fa79be
feat: save InitChain's ResponseDeliverTxs and add test
n0izn0iz Jun 10, 2024
e4dd0f6
chore: clean
n0izn0iz Jun 10, 2024
d0da512
fix: preserve existing getHeight behavior
n0izn0iz Jun 10, 2024
15e974b
chore: improve comment
n0izn0iz Jun 10, 2024
6c41220
chore: self-contained tests
n0izn0iz Jun 11, 2024
13be10f
chore: remove artifact
n0izn0iz Jun 11, 2024
366d229
chore: use testing utils
n0izn0iz Jun 11, 2024
404fcd5
chore: move MockApplication into testing package
n0izn0iz Jun 12, 2024
926e226
chore: inline asserts
n0izn0iz Jun 12, 2024
d625a9a
Merge branch 'master' into expose-genesis-response
n0izn0iz Jun 12, 2024
f2923e8
Merge branch 'master' into expose-genesis-response
n0izn0iz Jun 12, 2024
a182a3a
chore: update proto
n0izn0iz Jun 12, 2024
0d1a4bd
Merge branch 'master' into expose-genesis-response
n0izn0iz Jun 17, 2024
9d309b7
Merge branch 'master' into expose-genesis-response
n0izn0iz Jun 19, 2024
eda735b
Merge branch 'master' into expose-genesis-response
n0izn0iz Jun 19, 2024
c5357f1
Merge branch 'master' into expose-genesis-response
n0izn0iz Jun 19, 2024
d684021
Merge branch 'master' into expose-genesis-response
n0izn0iz Jul 17, 2024
87fe7d8
chore: use t.Cleanup
n0izn0iz Jul 17, 2024
8b33ffc
chore: use cast instead of generic
n0izn0iz Jul 17, 2024
bad682a
chore: add getHeight test
n0izn0iz Jul 17, 2024
409cd9d
Merge branch 'master' into expose-genesis-response
n0izn0iz Jul 17, 2024
5cec713
Merge branch 'master' into expose-genesis-response
n0izn0iz Jul 19, 2024
ecdf0f2
Merge branch 'master' into expose-genesis-response
n0izn0iz Jul 19, 2024
973a73f
Merge branch 'master' into expose-genesis-response
n0izn0iz Jul 22, 2024
110da7d
chore: revert getHeight
n0izn0iz Jul 22, 2024
01dc6f8
chore: remove MockApplication
n0izn0iz Jul 22, 2024
e649de5
chore: SaveABCIResponses internal usage note
n0izn0iz Jul 22, 2024
83dd5a0
Merge branch 'master' into expose-genesis-response
n0izn0iz Jul 22, 2024
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
11 changes: 10 additions & 1 deletion gno.land/pkg/gnoland/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,8 @@
resHandler GenesisTxHandler,
) func(sdk.Context, abci.RequestInitChain) abci.ResponseInitChain {
return func(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain {
txResponses := []abci.ResponseDeliverTx{}

Check warning on line 196 in gno.land/pkg/gnoland/app.go

View check run for this annotation

Codecov / codecov/patch

gno.land/pkg/gnoland/app.go#L196

Added line #L196 was not covered by tests

if req.AppState != nil {
// Get genesis state
genState := req.AppState.(GnoGenesisState)
Expand All @@ -219,13 +221,20 @@
)
}

txResponses = append(txResponses, abci.ResponseDeliverTx{
ResponseBase: res.ResponseBase,
GasWanted: res.GasWanted,
GasUsed: res.GasUsed,
})

Check warning on line 228 in gno.land/pkg/gnoland/app.go

View check run for this annotation

Codecov / codecov/patch

gno.land/pkg/gnoland/app.go#L224-L228

Added lines #L224 - L228 were not covered by tests

resHandler(ctx, tx, res)
}
}

// Done!
return abci.ResponseInitChain{
Validators: req.Validators,
Validators: req.Validators,
TxResponses: txResponses,

Check warning on line 237 in gno.land/pkg/gnoland/app.go

View check run for this annotation

Codecov / codecov/patch

gno.land/pkg/gnoland/app.go#L236-L237

Added lines #L236 - L237 were not covered by tests
}
}
}
Expand Down
1 change: 1 addition & 0 deletions tm2/pkg/bft/abci/types/abci.proto
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ message ResponseInitChain {
ResponseBase response_base = 1 [json_name = "ResponseBase"];
ConsensusParams consensus_params = 2 [json_name = "ConsensusParams"];
repeated ValidatorUpdate validators = 3 [json_name = "Validators"];
repeated ResponseDeliverTx tx_responses = 4 [json_name = "TxResponses"];
}

message ResponseQuery {
Expand Down
1 change: 1 addition & 0 deletions tm2/pkg/bft/abci/types/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ type ResponseInitChain struct {
ResponseBase
ConsensusParams *ConsensusParams
Validators []ValidatorUpdate
TxResponses []ResponseDeliverTx
zivkovicmilos marked this conversation as resolved.
Show resolved Hide resolved
}

type ResponseQuery struct {
Expand Down
7 changes: 7 additions & 0 deletions tm2/pkg/bft/consensus/replay.go
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,13 @@ func (h *Handshaker) ReplayBlocks(
return nil, err
}

// Save the results by height
abciResponse := sm.NewABCIResponsesFromNum(int64(len(res.TxResponses)))
copy(abciResponse.DeliverTxs, res.TxResponses)
sm.SaveABCIResponses(h.stateDB, 0, abciResponse)

// NOTE: we don't save results by tx hash since the transactions are in the AppState opaque type
zivkovicmilos marked this conversation as resolved.
Show resolved Hide resolved

if stateBlockHeight == 0 { // we only update state when we are in initial state
// If the app returned validators or consensus params, update the state.
if len(res.Validators) > 0 {
Expand Down
63 changes: 48 additions & 15 deletions tm2/pkg/bft/consensus/replay_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1131,11 +1131,19 @@ func TestHandshakeUpdatesValidators(t *testing.T) {

val, _ := types.RandValidator(true, 10)
vals := types.NewValidatorSet([]*types.Validator{val})
app := &initChainApp{vals: vals.ABCIValidatorUpdates()}
appVals := vals.ABCIValidatorUpdates()
// returns the vals on InitChain
app := initChainApp{
initChain: func(req abci.RequestInitChain) abci.ResponseInitChain {
return abci.ResponseInitChain{
Validators: appVals,
}
},
}
clientCreator := proxy.NewLocalClientCreator(app)

config, genesisFile := ResetConfig("handshake_test_")
defer os.RemoveAll(config.RootDir)
t.Cleanup(func() { require.NoError(t, os.RemoveAll(config.RootDir)) })
stateDB, state, store := makeStateAndStore(config, genesisFile, "v0.0.0-test")

oldValAddr := state.Validators.Validators[0].Address
Expand All @@ -1144,13 +1152,9 @@ func TestHandshakeUpdatesValidators(t *testing.T) {
genDoc, _ := sm.MakeGenesisDocFromFile(genesisFile)
handshaker := NewHandshaker(stateDB, state, store, genDoc)
proxyApp := appconn.NewAppConns(clientCreator)
if err := proxyApp.Start(); err != nil {
t.Fatalf("Error starting proxy app connections: %v", err)
}
defer proxyApp.Stop()
if err := handshaker.Handshake(proxyApp); err != nil {
t.Fatalf("Error on abci handshake: %v", err)
}
require.NoError(t, proxyApp.Start(), "Error starting proxy app connections")
t.Cleanup(func() { require.NoError(t, proxyApp.Stop()) })
require.NoError(t, handshaker.Handshake(proxyApp), "Error on abci handshake")

// reload the state, check the validator set was updated
state = sm.LoadState(stateDB)
Expand All @@ -1161,14 +1165,43 @@ func TestHandshakeUpdatesValidators(t *testing.T) {
assert.Equal(t, newValAddr, expectValAddr)
}

// returns the vals on InitChain
func TestHandshakeGenesisResponseDeliverTx(t *testing.T) {
t.Parallel()

const numInitResponses = 42

app := initChainApp{
initChain: func(req abci.RequestInitChain) abci.ResponseInitChain {
return abci.ResponseInitChain{
TxResponses: make([]abci.ResponseDeliverTx, numInitResponses),
}
},
}
clientCreator := proxy.NewLocalClientCreator(app)

config, genesisFile := ResetConfig("handshake_test_")
t.Cleanup(func() { require.NoError(t, os.RemoveAll(config.RootDir)) })
stateDB, state, store := makeStateAndStore(config, genesisFile, "v0.0.0-test")

// now start the app using the handshake - it should sync
genDoc, _ := sm.MakeGenesisDocFromFile(genesisFile)
handshaker := NewHandshaker(stateDB, state, store, genDoc)
proxyApp := appconn.NewAppConns(clientCreator)
require.NoError(t, proxyApp.Start(), "Error starting proxy app connections")
t.Cleanup(func() { require.NoError(t, proxyApp.Stop()) })
require.NoError(t, handshaker.Handshake(proxyApp), "Error on abci handshake")

// check that the genesis transaction results are saved
res, err := sm.LoadABCIResponses(stateDB, 0)
require.NoError(t, err, "Failed to load genesis ABCI responses")
assert.Len(t, res.DeliverTxs, numInitResponses)
}

type initChainApp struct {
abci.BaseApplication
vals []abci.ValidatorUpdate
initChain func(req abci.RequestInitChain) abci.ResponseInitChain
}

func (ica *initChainApp) InitChain(req abci.RequestInitChain) abci.ResponseInitChain {
return abci.ResponseInitChain{
Validators: ica.vals,
}
func (m initChainApp) InitChain(req abci.RequestInitChain) abci.ResponseInitChain {
return m.initChain(req)
}
10 changes: 7 additions & 3 deletions tm2/pkg/bft/rpc/core/blocks.go
Original file line number Diff line number Diff line change
Expand Up @@ -400,7 +400,7 @@
// ```
func BlockResults(ctx *rpctypes.Context, heightPtr *int64) (*ctypes.ResultBlockResults, error) {
storeHeight := blockStore.Height()
height, err := getHeight(storeHeight, heightPtr)
height, err := getHeightWithMin(storeHeight, heightPtr, 0)

Check warning on line 403 in tm2/pkg/bft/rpc/core/blocks.go

View check run for this annotation

Codecov / codecov/patch

tm2/pkg/bft/rpc/core/blocks.go#L403

Added line #L403 was not covered by tests
if err != nil {
return nil, err
}
Expand All @@ -418,10 +418,14 @@
}

func getHeight(currentHeight int64, heightPtr *int64) (int64, error) {
return getHeightWithMin(currentHeight, heightPtr, 1)
}

func getHeightWithMin(currentHeight int64, heightPtr *int64, min int64) (int64, error) {
if heightPtr != nil {
height := *heightPtr
if height <= 0 {
return 0, fmt.Errorf("height must be greater than 0")
if height < min {
return 0, fmt.Errorf("height must be greater than or equal to %d", min)
ajnavarro marked this conversation as resolved.
Show resolved Hide resolved
}
if height > currentHeight {
ajnavarro marked this conversation as resolved.
Show resolved Hide resolved
return 0, fmt.Errorf("height must be less than or equal to the current blockchain height")
Expand Down
37 changes: 37 additions & 0 deletions tm2/pkg/bft/rpc/core/blocks_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,40 @@ func TestBlockchainInfo(t *testing.T) {
}
}
}

func TestGetHeight(t *testing.T) {
t.Parallel()

cases := []struct {
currentHeight int64
heightPtr *int64
min int64
res int64
wantErr bool
}{
// height >= min
{42, int64Ptr(0), 0, 0, false},
{42, int64Ptr(1), 0, 1, false},

// height < min
{42, int64Ptr(0), 1, 0, true},

// nil height
{42, nil, 1, 42, false},
}

for i, c := range cases {
caseString := fmt.Sprintf("test %d failed", i)
res, err := getHeightWithMin(c.currentHeight, c.heightPtr, c.min)
if c.wantErr {
require.Error(t, err, caseString)
} else {
require.NoError(t, err, caseString)
require.Equal(t, res, c.res, caseString)
}
}
}

func int64Ptr(v int64) *int64 {
return &v
}
6 changes: 3 additions & 3 deletions tm2/pkg/bft/state/execution.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,10 +106,10 @@ func (blockExec *BlockExecutor) ApplyBlock(state State, blockID types.BlockID, b

fail.Fail() // XXX

// Save the results before we commit.
saveABCIResponses(blockExec.db, block.Height, abciResponses)
// Save the results by height
SaveABCIResponses(blockExec.db, block.Height, abciResponses)

// Save the transaction results
// Save the results by tx hash
for index, tx := range block.Txs {
saveTxResultIndex(
blockExec.db,
Expand Down
6 changes: 0 additions & 6 deletions tm2/pkg/bft/state/export_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,6 @@ func CalcValidatorsKey(height int64) []byte {
return calcValidatorsKey(height)
}

// SaveABCIResponses is an alias for the private saveABCIResponses method in
// store.go, exported exclusively and explicitly for testing.
func SaveABCIResponses(db dbm.DB, height int64, abciResponses *ABCIResponses) {
saveABCIResponses(db, height, abciResponses)
}

// SaveConsensusParamsInfo is an alias for the private saveConsensusParamsInfo
// method in store.go, exported exclusively and explicitly for testing.
func SaveConsensusParamsInfo(db dbm.DB, nextHeight, changeHeight int64, params abci.ConsensusParams) {
Expand Down
12 changes: 9 additions & 3 deletions tm2/pkg/bft/state/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,8 +131,13 @@ type ABCIResponses struct {

// NewABCIResponses returns a new ABCIResponses
func NewABCIResponses(block *types.Block) *ABCIResponses {
resDeliverTxs := make([]abci.ResponseDeliverTx, block.NumTxs)
if block.NumTxs == 0 {
return NewABCIResponsesFromNum(block.NumTxs)
}

// NewABCIResponsesFromNum returns a new ABCIResponses with a set number of txs
func NewABCIResponsesFromNum(numTxs int64) *ABCIResponses {
resDeliverTxs := make([]abci.ResponseDeliverTx, numTxs)
if numTxs == 0 {
// This makes Amino encoding/decoding consistent.
resDeliverTxs = nil
}
Expand Down Expand Up @@ -175,7 +180,8 @@ func LoadABCIResponses(db dbm.DB, height int64) (*ABCIResponses, error) {
// SaveABCIResponses persists the ABCIResponses to the database.
// This is useful in case we crash after app.Commit and before s.Save().
// Responses are indexed by height so they can also be loaded later to produce Merkle proofs.
func saveABCIResponses(db dbm.DB, height int64, abciResponses *ABCIResponses) {
// NOTE: this should only be used internally by the bft package and subpackages.
func SaveABCIResponses(db dbm.DB, height int64, abciResponses *ABCIResponses) {
thehowl marked this conversation as resolved.
Show resolved Hide resolved
db.Set(CalcABCIResponsesKey(height), abciResponses.Bytes())
}

Expand Down
Loading