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

// 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
}