// Copyright 2017-2018 DERO Project. All rights reserved. // Use of this source code in any form is governed by RESEARCH license. // license can be found in the LICENSE file. // GPG: 0F39 E425 8C65 3947 702A 8234 08B2 0360 A03A 9DE8 // // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL // THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. package mempool import "sync" import "time" import "sync/atomic" import log "github.com/sirupsen/logrus" import "github.com/deroproject/derosuite/transaction" import "github.com/deroproject/derosuite/globals" import "github.com/deroproject/derosuite/crypto" // NOTE: do NOT consider this code as useless, as it is used to avooid double spending attacks within the block // let me explain, since we are a state machine, we add block to our blockchain // so, if a double spending attack comes, 2 transactions with same inputs, we reject one of them // the algo is documented somewhere else which explains the entire process // at this point in time, this is an ultrafast written mempool, // it will not scale for more than 10000 transactions but is good enough for now // we can always come back and rewrite it // NOTE: the pool is not persistant , means closing the daemon will make the mempool empty next restart // TODO: make the pool persistant type Mempool struct { txs map[crypto.Hash]mempool_object key_images map[crypto.Hash]bool // contains key images of all txs modified bool // used to monitor whethel mem pool contents have changed, // global variable , but don't see it utilisation here except fot tx verification //chain *Blockchain sync.Mutex } type mempool_object struct { Tx *transaction.Transaction Added uint64 // time in epoch format Reason int // why is the tx in the mempool } var loggerpool *log.Entry func Init_Mempool(params map[string]interface{}) (*Mempool, error) { var mempool Mempool //mempool.chain = params["chain"].(*Blockchain) loggerpool = globals.Logger.WithFields(log.Fields{"com": "POOL"}) // all components must use this logger loggerpool.Infof("Mempool started") atomic.AddUint32(&globals.Subsystem_Active, 1) // increment subsystem // initialize maps mempool.txs = map[crypto.Hash]mempool_object{} mempool.key_images = map[crypto.Hash]bool{} //TODO load any trasactions saved at previous exit return &mempool, nil } // this is created per incoming block and then discarded // This does not require shutting down and will be garbage collected automatically func Init_Block_Mempool(params map[string]interface{}) (*Mempool, error) { var mempool Mempool // initialize maps mempool.txs = map[crypto.Hash]mempool_object{} mempool.key_images = map[crypto.Hash]bool{} //TODO load any trasactions saved at previous exit return &mempool, nil } func (pool *Mempool) Shutdown() { //TODO save mempool tx somewhere loggerpool.Infof("Mempool stopped") atomic.AddUint32(&globals.Subsystem_Active, ^uint32(0)) // this decrement 1 fom subsystem } // start pool monitoring for changes for some specific time // this is required so as we can add or discard transactions while selecting work for mining func (pool *Mempool) Monitor() { pool.Lock() pool.modified = false pool.Unlock() } // return whether pool contents have changed func (pool *Mempool) HasChanged() (result bool) { pool.Lock() result = pool.modified pool.Unlock() return } // a tx should only be added to pool after verification is complete func (pool *Mempool) Mempool_Add_TX(tx *transaction.Transaction, Reason int) (result bool) { result = false pool.Lock() defer pool.Unlock() var object mempool_object tx_hash := crypto.Hash(tx.GetHash()) // check if tx already exists, skip it if _, ok := pool.txs[tx_hash]; ok { loggerpool.Infof("Pool already contains %s, skipping \n", tx_hash) return false } // we should also extract all key images and add them to have multiple pending for i := 0; i < len(tx.Vin); i++ { if _, ok := pool.key_images[tx.Vin[i].(transaction.Txin_to_key).K_image]; ok { loggerpool.WithFields(log.Fields{ "txid": tx_hash, "kimage": tx.Vin[i].(transaction.Txin_to_key).K_image, }).Warnf("TX using inputs which have already been used, Possible Double spend attack rejected") return false } } // add all the key images to check double spend attack within the pool for i := 0; i < len(tx.Vin); i++ { pool.key_images[tx.Vin[i].(transaction.Txin_to_key).K_image] = true // add element to map for next check } // we are here means we can add it to pool object.Tx = tx object.Reason = Reason object.Added = uint64(time.Now().Unix()) pool.txs[tx_hash] = object pool.modified = true // pool has been modified return true } // check whether a tx exists in the pool func (pool *Mempool) Mempool_TX_Exist(txid crypto.Hash) (result bool) { pool.Lock() defer pool.Unlock() if _, ok := pool.txs[txid]; ok { return true } return false } // delete specific tx from pool and return it // if nil is returned Tx was not found in pool func (pool *Mempool) Mempool_Delete_TX(txid crypto.Hash) (tx *transaction.Transaction) { pool.Lock() defer pool.Unlock() // check if tx already exists, skip it if _, ok := pool.txs[txid]; !ok { loggerpool.Warnf("Pool does NOT contain %s, returning nil \n", txid) return nil } // we reached here means, we have the tx remove it from our list, do maintainance cleapup and discard it object := pool.txs[txid] delete(pool.txs, txid) // remove all the key images for i := 0; i < len(object.Tx.Vin); i++ { delete(pool.key_images, object.Tx.Vin[i].(transaction.Txin_to_key).K_image) } pool.modified = true // pool has been modified return object.Tx // return the tx } // get specific tx from mem pool without removing it func (pool *Mempool) Mempool_Get_TX(txid crypto.Hash) (tx *transaction.Transaction) { pool.Lock() defer pool.Unlock() if _, ok := pool.txs[txid]; !ok { //loggerpool.Warnf("Pool does NOT contain %s, returning nil \n", txid) return nil } // we reached here means, we have the tx, return the pointer back object := pool.txs[txid] return object.Tx } // return list of all txs in pool func (pool *Mempool) Mempool_List_TX() []crypto.Hash { pool.Lock() defer pool.Unlock() var list []crypto.Hash for k, _ := range pool.txs { list = append(list, k) } return list }