Browse Source

remove Must* proof and hash functions

wb/txrset
William Banfield 3 years ago
parent
commit
c25f7769a6
No known key found for this signature in database GPG Key ID: EFAD3442BF29E3AC
8 changed files with 123 additions and 169 deletions
  1. +7
    -14
      abci/types/types.go
  2. +6
    -3
      abci/types/types_test.go
  3. +16
    -8
      internal/rpc/core/tx.go
  4. +7
    -1
      internal/state/execution.go
  5. +82
    -21
      internal/state/state_test.go
  6. +5
    -1
      light/rpc/client.go
  7. +0
    -18
      types/tx.go
  8. +0
    -103
      types/tx_test.go

+ 7
- 14
abci/types/types.go View File

@ -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 {


+ 6
- 3
abci/types/types_test.go View File

@ -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)
}
}


+ 16
- 8
internal/rpc/core/tx.go View File

@ -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{


+ 7
- 1
internal/state/execution.go View File

@ -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)
}


+ 82
- 21
internal/state/state_test.go View File

@ -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)
}


+ 5
- 1
light/rpc/client.go View File

@ -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) {


+ 0
- 18
types/tx.go View File

@ -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.


+ 0
- 103
types/tx_test.go View File

@ -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)
}
}
}
}

Loading…
Cancel
Save