github.com/hyperledger/burrow@v0.34.5-0.20220512172541-77f09336001d/forensics/replay_test.go (about)

     1  package forensics
     2  
     3  import (
     4  	"fmt"
     5  	"testing"
     6  	"time"
     7  
     8  	"github.com/hyperledger/burrow/bcm"
     9  	"github.com/hyperledger/burrow/event"
    10  	"github.com/hyperledger/burrow/execution"
    11  	"github.com/hyperledger/burrow/logging"
    12  	"github.com/hyperledger/burrow/txs"
    13  
    14  	"github.com/hyperledger/burrow/txs/payload"
    15  
    16  	"github.com/hyperledger/burrow/consensus/tendermint"
    17  
    18  	"github.com/hyperledger/burrow/acm"
    19  	"github.com/hyperledger/burrow/execution/state"
    20  	"github.com/hyperledger/burrow/genesis"
    21  	"github.com/stretchr/testify/require"
    22  	sm "github.com/tendermint/tendermint/state"
    23  	"github.com/tendermint/tendermint/store"
    24  	"github.com/tendermint/tendermint/types"
    25  	dbm "github.com/tendermint/tm-db"
    26  )
    27  
    28  // This serves as a testbed for looking at non-deterministic burrow instances capture from the wild
    29  // Put the path to 'good' and 'bad' burrow directories here (containing the config files and .burrow dir)
    30  
    31  func TestStateComp(t *testing.T) {
    32  	st1 := state.NewState(dbm.NewMemDB())
    33  	_, _, err := st1.Update(func(ws state.Updatable) error {
    34  		return ws.UpdateAccount(acm.NewAccountFromSecret("1"))
    35  	})
    36  	require.NoError(t, err)
    37  	_, _, err = st1.Update(func(ws state.Updatable) error {
    38  		return ws.UpdateAccount(acm.NewAccountFromSecret("2"))
    39  	})
    40  	require.NoError(t, err)
    41  
    42  	db2 := dbm.NewMemDB()
    43  	st2, err := st1.Copy(db2)
    44  	require.NoError(t, err)
    45  	err = CompareStateAtHeight(st2, st1, 0)
    46  	require.Error(t, err)
    47  
    48  	_, _, err = st2.Update(func(ws state.Updatable) error {
    49  		return ws.UpdateAccount(acm.NewAccountFromSecret("3"))
    50  	})
    51  	require.NoError(t, err)
    52  
    53  	err = CompareStateAtHeight(st2, st1, 1)
    54  	require.Error(t, err)
    55  }
    56  
    57  func TestReplay(t *testing.T) {
    58  	var height uint64 = 10
    59  	genesisDoc, tmDB, burrowDB := makeChain(t, height)
    60  
    61  	src := NewSource(burrowDB, tmDB, genesisDoc)
    62  	dst := NewSourceFromGenesis(genesisDoc)
    63  	re := NewReplay(src, dst)
    64  
    65  	rc, err := re.Blocks(1, height)
    66  	require.NoError(t, err)
    67  	require.Len(t, rc, int(height-1))
    68  }
    69  
    70  func makeChain(t *testing.T, max uint64) (*genesis.GenesisDoc, dbm.DB, dbm.DB) {
    71  	genesisDoc, _, validators := genesis.NewDeterministicGenesis(0).GenesisDoc(0, 1)
    72  
    73  	tmDB := dbm.NewMemDB()
    74  	bs := store.NewBlockStore(tmDB)
    75  	gd := tendermint.DeriveGenesisDoc(genesisDoc, nil)
    76  	st, err := sm.MakeGenesisState(&types.GenesisDoc{
    77  		ChainID:    gd.ChainID,
    78  		Validators: gd.Validators,
    79  		AppHash:    gd.AppHash,
    80  	})
    81  	require.NoError(t, err)
    82  
    83  	burrowDB, burrowState, burrowChain, err := initBurrow(genesisDoc)
    84  	require.NoError(t, err)
    85  
    86  	committer, err := execution.NewBatchCommitter(burrowState, execution.ParamsFromGenesis(genesisDoc),
    87  		burrowChain, event.NewEmitter(), logging.NewNoopLogger())
    88  	require.NoError(t, err)
    89  
    90  	var stateHash []byte
    91  	for i := uint64(1); i < max; i++ {
    92  		makeBlock(t, st, bs, func(block *types.Block) {
    93  
    94  			decoder := txs.NewProtobufCodec()
    95  			err = bcm.NewBlock(decoder, block).Transactions(func(txEnv *txs.Envelope) error {
    96  				_, err := committer.Execute(txEnv)
    97  				require.NoError(t, err)
    98  				return nil
    99  			})
   100  			// empty if height == 1
   101  			block.AppHash = stateHash
   102  			// we need app hash in the abci header
   103  			abciHeader := types.TM2PB.Header(&block.Header)
   104  			stateHash, err = committer.Commit(&abciHeader)
   105  			require.NoError(t, err)
   106  
   107  		}, validators[0])
   108  		require.Equal(t, int64(i), bs.Height())
   109  	}
   110  	return genesisDoc, tmDB, burrowDB
   111  }
   112  
   113  func makeBlock(t *testing.T, st sm.State, bs *store.BlockStore, commit func(*types.Block), val *acm.PrivateAccount) {
   114  	height := bs.Height() + 1
   115  	tx := makeTx(t, st.ChainID, height, val)
   116  	block, _ := st.MakeBlock(height, []types.Tx{tx}, new(types.Commit), nil,
   117  		st.Validators.GetProposer().Address)
   118  
   119  	commit(block)
   120  	partSet := block.MakePartSet(2)
   121  	commitSigs := []types.CommitSig{{Timestamp: time.Time{}}}
   122  	seenCommit := types.NewCommit(height, 0, types.BlockID{
   123  		Hash:          block.Hash(),
   124  		PartSetHeader: partSet.Header(),
   125  	}, commitSigs)
   126  	bs.SaveBlock(block, partSet, seenCommit)
   127  }
   128  
   129  func makeTx(t *testing.T, chainID string, height int64, val *acm.PrivateAccount) (tx types.Tx) {
   130  	sendTx := payload.NewSendTx()
   131  	amount := uint64(height)
   132  	acc := acm.NewAccountFromSecret(fmt.Sprintf("%d", height))
   133  	sendTx.AddInputWithSequence(val.GetPublicKey(), amount, uint64(height))
   134  	sendTx.AddOutput(acc.GetAddress(), amount)
   135  	txEnv := txs.Enclose(chainID, sendTx)
   136  	err := txEnv.Sign(val)
   137  	require.NoError(t, err)
   138  
   139  	data, err := txs.NewProtobufCodec().EncodeTx(txEnv)
   140  	require.NoError(t, err)
   141  	return types.Tx(data)
   142  }