From c25f7769a673a6bac898ae2c419d8529f6b8562c Mon Sep 17 00:00:00 2001 From: William Banfield Date: Wed, 9 Mar 2022 16:25:47 -0500 Subject: [PATCH] remove Must* proof and hash functions --- abci/types/types.go | 21 +++---- abci/types/types_test.go | 9 ++- internal/rpc/core/tx.go | 24 +++++--- internal/state/execution.go | 8 ++- internal/state/state_test.go | 103 ++++++++++++++++++++++++++++------- light/rpc/client.go | 6 +- types/tx.go | 18 ------ types/tx_test.go | 103 ----------------------------------- 8 files changed, 123 insertions(+), 169 deletions(-) diff --git a/abci/types/types.go b/abci/types/types.go index ba7fd0d0d..a6197948b 100644 --- a/abci/types/types.go +++ b/abci/types/types.go @@ -8,7 +8,6 @@ import ( "github.com/gogo/protobuf/jsonpb" - "github.com/tendermint/tendermint/crypto/merkle" types "github.com/tendermint/tendermint/proto/tendermint/types" ) @@ -171,16 +170,6 @@ func RespondVerifyVoteExtension(ok bool) ResponseVerifyVoteExtension { } } -func MustHashResults(r []*ExecTxResult) []byte { - return merkle.HashFromByteSlices(mustResultsToByteSlices(r)) -} - -// ProveResult returns a merkle proof of one result from the set -func MustProveResult(r []*ExecTxResult, i int) merkle.Proof { - _, proofs := merkle.ProofsFromByteSlices(mustResultsToByteSlices(r)) - return *proofs[i] -} - // deterministicExecTxResult strips non-deterministic fields from // ResponseDeliverTx and returns another ResponseDeliverTx. func deterministicExecTxResult(response *ExecTxResult) *ExecTxResult { @@ -192,17 +181,21 @@ func deterministicExecTxResult(response *ExecTxResult) *ExecTxResult { } } -func mustResultsToByteSlices(r []*ExecTxResult) [][]byte { +// TxResultsToByteSlices encodes the the TxResults as a list of byte +// slices. It strips off the non-deterministic pieces of the TxResults +// so that the resulting data can be used for hash comparisons and used +// in Merkle proofs. +func TxResultsToByteSlices(r []*ExecTxResult) ([][]byte, error) { s := make([][]byte, len(r)) for i, e := range r { d := deterministicExecTxResult(e) b, err := d.Marshal() if err != nil { - panic(err) + return nil, err } s[i] = b } - return s + return s, nil } func (tr *TxRecord) IsIncluded() bool { diff --git a/abci/types/types_test.go b/abci/types/types_test.go index 84290f2a1..be16a4145 100644 --- a/abci/types/types_test.go +++ b/abci/types/types_test.go @@ -8,6 +8,7 @@ import ( "github.com/stretchr/testify/require" abci "github.com/tendermint/tendermint/abci/types" + "github.com/tendermint/tendermint/crypto/merkle" ) func TestHashAndProveResults(t *testing.T) { @@ -28,15 +29,17 @@ func TestHashAndProveResults(t *testing.T) { require.Equal(t, bz0, bz1) // Make sure that we can get a root hash from results and verify proofs. - root := abci.MustHashResults(trs) + rs, err := abci.TxResultsToByteSlices(trs) + require.NoError(t, err) + root := merkle.HashFromByteSlices(rs) assert.NotEmpty(t, root) + _, proofs := merkle.ProofsFromByteSlices(rs) for i, tr := range trs { bz, err := tr.Marshal() require.NoError(t, err) - proof := abci.MustProveResult(trs, i) - valid := proof.Verify(root, bz) + valid := proofs[i].Verify(root, bz) assert.NoError(t, valid, "%d", i) } } diff --git a/internal/rpc/core/tx.go b/internal/rpc/core/tx.go index 126875d0d..48501f148 100644 --- a/internal/rpc/core/tx.go +++ b/internal/rpc/core/tx.go @@ -6,6 +6,7 @@ import ( "fmt" "sort" + "github.com/tendermint/tendermint/crypto/merkle" tmquery "github.com/tendermint/tendermint/internal/pubsub/query" "github.com/tendermint/tendermint/internal/state/indexer" "github.com/tendermint/tendermint/libs/bytes" @@ -36,19 +37,21 @@ func (env *Environment) Tx(ctx context.Context, hash bytes.HexBytes, prove bool) return nil, fmt.Errorf("tx (%X) not found, err: %w", hash, err) } - height := r.Height - index := r.Index - var proof types.TxProof if prove { - block := env.BlockStore.LoadBlock(height) - proof = block.Data.Txs.Proof(int(index)) // XXX: overflow on 32-bit machines + block := env.BlockStore.LoadBlock(r.Height) + root, proofs := merkle.ProofsFromByteSlices(block.Data.Txs.ToSliceOfBytes()) + proof = types.TxProof{ + RootHash: root, + Proof: *proofs[int(r.Index)], + Data: block.Data.Txs[int(r.Index)], + } } return &coretypes.ResultTx{ Hash: hash, - Height: height, - Index: index, + Height: r.Height, + Index: r.Index, TxResult: r.Result, Tx: r.Tx, Proof: proof, @@ -127,7 +130,12 @@ func (env *Environment) TxSearch( var proof types.TxProof if prove { block := env.BlockStore.LoadBlock(r.Height) - proof = block.Data.Txs.Proof(int(r.Index)) // XXX: overflow on 32-bit machines + root, proofs := merkle.ProofsFromByteSlices(block.Data.Txs.ToSliceOfBytes()) + proof = types.TxProof{ + RootHash: root, + Proof: *proofs[int(r.Index)], + Data: block.Data.Txs[int(r.Index)], + } } apiResults = append(apiResults, &coretypes.ResultTx{ diff --git a/internal/state/execution.go b/internal/state/execution.go index 127004abf..d50ab6044 100644 --- a/internal/state/execution.go +++ b/internal/state/execution.go @@ -8,6 +8,7 @@ import ( abciclient "github.com/tendermint/tendermint/abci/client" abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/crypto/encoding" + "github.com/tendermint/tendermint/crypto/merkle" "github.com/tendermint/tendermint/internal/eventbus" "github.com/tendermint/tendermint/internal/mempool" "github.com/tendermint/tendermint/libs/log" @@ -261,7 +262,12 @@ func (blockExec *BlockExecutor) ApplyBlock( } // Update the state with the block and responses. - state, err = state.Update(blockID, &block.Header, abci.MustHashResults(finalizeBlockResponse.TxResults), finalizeBlockResponse.ConsensusParamUpdates, validatorUpdates) + rs, err := abci.TxResultsToByteSlices(finalizeBlockResponse.TxResults) + if err != nil { + return state, fmt.Errorf("marshaling TxResults: %w", err) + } + h := merkle.HashFromByteSlices(rs) + state, err = state.Update(blockID, &block.Header, h, finalizeBlockResponse.ConsensusParamUpdates, validatorUpdates) if err != nil { return state, fmt.Errorf("commit failed for application: %w", err) } diff --git a/internal/state/state_test.go b/internal/state/state_test.go index a682d7824..592d79161 100644 --- a/internal/state/state_test.go +++ b/internal/state/state_test.go @@ -18,6 +18,7 @@ import ( "github.com/tendermint/tendermint/config" "github.com/tendermint/tendermint/crypto/ed25519" "github.com/tendermint/tendermint/crypto/encoding" + "github.com/tendermint/tendermint/crypto/merkle" sm "github.com/tendermint/tendermint/internal/state" statefactory "github.com/tendermint/tendermint/internal/state/test/factory" tmstate "github.com/tendermint/tendermint/proto/tendermint/state" @@ -204,7 +205,13 @@ func TestABCIResponsesSaveLoad2(t *testing.T) { res, err := stateStore.LoadABCIResponses(h) if assert.NoError(t, err, "%d", i) { t.Log(res) - assert.Equal(t, abci.MustHashResults(tc.expected), abci.MustHashResults(res.FinalizeBlock.TxResults), "%d", i) + e, err := abci.TxResultsToByteSlices(tc.expected) + require.NoError(t, err) + he := merkle.HashFromByteSlices(e) + rs, err := abci.TxResultsToByteSlices(res.FinalizeBlock.TxResults) + hrs := merkle.HashFromByteSlices(rs) + require.NoError(t, err) + assert.Equal(t, he, hrs, "%d", i) } } } @@ -270,9 +277,12 @@ func TestOneValidatorChangesSaveLoad(t *testing.T) { header, blockID, responses := makeHeaderPartsResponsesValPowerChange(t, state, power) validatorUpdates, err = types.PB2TM.ValidatorUpdates(responses.FinalizeBlock.ValidatorUpdates) require.NoError(t, err) - state, err = state.Update(blockID, &header, abci.MustHashResults(responses.FinalizeBlock.TxResults), responses.FinalizeBlock.ConsensusParamUpdates, validatorUpdates) + rs, err := abci.TxResultsToByteSlices(responses.FinalizeBlock.TxResults) + require.NoError(t, err) + h := merkle.HashFromByteSlices(rs) + state, err = state.Update(blockID, &header, h, responses.FinalizeBlock.ConsensusParamUpdates, validatorUpdates) require.NoError(t, err) - err := stateStore.Save(state) + err = stateStore.Save(state) require.NoError(t, err) } @@ -452,7 +462,10 @@ func TestProposerPriorityDoesNotGetResetToZero(t *testing.T) { } validatorUpdates, err := types.PB2TM.ValidatorUpdates(fb.ValidatorUpdates) require.NoError(t, err) - updatedState, err := state.Update(blockID, &block.Header, abci.MustHashResults(fb.TxResults), fb.ConsensusParamUpdates, validatorUpdates) + rs, err := abci.TxResultsToByteSlices(fb.TxResults) + require.NoError(t, err) + h := merkle.HashFromByteSlices(rs) + updatedState, err := state.Update(blockID, &block.Header, h, fb.ConsensusParamUpdates, validatorUpdates) assert.NoError(t, err) curTotal := val1VotingPower // one increment step and one validator: 0 + power - total_power == 0 @@ -467,7 +480,10 @@ func TestProposerPriorityDoesNotGetResetToZero(t *testing.T) { updateAddVal := abci.ValidatorUpdate{PubKey: fvp, Power: val2VotingPower} validatorUpdates, err = types.PB2TM.ValidatorUpdates([]abci.ValidatorUpdate{updateAddVal}) assert.NoError(t, err) - updatedState2, err := updatedState.Update(blockID, &block.Header, abci.MustHashResults(fb.TxResults), fb.ConsensusParamUpdates, validatorUpdates) + rs, err = abci.TxResultsToByteSlices(fb.TxResults) + require.NoError(t, err) + h = merkle.HashFromByteSlices(rs) + updatedState2, err := updatedState.Update(blockID, &block.Header, h, fb.ConsensusParamUpdates, validatorUpdates) assert.NoError(t, err) require.Equal(t, len(updatedState2.NextValidators.Validators), 2) @@ -506,7 +522,10 @@ func TestProposerPriorityDoesNotGetResetToZero(t *testing.T) { // this will cause the diff of priorities (77) // to be larger than threshold == 2*totalVotingPower (22): - updatedState3, err := updatedState2.Update(blockID, &block.Header, abci.MustHashResults(fb.TxResults), fb.ConsensusParamUpdates, validatorUpdates) + rs, err = abci.TxResultsToByteSlices(fb.TxResults) + require.NoError(t, err) + h = merkle.HashFromByteSlices(rs) + updatedState3, err := updatedState2.Update(blockID, &block.Header, h, fb.ConsensusParamUpdates, validatorUpdates) assert.NoError(t, err) require.Equal(t, len(updatedState3.NextValidators.Validators), 2) @@ -569,7 +588,10 @@ func TestProposerPriorityProposerAlternates(t *testing.T) { validatorUpdates, err := types.PB2TM.ValidatorUpdates(fb.ValidatorUpdates) require.NoError(t, err) - updatedState, err := state.Update(blockID, &block.Header, abci.MustHashResults(fb.TxResults), fb.ConsensusParamUpdates, validatorUpdates) + rs, err := abci.TxResultsToByteSlices(fb.TxResults) + require.NoError(t, err) + h := merkle.HashFromByteSlices(rs) + updatedState, err := state.Update(blockID, &block.Header, h, fb.ConsensusParamUpdates, validatorUpdates) assert.NoError(t, err) // 0 + 10 (initial prio) - 10 (avg) - 10 (mostest - total) = -10 @@ -586,7 +608,10 @@ func TestProposerPriorityProposerAlternates(t *testing.T) { validatorUpdates, err = types.PB2TM.ValidatorUpdates([]abci.ValidatorUpdate{updateAddVal}) assert.NoError(t, err) - updatedState2, err := updatedState.Update(blockID, &block.Header, abci.MustHashResults(fb.TxResults), fb.ConsensusParamUpdates, validatorUpdates) + rs, err = abci.TxResultsToByteSlices(fb.TxResults) + require.NoError(t, err) + h = merkle.HashFromByteSlices(rs) + updatedState2, err := updatedState.Update(blockID, &block.Header, h, fb.ConsensusParamUpdates, validatorUpdates) assert.NoError(t, err) require.Equal(t, len(updatedState2.NextValidators.Validators), 2) @@ -629,7 +654,10 @@ func TestProposerPriorityProposerAlternates(t *testing.T) { validatorUpdates, err = types.PB2TM.ValidatorUpdates(fb.ValidatorUpdates) require.NoError(t, err) - updatedState3, err := updatedState2.Update(blockID, &block.Header, abci.MustHashResults(fb.TxResults), fb.ConsensusParamUpdates, validatorUpdates) + rs, err = abci.TxResultsToByteSlices(fb.TxResults) + require.NoError(t, err) + h = merkle.HashFromByteSlices(rs) + updatedState3, err := updatedState2.Update(blockID, &block.Header, h, fb.ConsensusParamUpdates, validatorUpdates) assert.NoError(t, err) assert.Equal(t, updatedState3.Validators.Proposer.Address, updatedState3.NextValidators.Proposer.Address) @@ -671,7 +699,10 @@ func TestProposerPriorityProposerAlternates(t *testing.T) { validatorUpdates, err = types.PB2TM.ValidatorUpdates(fb.ValidatorUpdates) require.NoError(t, err) - oldState, err = oldState.Update(blockID, &block.Header, abci.MustHashResults(fb.TxResults), fb.ConsensusParamUpdates, validatorUpdates) + rs, err = abci.TxResultsToByteSlices(fb.TxResults) + require.NoError(t, err) + h = merkle.HashFromByteSlices(rs) + oldState, err = oldState.Update(blockID, &block.Header, h, fb.ConsensusParamUpdates, validatorUpdates) assert.NoError(t, err) expectedVal1Prio2 = 1 expectedVal2Prio2 = -1 @@ -686,7 +717,10 @@ func TestProposerPriorityProposerAlternates(t *testing.T) { validatorUpdates, err = types.PB2TM.ValidatorUpdates(fb.ValidatorUpdates) require.NoError(t, err) - updatedState, err := oldState.Update(blockID, &block.Header, abci.MustHashResults(fb.TxResults), fb.ConsensusParamUpdates, validatorUpdates) + rs, err := abci.TxResultsToByteSlices(fb.TxResults) + require.NoError(t, err) + h := merkle.HashFromByteSlices(rs) + updatedState, err := oldState.Update(blockID, &block.Header, h, fb.ConsensusParamUpdates, validatorUpdates) assert.NoError(t, err) // alternate (and cyclic priorities): assert.NotEqual( @@ -748,7 +782,10 @@ func TestLargeGenesisValidator(t *testing.T) { require.NoError(t, err) blockID := types.BlockID{Hash: block.Hash(), PartSetHeader: bps.Header()} - updatedState, err := oldState.Update(blockID, &block.Header, abci.MustHashResults(fb.TxResults), fb.ConsensusParamUpdates, validatorUpdates) + rs, err := abci.TxResultsToByteSlices(fb.TxResults) + require.NoError(t, err) + h := merkle.HashFromByteSlices(rs) + updatedState, err := oldState.Update(blockID, &block.Header, h, fb.ConsensusParamUpdates, validatorUpdates) require.NoError(t, err) // no changes in voting power (ProposerPrio += VotingPower == Voting in 1st round; than shiftByAvg == 0, // than -Total == -Voting) @@ -779,7 +816,10 @@ func TestLargeGenesisValidator(t *testing.T) { require.NoError(t, err) blockID := types.BlockID{Hash: block.Hash(), PartSetHeader: bps.Header()} - updatedState, err := oldState.Update(blockID, &block.Header, abci.MustHashResults(fb.TxResults), fb.ConsensusParamUpdates, validatorUpdates) + rs, err := abci.TxResultsToByteSlices(fb.TxResults) + require.NoError(t, err) + h := merkle.HashFromByteSlices(rs) + updatedState, err := oldState.Update(blockID, &block.Header, h, fb.ConsensusParamUpdates, validatorUpdates) require.NoError(t, err) lastState := updatedState @@ -798,7 +838,10 @@ func TestLargeGenesisValidator(t *testing.T) { blockID := types.BlockID{Hash: block.Hash(), PartSetHeader: bps.Header()} - updatedStateInner, err := lastState.Update(blockID, &block.Header, abci.MustHashResults(fb.TxResults), fb.ConsensusParamUpdates, validatorUpdates) + rs, err := abci.TxResultsToByteSlices(fb.TxResults) + require.NoError(t, err) + h := merkle.HashFromByteSlices(rs) + updatedStateInner, err := lastState.Update(blockID, &block.Header, h, fb.ConsensusParamUpdates, validatorUpdates) require.NoError(t, err) lastState = updatedStateInner } @@ -832,7 +875,10 @@ func TestLargeGenesisValidator(t *testing.T) { require.NoError(t, err) blockID := types.BlockID{Hash: block.Hash(), PartSetHeader: bps.Header()} - state, err = state.Update(blockID, &block.Header, abci.MustHashResults(fb.TxResults), fb.ConsensusParamUpdates, validatorUpdates) + rs, err := abci.TxResultsToByteSlices(fb.TxResults) + require.NoError(t, err) + h := merkle.HashFromByteSlices(rs) + state, err = state.Update(blockID, &block.Header, h, fb.ConsensusParamUpdates, validatorUpdates) require.NoError(t, err) } require.Equal(t, 10+2, len(state.NextValidators.Validators)) @@ -854,7 +900,10 @@ func TestLargeGenesisValidator(t *testing.T) { blockID = types.BlockID{Hash: block.Hash(), PartSetHeader: bps.Header()} validatorUpdates, err = types.PB2TM.ValidatorUpdates(fb.ValidatorUpdates) require.NoError(t, err) - updatedState, err = state.Update(blockID, &block.Header, abci.MustHashResults(fb.TxResults), fb.ConsensusParamUpdates, validatorUpdates) + rs, err = abci.TxResultsToByteSlices(fb.TxResults) + require.NoError(t, err) + h = merkle.HashFromByteSlices(rs) + updatedState, err = state.Update(blockID, &block.Header, h, fb.ConsensusParamUpdates, validatorUpdates) require.NoError(t, err) // only the first added val (not the genesis val) should be left assert.Equal(t, 11, len(updatedState.NextValidators.Validators)) @@ -876,7 +925,10 @@ func TestLargeGenesisValidator(t *testing.T) { require.NoError(t, err) blockID = types.BlockID{Hash: block.Hash(), PartSetHeader: bps.Header()} - curState, err = curState.Update(blockID, &block.Header, abci.MustHashResults(fb.TxResults), fb.ConsensusParamUpdates, validatorUpdates) + rs, err := abci.TxResultsToByteSlices(fb.TxResults) + require.NoError(t, err) + h := merkle.HashFromByteSlices(rs) + curState, err = curState.Update(blockID, &block.Header, h, fb.ConsensusParamUpdates, validatorUpdates) require.NoError(t, err) if !bytes.Equal(curState.Validators.Proposer.Address, curState.NextValidators.Proposer.Address) { isProposerUnchanged = false @@ -905,7 +957,10 @@ func TestLargeGenesisValidator(t *testing.T) { blockID := types.BlockID{Hash: block.Hash(), PartSetHeader: bps.Header()} - updatedState, err = updatedState.Update(blockID, &block.Header, abci.MustHashResults(fb.TxResults), fb.ConsensusParamUpdates, validatorUpdates) + rs, err := abci.TxResultsToByteSlices(fb.TxResults) + require.NoError(t, err) + h := merkle.HashFromByteSlices(rs) + updatedState, err = updatedState.Update(blockID, &block.Header, h, fb.ConsensusParamUpdates, validatorUpdates) require.NoError(t, err) if i > numVals { // expect proposers to cycle through after the first iteration (of numVals blocks): if proposers[i%numVals] == nil { @@ -964,7 +1019,10 @@ func TestManyValidatorChangesSaveLoad(t *testing.T) { var validatorUpdates []*types.Validator validatorUpdates, err = types.PB2TM.ValidatorUpdates(responses.FinalizeBlock.ValidatorUpdates) require.NoError(t, err) - state, err = state.Update(blockID, &header, abci.MustHashResults(responses.FinalizeBlock.TxResults), responses.FinalizeBlock.ConsensusParamUpdates, validatorUpdates) + rs, err := abci.TxResultsToByteSlices(responses.FinalizeBlock.TxResults) + require.NoError(t, err) + h := merkle.HashFromByteSlices(rs) + state, err = state.Update(blockID, &header, h, responses.FinalizeBlock.ConsensusParamUpdates, validatorUpdates) require.NoError(t, err) nextHeight := state.LastBlockHeight + 1 err = stateStore.Save(state) @@ -1041,10 +1099,13 @@ func TestConsensusParamsChangesSaveLoad(t *testing.T) { header, blockID, responses := makeHeaderPartsResponsesParams(t, state, &cp) validatorUpdates, err = types.PB2TM.ValidatorUpdates(responses.FinalizeBlock.ValidatorUpdates) require.NoError(t, err) - state, err = state.Update(blockID, &header, abci.MustHashResults(responses.FinalizeBlock.TxResults), responses.FinalizeBlock.ConsensusParamUpdates, validatorUpdates) + rs, err := abci.TxResultsToByteSlices(responses.FinalizeBlock.TxResults) + require.NoError(t, err) + h := merkle.HashFromByteSlices(rs) + state, err = state.Update(blockID, &header, h, responses.FinalizeBlock.ConsensusParamUpdates, validatorUpdates) require.NoError(t, err) - err := stateStore.Save(state) + err = stateStore.Save(state) require.NoError(t, err) } diff --git a/light/rpc/client.go b/light/rpc/client.go index 81ea8957d..de6168e3b 100644 --- a/light/rpc/client.go +++ b/light/rpc/client.go @@ -459,7 +459,11 @@ func (c *Client) BlockResults(ctx context.Context, height *int64) (*coretypes.Re } // Build a Merkle tree out of the slice. - rH := merkle.HashFromByteSlices([][]byte{bbeBytes, abci.MustHashResults(res.TxsResults)}) + rs, err := abci.TxResultsToByteSlices(res.TxsResults) + if err != nil { + return nil, err + } + rH := merkle.HashFromByteSlices(append([][]byte{bbeBytes}, rs...)) // Verify block results. if !bytes.Equal(rH, trustedBlock.LastResultsHash) { diff --git a/types/tx.go b/types/tx.go index c763e58c5..659093059 100644 --- a/types/tx.go +++ b/types/tx.go @@ -62,24 +62,6 @@ func (txs Txs) IndexByHash(hash []byte) int { return -1 } -// Proof returns a simple merkle proof for this node. -// Panics if i < 0 or i >= len(txs) -// TODO: optimize this! -func (txs Txs) Proof(i int) TxProof { - l := len(txs) - bzs := make([][]byte, l) - for i := 0; i < l; i++ { - bzs[i] = txs[i].Hash() - } - root, proofs := merkle.ProofsFromByteSlices(bzs) - - return TxProof{ - RootHash: root, - Data: txs[i], - Proof: *proofs[i], - } -} - // ToSliceOfBytes converts a Txs to slice of byte slices. // // NOTE: This method should become obsolete once Txs is switched to [][]byte. diff --git a/types/tx_test.go b/types/tx_test.go index e2f12772a..1366b8802 100644 --- a/types/tx_test.go +++ b/types/tx_test.go @@ -1,16 +1,12 @@ package types import ( - "bytes" mrand "math/rand" "testing" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - ctest "github.com/tendermint/tendermint/internal/libs/test" tmrand "github.com/tendermint/tendermint/libs/rand" - tmproto "github.com/tendermint/tendermint/proto/tendermint/types" ) func makeTxs(cnt, size int) Txs { @@ -51,102 +47,3 @@ func TestTxIndexByHash(t *testing.T) { assert.Equal(t, -1, txs.IndexByHash(Tx("foodnwkf").Hash())) } } - -func TestValidTxProof(t *testing.T) { - cases := []struct { - txs Txs - }{ - {Txs{{1, 4, 34, 87, 163, 1}}}, - {Txs{{5, 56, 165, 2}, {4, 77}}}, - {Txs{Tx("foo"), Tx("bar"), Tx("baz")}}, - {makeTxs(20, 5)}, - {makeTxs(7, 81)}, - {makeTxs(61, 15)}, - } - - for h, tc := range cases { - txs := tc.txs - root := txs.Hash() - // make sure valid proof for every tx - for i := range txs { - tx := []byte(txs[i]) - proof := txs.Proof(i) - assert.EqualValues(t, i, proof.Proof.Index, "%d: %d", h, i) - assert.EqualValues(t, len(txs), proof.Proof.Total, "%d: %d", h, i) - assert.EqualValues(t, root, proof.RootHash, "%d: %d", h, i) - assert.EqualValues(t, tx, proof.Data, "%d: %d", h, i) - assert.EqualValues(t, txs[i].Hash(), proof.Leaf(), "%d: %d", h, i) - assert.Nil(t, proof.Validate(root), "%d: %d", h, i) - assert.NotNil(t, proof.Validate([]byte("foobar")), "%d: %d", h, i) - - // read-write must also work - var ( - p2 TxProof - pb2 tmproto.TxProof - ) - pbProof := proof.ToProto() - bin, err := pbProof.Marshal() - require.NoError(t, err) - - err = pb2.Unmarshal(bin) - require.NoError(t, err) - - p2, err = TxProofFromProto(pb2) - if assert.NoError(t, err, "%d: %d: %+v", h, i, err) { - assert.Nil(t, p2.Validate(root), "%d: %d", h, i) - } - } - } -} - -func TestTxProofUnchangable(t *testing.T) { - // run the other test a bunch... - for i := 0; i < 40; i++ { - testTxProofUnchangable(t) - } -} - -func testTxProofUnchangable(t *testing.T) { - // make some proof - txs := makeTxs(randInt(2, 100), randInt(16, 128)) - root := txs.Hash() - i := randInt(0, len(txs)-1) - proof := txs.Proof(i) - - // make sure it is valid to start with - assert.Nil(t, proof.Validate(root)) - pbProof := proof.ToProto() - bin, err := pbProof.Marshal() - require.NoError(t, err) - - // try mutating the data and make sure nothing breaks - for j := 0; j < 500; j++ { - bad := ctest.MutateByteSlice(bin) - if !bytes.Equal(bad, bin) { - assertBadProof(t, root, bad, proof) - } - } -} - -// This makes sure that the proof doesn't deserialize into something valid. -func assertBadProof(t *testing.T, root []byte, bad []byte, good TxProof) { - - var ( - proof TxProof - pbProof tmproto.TxProof - ) - err := pbProof.Unmarshal(bad) - if err == nil { - proof, err = TxProofFromProto(pbProof) - if err == nil { - err = proof.Validate(root) - if err == nil { - // XXX Fix simple merkle proofs so the following is *not* OK. - // This can happen if we have a slightly different total (where the - // path ends up the same). If it is something else, we have a real - // problem. - assert.NotEqual(t, proof.Proof.Total, good.Proof.Total, "bad: %#v\ngood: %#v", proof, good) - } - } - } -}