|
|
// 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/arnaucode/derosuite/transaction" import "github.com/arnaucode/derosuite/globals" import "github.com/arnaucode/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 }
|