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 }