queue.go 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. // Copyright 2022 The go-ethereum Authors
  2. // This file is part of the go-ethereum library.
  3. //
  4. // The go-ethereum library is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU Lesser General Public License as published by
  6. // the Free Software Foundation, either version 3 of the License, or
  7. // (at your option) any later version.
  8. //
  9. // The go-ethereum library is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU Lesser General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU Lesser General Public License
  15. // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
  16. package catalyst
  17. import (
  18. "sync"
  19. "time"
  20. "github.com/ethereum/go-ethereum/common"
  21. "github.com/ethereum/go-ethereum/core/beacon"
  22. "github.com/ethereum/go-ethereum/core/types"
  23. )
  24. // maxTrackedPayloads is the maximum number of prepared payloads the execution
  25. // engine tracks before evicting old ones. Ideally we should only ever track the
  26. // latest one; but have a slight wiggle room for non-ideal conditions.
  27. const maxTrackedPayloads = 10
  28. // maxTrackedHeaders is the maximum number of executed payloads the execution
  29. // engine tracks before evicting old ones. Ideally we should only ever track the
  30. // latest one; but have a slight wiggle room for non-ideal conditions.
  31. const maxTrackedHeaders = 10
  32. // payload wraps the miner's block production channel, allowing the mined block
  33. // to be retrieved later upon the GetPayload engine API call.
  34. type payload struct {
  35. lock sync.Mutex
  36. done bool
  37. empty *types.Block
  38. block *types.Block
  39. result chan *types.Block
  40. }
  41. // resolve extracts the generated full block from the given channel if possible
  42. // or fallback to empty block as an alternative.
  43. func (req *payload) resolve() *beacon.ExecutableDataV1 {
  44. // this function can be called concurrently, prevent any
  45. // concurrency issue in the first place.
  46. req.lock.Lock()
  47. defer req.lock.Unlock()
  48. // Try to resolve the full block first if it's not obtained
  49. // yet. The returned block can be nil if the generation fails.
  50. if !req.done {
  51. timeout := time.NewTimer(500 * time.Millisecond)
  52. defer timeout.Stop()
  53. select {
  54. case req.block = <-req.result:
  55. req.done = true
  56. case <-timeout.C:
  57. // TODO(rjl49345642, Marius), should we keep this
  58. // 100ms timeout allowance? Why not just use the
  59. // default and then fallback to empty directly?
  60. }
  61. }
  62. if req.block != nil {
  63. return beacon.BlockToExecutableData(req.block)
  64. }
  65. return beacon.BlockToExecutableData(req.empty)
  66. }
  67. // payloadQueueItem represents an id->payload tuple to store until it's retrieved
  68. // or evicted.
  69. type payloadQueueItem struct {
  70. id beacon.PayloadID
  71. data *payload
  72. }
  73. // payloadQueue tracks the latest handful of constructed payloads to be retrieved
  74. // by the beacon chain if block production is requested.
  75. type payloadQueue struct {
  76. payloads []*payloadQueueItem
  77. lock sync.RWMutex
  78. }
  79. // newPayloadQueue creates a pre-initialized queue with a fixed number of slots
  80. // all containing empty items.
  81. func newPayloadQueue() *payloadQueue {
  82. return &payloadQueue{
  83. payloads: make([]*payloadQueueItem, maxTrackedPayloads),
  84. }
  85. }
  86. // put inserts a new payload into the queue at the given id.
  87. func (q *payloadQueue) put(id beacon.PayloadID, data *payload) {
  88. q.lock.Lock()
  89. defer q.lock.Unlock()
  90. copy(q.payloads[1:], q.payloads)
  91. q.payloads[0] = &payloadQueueItem{
  92. id: id,
  93. data: data,
  94. }
  95. }
  96. // get retrieves a previously stored payload item or nil if it does not exist.
  97. func (q *payloadQueue) get(id beacon.PayloadID) *beacon.ExecutableDataV1 {
  98. q.lock.RLock()
  99. defer q.lock.RUnlock()
  100. for _, item := range q.payloads {
  101. if item == nil {
  102. return nil // no more items
  103. }
  104. if item.id == id {
  105. return item.data.resolve()
  106. }
  107. }
  108. return nil
  109. }
  110. // headerQueueItem represents an hash->header tuple to store until it's retrieved
  111. // or evicted.
  112. type headerQueueItem struct {
  113. hash common.Hash
  114. header *types.Header
  115. }
  116. // headerQueue tracks the latest handful of constructed headers to be retrieved
  117. // by the beacon chain if block production is requested.
  118. type headerQueue struct {
  119. headers []*headerQueueItem
  120. lock sync.RWMutex
  121. }
  122. // newHeaderQueue creates a pre-initialized queue with a fixed number of slots
  123. // all containing empty items.
  124. func newHeaderQueue() *headerQueue {
  125. return &headerQueue{
  126. headers: make([]*headerQueueItem, maxTrackedHeaders),
  127. }
  128. }
  129. // put inserts a new header into the queue at the given hash.
  130. func (q *headerQueue) put(hash common.Hash, data *types.Header) {
  131. q.lock.Lock()
  132. defer q.lock.Unlock()
  133. copy(q.headers[1:], q.headers)
  134. q.headers[0] = &headerQueueItem{
  135. hash: hash,
  136. header: data,
  137. }
  138. }
  139. // get retrieves a previously stored header item or nil if it does not exist.
  140. func (q *headerQueue) get(hash common.Hash) *types.Header {
  141. q.lock.RLock()
  142. defer q.lock.RUnlock()
  143. for _, item := range q.headers {
  144. if item == nil {
  145. return nil // no more items
  146. }
  147. if item.hash == hash {
  148. return item.header
  149. }
  150. }
  151. return nil
  152. }