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.

216 lines
6.9 KiB

  1. // Copyright 2017-2018 DERO Project. All rights reserved.
  2. // Use of this source code in any form is governed by RESEARCH license.
  3. // license can be found in the LICENSE file.
  4. // GPG: 0F39 E425 8C65 3947 702A 8234 08B2 0360 A03A 9DE8
  5. //
  6. //
  7. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
  8. // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
  9. // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
  10. // THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  11. // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  12. // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  13. // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  14. // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
  15. // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  16. package mempool
  17. import "sync"
  18. import "time"
  19. import "sync/atomic"
  20. import log "github.com/sirupsen/logrus"
  21. import "github.com/deroproject/derosuite/transaction"
  22. import "github.com/deroproject/derosuite/globals"
  23. import "github.com/deroproject/derosuite/crypto"
  24. // NOTE: do NOT consider this code as useless, as it is used to avooid double spending attacks within the block
  25. // let me explain, since we are a state machine, we add block to our blockchain
  26. // so, if a double spending attack comes, 2 transactions with same inputs, we reject one of them
  27. // the algo is documented somewhere else which explains the entire process
  28. // at this point in time, this is an ultrafast written mempool,
  29. // it will not scale for more than 10000 transactions but is good enough for now
  30. // we can always come back and rewrite it
  31. // NOTE: the pool is not persistant , means closing the daemon will make the mempool empty next restart
  32. // TODO: make the pool persistant
  33. type Mempool struct {
  34. txs map[crypto.Hash]mempool_object
  35. key_images map[crypto.Hash]bool // contains key images of all txs
  36. modified bool // used to monitor whethel mem pool contents have changed,
  37. // global variable , but don't see it utilisation here except fot tx verification
  38. //chain *Blockchain
  39. sync.Mutex
  40. }
  41. type mempool_object struct {
  42. Tx *transaction.Transaction
  43. Added uint64 // time in epoch format
  44. Reason int // why is the tx in the mempool
  45. }
  46. var loggerpool *log.Entry
  47. func Init_Mempool(params map[string]interface{}) (*Mempool, error) {
  48. var mempool Mempool
  49. //mempool.chain = params["chain"].(*Blockchain)
  50. loggerpool = globals.Logger.WithFields(log.Fields{"com": "POOL"}) // all components must use this logger
  51. loggerpool.Infof("Mempool started")
  52. atomic.AddUint32(&globals.Subsystem_Active, 1) // increment subsystem
  53. // initialize maps
  54. mempool.txs = map[crypto.Hash]mempool_object{}
  55. mempool.key_images = map[crypto.Hash]bool{}
  56. //TODO load any trasactions saved at previous exit
  57. return &mempool, nil
  58. }
  59. // this is created per incoming block and then discarded
  60. // This does not require shutting down and will be garbage collected automatically
  61. func Init_Block_Mempool(params map[string]interface{}) (*Mempool, error) {
  62. var mempool Mempool
  63. // initialize maps
  64. mempool.txs = map[crypto.Hash]mempool_object{}
  65. mempool.key_images = map[crypto.Hash]bool{}
  66. //TODO load any trasactions saved at previous exit
  67. return &mempool, nil
  68. }
  69. func (pool *Mempool) Shutdown() {
  70. //TODO save mempool tx somewhere
  71. loggerpool.Infof("Mempool stopped")
  72. atomic.AddUint32(&globals.Subsystem_Active, ^uint32(0)) // this decrement 1 fom subsystem
  73. }
  74. // start pool monitoring for changes for some specific time
  75. // this is required so as we can add or discard transactions while selecting work for mining
  76. func (pool *Mempool) Monitor() {
  77. pool.Lock()
  78. pool.modified = false
  79. pool.Unlock()
  80. }
  81. // return whether pool contents have changed
  82. func (pool *Mempool) HasChanged() (result bool) {
  83. pool.Lock()
  84. result = pool.modified
  85. pool.Unlock()
  86. return
  87. }
  88. // a tx should only be added to pool after verification is complete
  89. func (pool *Mempool) Mempool_Add_TX(tx *transaction.Transaction, Reason int) (result bool) {
  90. result = false
  91. pool.Lock()
  92. defer pool.Unlock()
  93. var object mempool_object
  94. tx_hash := crypto.Hash(tx.GetHash())
  95. // check if tx already exists, skip it
  96. if _, ok := pool.txs[tx_hash]; ok {
  97. loggerpool.Infof("Pool already contains %s, skipping \n", tx_hash)
  98. return false
  99. }
  100. // we should also extract all key images and add them to have multiple pending
  101. for i := 0; i < len(tx.Vin); i++ {
  102. if _, ok := pool.key_images[tx.Vin[i].(transaction.Txin_to_key).K_image]; ok {
  103. loggerpool.WithFields(log.Fields{
  104. "txid": tx_hash,
  105. "kimage": tx.Vin[i].(transaction.Txin_to_key).K_image,
  106. }).Warnf("TX using inputs which have already been used, Possible Double spend attack rejected")
  107. return false
  108. }
  109. }
  110. // add all the key images to check double spend attack within the pool
  111. for i := 0; i < len(tx.Vin); i++ {
  112. pool.key_images[tx.Vin[i].(transaction.Txin_to_key).K_image] = true // add element to map for next check
  113. }
  114. // we are here means we can add it to pool
  115. object.Tx = tx
  116. object.Reason = Reason
  117. object.Added = uint64(time.Now().Unix())
  118. pool.txs[tx_hash] = object
  119. pool.modified = true // pool has been modified
  120. return true
  121. }
  122. // check whether a tx exists in the pool
  123. func (pool *Mempool) Mempool_TX_Exist(txid crypto.Hash) (result bool) {
  124. pool.Lock()
  125. defer pool.Unlock()
  126. if _, ok := pool.txs[txid]; ok {
  127. return true
  128. }
  129. return false
  130. }
  131. // delete specific tx from pool and return it
  132. // if nil is returned Tx was not found in pool
  133. func (pool *Mempool) Mempool_Delete_TX(txid crypto.Hash) (tx *transaction.Transaction) {
  134. pool.Lock()
  135. defer pool.Unlock()
  136. // check if tx already exists, skip it
  137. if _, ok := pool.txs[txid]; !ok {
  138. loggerpool.Warnf("Pool does NOT contain %s, returning nil \n", txid)
  139. return nil
  140. }
  141. // we reached here means, we have the tx remove it from our list, do maintainance cleapup and discard it
  142. object := pool.txs[txid]
  143. delete(pool.txs, txid)
  144. // remove all the key images
  145. for i := 0; i < len(object.Tx.Vin); i++ {
  146. delete(pool.key_images, object.Tx.Vin[i].(transaction.Txin_to_key).K_image)
  147. }
  148. pool.modified = true // pool has been modified
  149. return object.Tx // return the tx
  150. }
  151. // get specific tx from mem pool without removing it
  152. func (pool *Mempool) Mempool_Get_TX(txid crypto.Hash) (tx *transaction.Transaction) {
  153. pool.Lock()
  154. defer pool.Unlock()
  155. if _, ok := pool.txs[txid]; !ok {
  156. //loggerpool.Warnf("Pool does NOT contain %s, returning nil \n", txid)
  157. return nil
  158. }
  159. // we reached here means, we have the tx, return the pointer back
  160. object := pool.txs[txid]
  161. return object.Tx
  162. }
  163. // return list of all txs in pool
  164. func (pool *Mempool) Mempool_List_TX() []crypto.Hash {
  165. pool.Lock()
  166. defer pool.Unlock()
  167. var list []crypto.Hash
  168. for k, _ := range pool.txs {
  169. list = append(list, k)
  170. }
  171. return list
  172. }