You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

242 lines
5.2 KiB

add support for block pruning via ABCI Commit response (#4588) * Added BlockStore.DeleteBlock() * Added initial block pruner prototype * wip * Added BlockStore.PruneBlocks() * Added consensus setting for block pruning * Added BlockStore base * Error on replay if base does not have blocks * Handle missing blocks when sending VoteSetMaj23Message * Error message tweak * Properly update blockstore state * Error message fix again * blockchain: ignore peer missing blocks * Added FIXME * Added test for block replay with truncated history * Handle peer base in blockchain reactor * Improved replay error handling * Added tests for Store.PruneBlocks() * Fix non-RPC handling of truncated block history * Panic on missing block meta in needProofBlock() * Updated changelog * Handle truncated block history in RPC layer * Added info about earliest block in /status RPC * Reorder height and base in blockchain reactor messages * Updated changelog * Fix tests * Appease linter * Minor review fixes * Non-empty BlockStores should always have base > 0 * Update code to assume base > 0 invariant * Added blockstore tests for pruning to 0 * Make sure we don't prune below the current base * Added BlockStore.Size() * config: added retain_blocks recommendations * Update v1 blockchain reactor to handle blockstore base * Added state database pruning * Propagate errors on missing validator sets * Comment tweaks * Improved error message Co-Authored-By: Anton Kaliaev <anton.kalyaev@gmail.com> * use ABCI field ResponseCommit.retain_height instead of retain-blocks config option * remove State.RetainHeight, return value instead * fix minor issues * rename pruneHeights() to pruneBlocks() * noop to fix GitHub borkage Co-authored-by: Anton Kaliaev <anton.kalyaev@gmail.com>
5 years ago
add support for block pruning via ABCI Commit response (#4588) * Added BlockStore.DeleteBlock() * Added initial block pruner prototype * wip * Added BlockStore.PruneBlocks() * Added consensus setting for block pruning * Added BlockStore base * Error on replay if base does not have blocks * Handle missing blocks when sending VoteSetMaj23Message * Error message tweak * Properly update blockstore state * Error message fix again * blockchain: ignore peer missing blocks * Added FIXME * Added test for block replay with truncated history * Handle peer base in blockchain reactor * Improved replay error handling * Added tests for Store.PruneBlocks() * Fix non-RPC handling of truncated block history * Panic on missing block meta in needProofBlock() * Updated changelog * Handle truncated block history in RPC layer * Added info about earliest block in /status RPC * Reorder height and base in blockchain reactor messages * Updated changelog * Fix tests * Appease linter * Minor review fixes * Non-empty BlockStores should always have base > 0 * Update code to assume base > 0 invariant * Added blockstore tests for pruning to 0 * Make sure we don't prune below the current base * Added BlockStore.Size() * config: added retain_blocks recommendations * Update v1 blockchain reactor to handle blockstore base * Added state database pruning * Propagate errors on missing validator sets * Comment tweaks * Improved error message Co-Authored-By: Anton Kaliaev <anton.kalyaev@gmail.com> * use ABCI field ResponseCommit.retain_height instead of retain-blocks config option * remove State.RetainHeight, return value instead * fix minor issues * rename pruneHeights() to pruneBlocks() * noop to fix GitHub borkage Co-authored-by: Anton Kaliaev <anton.kalyaev@gmail.com>
5 years ago
8 years ago
add support for block pruning via ABCI Commit response (#4588) * Added BlockStore.DeleteBlock() * Added initial block pruner prototype * wip * Added BlockStore.PruneBlocks() * Added consensus setting for block pruning * Added BlockStore base * Error on replay if base does not have blocks * Handle missing blocks when sending VoteSetMaj23Message * Error message tweak * Properly update blockstore state * Error message fix again * blockchain: ignore peer missing blocks * Added FIXME * Added test for block replay with truncated history * Handle peer base in blockchain reactor * Improved replay error handling * Added tests for Store.PruneBlocks() * Fix non-RPC handling of truncated block history * Panic on missing block meta in needProofBlock() * Updated changelog * Handle truncated block history in RPC layer * Added info about earliest block in /status RPC * Reorder height and base in blockchain reactor messages * Updated changelog * Fix tests * Appease linter * Minor review fixes * Non-empty BlockStores should always have base > 0 * Update code to assume base > 0 invariant * Added blockstore tests for pruning to 0 * Make sure we don't prune below the current base * Added BlockStore.Size() * config: added retain_blocks recommendations * Update v1 blockchain reactor to handle blockstore base * Added state database pruning * Propagate errors on missing validator sets * Comment tweaks * Improved error message Co-Authored-By: Anton Kaliaev <anton.kalyaev@gmail.com> * use ABCI field ResponseCommit.retain_height instead of retain-blocks config option * remove State.RetainHeight, return value instead * fix minor issues * rename pruneHeights() to pruneBlocks() * noop to fix GitHub borkage Co-authored-by: Anton Kaliaev <anton.kalyaev@gmail.com>
5 years ago
8 years ago
10 years ago
8 years ago
10 years ago
10 years ago
add support for block pruning via ABCI Commit response (#4588) * Added BlockStore.DeleteBlock() * Added initial block pruner prototype * wip * Added BlockStore.PruneBlocks() * Added consensus setting for block pruning * Added BlockStore base * Error on replay if base does not have blocks * Handle missing blocks when sending VoteSetMaj23Message * Error message tweak * Properly update blockstore state * Error message fix again * blockchain: ignore peer missing blocks * Added FIXME * Added test for block replay with truncated history * Handle peer base in blockchain reactor * Improved replay error handling * Added tests for Store.PruneBlocks() * Fix non-RPC handling of truncated block history * Panic on missing block meta in needProofBlock() * Updated changelog * Handle truncated block history in RPC layer * Added info about earliest block in /status RPC * Reorder height and base in blockchain reactor messages * Updated changelog * Fix tests * Appease linter * Minor review fixes * Non-empty BlockStores should always have base > 0 * Update code to assume base > 0 invariant * Added blockstore tests for pruning to 0 * Make sure we don't prune below the current base * Added BlockStore.Size() * config: added retain_blocks recommendations * Update v1 blockchain reactor to handle blockstore base * Added state database pruning * Propagate errors on missing validator sets * Comment tweaks * Improved error message Co-Authored-By: Anton Kaliaev <anton.kalyaev@gmail.com> * use ABCI field ResponseCommit.retain_height instead of retain-blocks config option * remove State.RetainHeight, return value instead * fix minor issues * rename pruneHeights() to pruneBlocks() * noop to fix GitHub borkage Co-authored-by: Anton Kaliaev <anton.kalyaev@gmail.com>
5 years ago
10 years ago
10 years ago
10 years ago
10 years ago
add support for block pruning via ABCI Commit response (#4588) * Added BlockStore.DeleteBlock() * Added initial block pruner prototype * wip * Added BlockStore.PruneBlocks() * Added consensus setting for block pruning * Added BlockStore base * Error on replay if base does not have blocks * Handle missing blocks when sending VoteSetMaj23Message * Error message tweak * Properly update blockstore state * Error message fix again * blockchain: ignore peer missing blocks * Added FIXME * Added test for block replay with truncated history * Handle peer base in blockchain reactor * Improved replay error handling * Added tests for Store.PruneBlocks() * Fix non-RPC handling of truncated block history * Panic on missing block meta in needProofBlock() * Updated changelog * Handle truncated block history in RPC layer * Added info about earliest block in /status RPC * Reorder height and base in blockchain reactor messages * Updated changelog * Fix tests * Appease linter * Minor review fixes * Non-empty BlockStores should always have base > 0 * Update code to assume base > 0 invariant * Added blockstore tests for pruning to 0 * Make sure we don't prune below the current base * Added BlockStore.Size() * config: added retain_blocks recommendations * Update v1 blockchain reactor to handle blockstore base * Added state database pruning * Propagate errors on missing validator sets * Comment tweaks * Improved error message Co-Authored-By: Anton Kaliaev <anton.kalyaev@gmail.com> * use ABCI field ResponseCommit.retain_height instead of retain-blocks config option * remove State.RetainHeight, return value instead * fix minor issues * rename pruneHeights() to pruneBlocks() * noop to fix GitHub borkage Co-authored-by: Anton Kaliaev <anton.kalyaev@gmail.com>
5 years ago
add support for block pruning via ABCI Commit response (#4588) * Added BlockStore.DeleteBlock() * Added initial block pruner prototype * wip * Added BlockStore.PruneBlocks() * Added consensus setting for block pruning * Added BlockStore base * Error on replay if base does not have blocks * Handle missing blocks when sending VoteSetMaj23Message * Error message tweak * Properly update blockstore state * Error message fix again * blockchain: ignore peer missing blocks * Added FIXME * Added test for block replay with truncated history * Handle peer base in blockchain reactor * Improved replay error handling * Added tests for Store.PruneBlocks() * Fix non-RPC handling of truncated block history * Panic on missing block meta in needProofBlock() * Updated changelog * Handle truncated block history in RPC layer * Added info about earliest block in /status RPC * Reorder height and base in blockchain reactor messages * Updated changelog * Fix tests * Appease linter * Minor review fixes * Non-empty BlockStores should always have base > 0 * Update code to assume base > 0 invariant * Added blockstore tests for pruning to 0 * Make sure we don't prune below the current base * Added BlockStore.Size() * config: added retain_blocks recommendations * Update v1 blockchain reactor to handle blockstore base * Added state database pruning * Propagate errors on missing validator sets * Comment tweaks * Improved error message Co-Authored-By: Anton Kaliaev <anton.kalyaev@gmail.com> * use ABCI field ResponseCommit.retain_height instead of retain-blocks config option * remove State.RetainHeight, return value instead * fix minor issues * rename pruneHeights() to pruneBlocks() * noop to fix GitHub borkage Co-authored-by: Anton Kaliaev <anton.kalyaev@gmail.com>
5 years ago
  1. package blocksync
  2. import (
  3. "fmt"
  4. mrand "math/rand"
  5. "testing"
  6. "time"
  7. "github.com/stretchr/testify/assert"
  8. "github.com/stretchr/testify/require"
  9. "github.com/tendermint/tendermint/libs/log"
  10. tmrand "github.com/tendermint/tendermint/libs/rand"
  11. "github.com/tendermint/tendermint/types"
  12. )
  13. func init() {
  14. peerTimeout = 2 * time.Second
  15. }
  16. type testPeer struct {
  17. id types.NodeID
  18. base int64
  19. height int64
  20. inputChan chan inputData // make sure each peer's data is sequential
  21. }
  22. type inputData struct {
  23. t *testing.T
  24. pool *BlockPool
  25. request BlockRequest
  26. }
  27. func (p testPeer) runInputRoutine() {
  28. go func() {
  29. for input := range p.inputChan {
  30. p.simulateInput(input)
  31. }
  32. }()
  33. }
  34. // Request desired, pretend like we got the block immediately.
  35. func (p testPeer) simulateInput(input inputData) {
  36. block := &types.Block{Header: types.Header{Height: input.request.Height}}
  37. input.pool.AddBlock(input.request.PeerID, block, 123)
  38. // TODO: uncommenting this creates a race which is detected by:
  39. // https://github.com/golang/go/blob/2bd767b1022dd3254bcec469f0ee164024726486/src/testing/testing.go#L854-L856
  40. // see: https://github.com/tendermint/tendermint/issues/3390#issue-418379890
  41. // input.t.Logf("Added block from peer %v (height: %v)", input.request.PeerID, input.request.Height)
  42. }
  43. type testPeers map[types.NodeID]testPeer
  44. func (ps testPeers) start() {
  45. for _, v := range ps {
  46. v.runInputRoutine()
  47. }
  48. }
  49. func (ps testPeers) stop() {
  50. for _, v := range ps {
  51. close(v.inputChan)
  52. }
  53. }
  54. func makePeers(numPeers int, minHeight, maxHeight int64) testPeers {
  55. peers := make(testPeers, numPeers)
  56. for i := 0; i < numPeers; i++ {
  57. peerID := types.NodeID(tmrand.Str(12))
  58. height := minHeight + mrand.Int63n(maxHeight-minHeight)
  59. base := minHeight + int64(i)
  60. if base > height {
  61. base = height
  62. }
  63. peers[peerID] = testPeer{peerID, base, height, make(chan inputData, 10)}
  64. }
  65. return peers
  66. }
  67. func TestBlockPoolBasic(t *testing.T) {
  68. start := int64(42)
  69. peers := makePeers(10, start+1, 1000)
  70. errorsCh := make(chan peerError, 1000)
  71. requestsCh := make(chan BlockRequest, 1000)
  72. pool := NewBlockPool(start, requestsCh, errorsCh)
  73. pool.SetLogger(log.TestingLogger())
  74. err := pool.Start()
  75. if err != nil {
  76. t.Error(err)
  77. }
  78. t.Cleanup(func() {
  79. if err := pool.Stop(); err != nil {
  80. t.Error(err)
  81. }
  82. })
  83. peers.start()
  84. defer peers.stop()
  85. // Introduce each peer.
  86. go func() {
  87. for _, peer := range peers {
  88. pool.SetPeerRange(peer.id, peer.base, peer.height)
  89. }
  90. }()
  91. // Start a goroutine to pull blocks
  92. go func() {
  93. for {
  94. if !pool.IsRunning() {
  95. return
  96. }
  97. first, second := pool.PeekTwoBlocks()
  98. if first != nil && second != nil {
  99. pool.PopRequest()
  100. } else {
  101. time.Sleep(1 * time.Second)
  102. }
  103. }
  104. }()
  105. // Pull from channels
  106. for {
  107. select {
  108. case err := <-errorsCh:
  109. t.Error(err)
  110. case request := <-requestsCh:
  111. t.Logf("Pulled new BlockRequest %v", request)
  112. if request.Height == 300 {
  113. return // Done!
  114. }
  115. peers[request.PeerID].inputChan <- inputData{t, pool, request}
  116. }
  117. }
  118. }
  119. func TestBlockPoolTimeout(t *testing.T) {
  120. start := int64(42)
  121. peers := makePeers(10, start+1, 1000)
  122. errorsCh := make(chan peerError, 1000)
  123. requestsCh := make(chan BlockRequest, 1000)
  124. pool := NewBlockPool(start, requestsCh, errorsCh)
  125. pool.SetLogger(log.TestingLogger())
  126. err := pool.Start()
  127. if err != nil {
  128. t.Error(err)
  129. }
  130. t.Cleanup(func() {
  131. if err := pool.Stop(); err != nil {
  132. t.Error(err)
  133. }
  134. })
  135. for _, peer := range peers {
  136. t.Logf("Peer %v", peer.id)
  137. }
  138. // Introduce each peer.
  139. go func() {
  140. for _, peer := range peers {
  141. pool.SetPeerRange(peer.id, peer.base, peer.height)
  142. }
  143. }()
  144. // Start a goroutine to pull blocks
  145. go func() {
  146. for {
  147. if !pool.IsRunning() {
  148. return
  149. }
  150. first, second := pool.PeekTwoBlocks()
  151. if first != nil && second != nil {
  152. pool.PopRequest()
  153. } else {
  154. time.Sleep(1 * time.Second)
  155. }
  156. }
  157. }()
  158. // Pull from channels
  159. counter := 0
  160. timedOut := map[types.NodeID]struct{}{}
  161. for {
  162. select {
  163. case err := <-errorsCh:
  164. t.Log(err)
  165. // consider error to be always timeout here
  166. if _, ok := timedOut[err.peerID]; !ok {
  167. counter++
  168. if counter == len(peers) {
  169. return // Done!
  170. }
  171. }
  172. case request := <-requestsCh:
  173. t.Logf("Pulled new BlockRequest %+v", request)
  174. }
  175. }
  176. }
  177. func TestBlockPoolRemovePeer(t *testing.T) {
  178. peers := make(testPeers, 10)
  179. for i := 0; i < 10; i++ {
  180. peerID := types.NodeID(fmt.Sprintf("%d", i+1))
  181. height := int64(i + 1)
  182. peers[peerID] = testPeer{peerID, 0, height, make(chan inputData)}
  183. }
  184. requestsCh := make(chan BlockRequest)
  185. errorsCh := make(chan peerError)
  186. pool := NewBlockPool(1, requestsCh, errorsCh)
  187. pool.SetLogger(log.TestingLogger())
  188. err := pool.Start()
  189. require.NoError(t, err)
  190. t.Cleanup(func() {
  191. if err := pool.Stop(); err != nil {
  192. t.Error(err)
  193. }
  194. })
  195. // add peers
  196. for peerID, peer := range peers {
  197. pool.SetPeerRange(peerID, peer.base, peer.height)
  198. }
  199. assert.EqualValues(t, 10, pool.MaxPeerHeight())
  200. // remove not-existing peer
  201. assert.NotPanics(t, func() { pool.RemovePeer(types.NodeID("Superman")) })
  202. // remove peer with biggest height
  203. pool.RemovePeer(types.NodeID("10"))
  204. assert.EqualValues(t, 9, pool.MaxPeerHeight())
  205. // remove all peers
  206. for peerID := range peers {
  207. pool.RemovePeer(peerID)
  208. }
  209. assert.EqualValues(t, 0, pool.MaxPeerHeight())
  210. }