From 5a3a6a341bd6364fb9db9d0f827d7bda314ee16f Mon Sep 17 00:00:00 2001
From: Hans Moog <hm@mkjc.net>
Date: Wed, 19 Feb 2020 21:23:36 +0100
Subject: [PATCH] Feat: fixed some bugs

---
 go.mod                                        |   4 +-
 main.go                                       |  15 +-
 packages/binary/spammer/spammer.go            |  50 +++++-
 .../tangle/model/approver/cached_approver.go  |   4 +-
 packages/binary/tangle/tangle.go              |   8 +-
 packages/database/database.go                 |   4 +
 plugins/gossip/plugin.go                      |  14 +-
 plugins/metrics/plugin.go                     |  14 +-
 plugins/spa/explorer_routes.go                | 166 ++++++++++--------
 plugins/spa/livefeed.go                       |  23 ++-
 plugins/spa/plugin.go                         |   6 +-
 plugins/tangle/plugin.go                      |   4 -
 plugins/webapi/spammer/plugin.go              |   2 +-
 plugins/webapi/spammer/webapi.go              |   9 +
 14 files changed, 204 insertions(+), 119 deletions(-)

diff --git a/go.mod b/go.mod
index e5b295a4..cb09e5a0 100644
--- a/go.mod
+++ b/go.mod
@@ -7,7 +7,7 @@ require (
 	github.com/dgrijalva/jwt-go v3.2.0+incompatible
 	github.com/dgryski/go-farm v0.0.0-20191112170834-c2139c5d712b // indirect
 	github.com/gobuffalo/envy v1.8.1 // indirect
-	github.com/gobuffalo/logger v1.0.3
+	github.com/gobuffalo/logger v1.0.3 // indirect
 	github.com/gobuffalo/packr/v2 v2.7.1
 	github.com/golang/protobuf v1.3.2
 	github.com/googollee/go-engine.io v1.4.3-0.20190924125625-798118fc0dd2
@@ -16,7 +16,7 @@ require (
 	github.com/iotaledger/hive.go v0.0.0-20200217140357-8f1ea1f52085
 	github.com/iotaledger/iota.go v1.0.0-beta.14
 	github.com/labstack/echo v3.3.10+incompatible
-	github.com/labstack/gommon v0.3.0
+	github.com/labstack/gommon v0.3.0 // indirect
 	github.com/magiconair/properties v1.8.1
 	github.com/mattn/go-colorable v0.1.4 // indirect
 	github.com/mattn/go-isatty v0.0.11 // indirect
diff --git a/main.go b/main.go
index 7329f0cf..39997396 100644
--- a/main.go
+++ b/main.go
@@ -1,9 +1,13 @@
 package main
 
 import (
+	"fmt"
 	"net/http"
 	_ "net/http/pprof"
 
+	"github.com/mr-tron/base58"
+
+	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction"
 	"github.com/iotaledger/goshimmer/plugins/analysis"
 	"github.com/iotaledger/goshimmer/plugins/autopeering"
 	"github.com/iotaledger/goshimmer/plugins/banner"
@@ -15,6 +19,7 @@ import (
 	"github.com/iotaledger/goshimmer/plugins/metrics"
 	"github.com/iotaledger/goshimmer/plugins/portcheck"
 	"github.com/iotaledger/goshimmer/plugins/remotelog"
+	"github.com/iotaledger/goshimmer/plugins/spa"
 	"github.com/iotaledger/goshimmer/plugins/tangle"
 	"github.com/iotaledger/goshimmer/plugins/webapi"
 	webapi_gtta "github.com/iotaledger/goshimmer/plugins/webapi/gtta"
@@ -27,6 +32,13 @@ import (
 func main() {
 	go http.ListenAndServe("localhost:6060", nil) // pprof Server for Debbuging Mutexes
 
+	testTxId := transaction.NewId([]byte("Test"))
+
+	fmt.Println(len(base58.Encode(transaction.EmptyId[:])))
+	fmt.Println(base58.Encode(transaction.EmptyId[:]))
+	fmt.Println(len(base58.Encode(testTxId[:])))
+	fmt.Println(base58.Encode(testTxId[:]))
+
 	node.Run(
 		node.Plugins(
 			banner.PLUGIN,
@@ -49,6 +61,8 @@ func main() {
 			webapi_gtta.PLUGIN,
 			webapi_spammer.PLUGIN,
 
+			spa.PLUGIN,
+
 			/*
 				webapi_broadcastData.PLUGIN,
 				webapi_getTransactionTrytesByHash.PLUGIN,
@@ -56,7 +70,6 @@ func main() {
 				webapi_findTransactionHashes.PLUGIN,
 				webapi_getNeighbors.PLUGIN,
 
-				//spa.PLUGIN,
 				//graph.PLUGIN,
 			*/
 		),
diff --git a/packages/binary/spammer/spammer.go b/packages/binary/spammer/spammer.go
index 7f163170..31a812b4 100644
--- a/packages/binary/spammer/spammer.go
+++ b/packages/binary/spammer/spammer.go
@@ -1,35 +1,67 @@
 package spammer
 
 import (
+	"fmt"
 	"sync"
 	"time"
 
 	"github.com/iotaledger/hive.go/types"
 
 	"github.com/iotaledger/goshimmer/packages/binary/identity"
-	"github.com/iotaledger/goshimmer/packages/binary/tangle"
 	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction"
 	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction/payload/data"
 	"github.com/iotaledger/goshimmer/packages/binary/tangle/tipselector"
+	"github.com/iotaledger/goshimmer/packages/binary/tangle/transactionparser"
 )
 
 type Spammer struct {
-	tangle      *tangle.Tangle
-	tipSelector *tipselector.TipSelector
+	transactionParser *transactionparser.TransactionParser
+	tipSelector       *tipselector.TipSelector
 
 	running        bool
 	startStopMutex sync.Mutex
 	shutdownSignal chan types.Empty
 }
 
-func New(tangle *tangle.Tangle, tipSelector *tipselector.TipSelector) *Spammer {
+func New(transactionParser *transactionparser.TransactionParser, tipSelector *tipselector.TipSelector) *Spammer {
 	return &Spammer{
-		shutdownSignal: make(chan types.Empty),
-		tangle:         tangle,
-		tipSelector:    tipSelector,
+		shutdownSignal:    make(chan types.Empty),
+		transactionParser: transactionParser,
+		tipSelector:       tipSelector,
 	}
 }
 
+func (spammer *Spammer) Burst(transactions int) {
+	spammingIdentity := identity.Generate()
+
+	previousTransactionId := transaction.EmptyId
+
+	fmt.Println("STARTING TO GENERATE")
+
+	burstBuffer := make([][]byte, transactions)
+	for i := 0; i < transactions; i++ {
+		spamTransaction := transaction.New(previousTransactionId, previousTransactionId, spammingIdentity, data.New([]byte("SPAM")))
+		previousTransactionId = spamTransaction.GetId()
+		burstBuffer[i] = spamTransaction.GetBytes()
+
+		if i%1000 == 0 {
+			fmt.Println("GENERATED", i)
+		}
+	}
+
+	fmt.Println("STARTING TO SPAM")
+
+	for i := 0; i < transactions; i++ {
+		spammer.transactionParser.Parse(burstBuffer[i])
+
+		if i%1000 == 0 {
+			fmt.Println("SENT", i)
+		}
+	}
+
+	fmt.Println("SPAMMING DONE")
+}
+
 func (spammer *Spammer) Start(tps int) {
 	spammer.startStopMutex.Lock()
 	defer spammer.startStopMutex.Unlock()
@@ -63,8 +95,8 @@ func (spammer *Spammer) run(tps int) {
 
 		default:
 			trunkTransactionId, branchTransactionId := spammer.tipSelector.GetTips()
-			spammer.tangle.AttachTransaction(
-				transaction.New(trunkTransactionId, branchTransactionId, identity.Generate(), data.New([]byte("SPAM"))),
+			spammer.transactionParser.Parse(
+				transaction.New(trunkTransactionId, branchTransactionId, identity.Generate(), data.New([]byte("SPAM"))).GetBytes(),
 			)
 
 			currentSentCounter++
diff --git a/packages/binary/tangle/model/approver/cached_approver.go b/packages/binary/tangle/model/approver/cached_approver.go
index c5284432..68fe1bcb 100644
--- a/packages/binary/tangle/model/approver/cached_approver.go
+++ b/packages/binary/tangle/model/approver/cached_approver.go
@@ -24,9 +24,9 @@ type CachedApprovers []*CachedApprover
 
 func (cachedApprovers CachedApprovers) Consume(consumer func(approver *Approver)) (consumed bool) {
 	for _, cachedApprover := range cachedApprovers {
-		consumed = consumed || cachedApprover.Consume(func(object objectstorage.StorableObject) {
+		consumed = cachedApprover.Consume(func(object objectstorage.StorableObject) {
 			consumer(object.(*Approver))
-		})
+		}) || consumed
 	}
 
 	return
diff --git a/packages/binary/tangle/tangle.go b/packages/binary/tangle/tangle.go
index 876c0d37..18593704 100644
--- a/packages/binary/tangle/tangle.go
+++ b/packages/binary/tangle/tangle.go
@@ -41,10 +41,10 @@ type Tangle struct {
 func New(badgerInstance *badger.DB, storageId []byte) (result *Tangle) {
 	result = &Tangle{
 		storageId:                  storageId,
-		transactionStorage:         objectstorage.New(badgerInstance, append(storageId, storageprefix.TangleTransaction...), transaction.FromStorage),
-		transactionMetadataStorage: objectstorage.New(badgerInstance, append(storageId, storageprefix.TangleTransactionMetadata...), transactionmetadata.FromStorage),
-		approverStorage:            objectstorage.New(badgerInstance, append(storageId, storageprefix.TangleApprovers...), approver.FromStorage, objectstorage.PartitionKey(transaction.IdLength, transaction.IdLength)),
-		missingTransactionsStorage: objectstorage.New(badgerInstance, append(storageId, storageprefix.TangleMissingTransaction...), missingtransaction.FromStorage),
+		transactionStorage:         objectstorage.New(badgerInstance, append(storageId, storageprefix.TangleTransaction...), transaction.FromStorage, objectstorage.CacheTime(10*time.Second), objectstorage.LeakDetectionEnabled(false)),
+		transactionMetadataStorage: objectstorage.New(badgerInstance, append(storageId, storageprefix.TangleTransactionMetadata...), transactionmetadata.FromStorage, objectstorage.CacheTime(10*time.Second), objectstorage.LeakDetectionEnabled(false)),
+		approverStorage:            objectstorage.New(badgerInstance, append(storageId, storageprefix.TangleApprovers...), approver.FromStorage, objectstorage.CacheTime(10*time.Second), objectstorage.PartitionKey(transaction.IdLength, transaction.IdLength), objectstorage.LeakDetectionEnabled(false)),
+		missingTransactionsStorage: objectstorage.New(badgerInstance, append(storageId, storageprefix.TangleMissingTransaction...), missingtransaction.FromStorage, objectstorage.CacheTime(10*time.Second), objectstorage.LeakDetectionEnabled(false)),
 
 		Events: *newEvents(),
 	}
diff --git a/packages/database/database.go b/packages/database/database.go
index 094abae6..437e59d7 100644
--- a/packages/database/database.go
+++ b/packages/database/database.go
@@ -57,12 +57,16 @@ func GetBadgerInstance() *badger.DB {
 			opts = opts.WithTruncate(true)
 		}
 
+		opts.SyncWrites = false
+		opts.TableLoadingMode = options.MemoryMap
+		opts.ValueLogLoadingMode = options.MemoryMap
 		opts.CompactL0OnClose = false
 		opts.KeepL0InMemory = false
 		opts.VerifyValueChecksum = false
 		opts.ZSTDCompressionLevel = 1
 		opts.Compression = options.None
 		opts.MaxCacheSize = 50000000
+		opts.EventLogging = false
 
 		db, err := database.CreateDB(dbDir, opts)
 		if err != nil {
diff --git a/plugins/gossip/plugin.go b/plugins/gossip/plugin.go
index 4b8bee22..39a3e7fd 100644
--- a/plugins/gossip/plugin.go
+++ b/plugins/gossip/plugin.go
@@ -9,7 +9,6 @@ import (
 	"github.com/iotaledger/hive.go/node"
 
 	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction"
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transactionmetadata"
 	"github.com/iotaledger/goshimmer/packages/gossip"
 	"github.com/iotaledger/goshimmer/packages/shutdown"
 	"github.com/iotaledger/goshimmer/plugins/tangle"
@@ -77,13 +76,14 @@ func configureEvents() {
 	}))
 
 	// configure flow of outgoing transactions (gossip on solidification)
-	tangle.Instance.Events.TransactionSolid.Attach(events.NewClosure(func(cachedTransaction *transaction.CachedTransaction, transactionMetadata *transactionmetadata.CachedTransactionMetadata) {
-		transactionMetadata.Release()
+	/*
+		tangle.Instance.Events.TransactionSolid.Attach(events.NewClosure(func(cachedTransaction *transaction.CachedTransaction, transactionMetadata *transactionmetadata.CachedTransactionMetadata) {
+			transactionMetadata.Release()
 
-		cachedTransaction.Consume(func(transaction *transaction.Transaction) {
-			mgr.SendTransaction(transaction.GetBytes())
-		})
-	}))
+			cachedTransaction.Consume(func(transaction *transaction.Transaction) {
+				mgr.SendTransaction(transaction.GetBytes())
+			})
+		}))*/
 
 	// request missing transactions
 	tangle.TransactionRequester.Events.SendRequest.Attach(events.NewClosure(func(transactionId transaction.Id) {
diff --git a/plugins/metrics/plugin.go b/plugins/metrics/plugin.go
index 3624874e..28f3013c 100644
--- a/plugins/metrics/plugin.go
+++ b/plugins/metrics/plugin.go
@@ -3,19 +3,27 @@ package metrics
 import (
 	"time"
 
-	"github.com/iotaledger/goshimmer/packages/gossip"
-	"github.com/iotaledger/goshimmer/packages/shutdown"
 	"github.com/iotaledger/hive.go/daemon"
 	"github.com/iotaledger/hive.go/events"
 	"github.com/iotaledger/hive.go/node"
 	"github.com/iotaledger/hive.go/timeutil"
+
+	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction"
+	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transactionmetadata"
+	"github.com/iotaledger/goshimmer/packages/shutdown"
+	"github.com/iotaledger/goshimmer/plugins/tangle"
 )
 
 var PLUGIN = node.NewPlugin("Metrics", node.Enabled, configure, run)
 
 func configure(plugin *node.Plugin) {
 	// increase received TPS counter whenever we receive a new transaction
-	gossip.Events.TransactionReceived.Attach(events.NewClosure(func(_ *gossip.TransactionReceivedEvent) { increaseReceivedTPSCounter() }))
+	tangle.Instance.Events.TransactionAttached.Attach(events.NewClosure(func(transaction *transaction.CachedTransaction, metadata *transactionmetadata.CachedTransactionMetadata) {
+		transaction.Release()
+		metadata.Release()
+
+		increaseReceivedTPSCounter()
+	}))
 }
 
 func run(plugin *node.Plugin) {
diff --git a/plugins/spa/explorer_routes.go b/plugins/spa/explorer_routes.go
index 13d45105..c3de4085 100644
--- a/plugins/spa/explorer_routes.go
+++ b/plugins/spa/explorer_routes.go
@@ -4,57 +4,46 @@ import (
 	"net/http"
 	"sync"
 
+	"github.com/mr-tron/base58"
+
 	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction"
 	"github.com/iotaledger/goshimmer/plugins/tangle"
 
-	"github.com/iotaledger/goshimmer/packages/model/transactionmetadata"
-	"github.com/iotaledger/goshimmer/plugins/tangle_old"
 	"github.com/labstack/echo"
 	"github.com/pkg/errors"
-
-	"github.com/iotaledger/iota.go/consts"
-	"github.com/iotaledger/iota.go/guards"
-	. "github.com/iotaledger/iota.go/trinary"
 )
 
 type ExplorerTx struct {
-	Hash                     transaction.Id `json:"hash"`
-	SignatureMessageFragment Trytes         `json:"signature_message_fragment"`
-	Timestamp                uint           `json:"timestamp"`
-	Trunk                    transaction.Id `json:"trunk"`
-	Branch                   transaction.Id `json:"branch"`
-	Solid                    bool           `json:"solid"`
-	MWM                      int            `json:"mwm"`
+	Hash                     string `json:"hash"`
+	SignatureMessageFragment string `json:"signature_message_fragment"`
+	Address                  string `json:"address"`
+	Value                    int64  `json:"value"`
+	Timestamp                uint   `json:"timestamp"`
+	Trunk                    string `json:"trunk"`
+	Branch                   string `json:"branch"`
+	Solid                    bool   `json:"solid"`
+	MWM                      int    `json:"mwm"`
 }
 
 func createExplorerTx(tx *transaction.Transaction) (*ExplorerTx, error) {
-	txMetadata, err := tangle_old.GetTransactionMetadata(hash, transactionmetadata.New)
-	if err != nil {
-		return nil, err
-	}
+	transactionId := tx.GetId()
+
+	txMetadata := tangle.Instance.GetTransactionMetadata(transactionId)
 
 	t := &ExplorerTx{
-		Hash:                     tx.GetId(),
-		SignatureMessageFragment: tx.GetSignatureMessageFragment(),
-		Address:                  tx.GetAddress(),
-		Timestamp:                tx.GetTimestamp(),
-		Value:                    tx.GetValue(),
-		Trunk:                    tx.GetTrunkTransactionHash(),
-		Branch:                   tx.GetBranchTransactionHash(),
-		Solid:                    txMetadata.GetSolid(),
+		Hash:                     transactionId.String(),
+		SignatureMessageFragment: "",
+		Address:                  "",
+		Timestamp:                0,
+		Value:                    0,
+		Trunk:                    tx.GetTrunkTransactionId().String(),
+		Branch:                   tx.GetBranchTransactionId().String(),
+		Solid:                    txMetadata.Unwrap().IsSolid(),
 	}
 
-	// compute mwm
-	trits := MustTrytesToTrits(hash)
-	var mwm int
-	for i := len(trits) - 1; i >= 0; i-- {
-		if trits[i] == 0 {
-			mwm++
-			continue
-		}
-		break
-	}
-	t.MWM = mwm
+	// TODO: COMPUTE MWM
+	t.MWM = 0
+
 	return t, nil
 }
 
@@ -68,13 +57,31 @@ type SearchResult struct {
 	Milestone *ExplorerTx     `json:"milestone"`
 }
 
+func transactionIdFromString(transactionId string) (transaction.Id, error) {
+	// TODO: CHECK LENGTH
+
+	transactionIdBytes, err := base58.Decode(transactionId)
+	if err != nil {
+		return transaction.EmptyId, err
+	}
+
+	// TODO: REMOVE CHECKSUM FROM STRING
+
+	return transaction.NewId(transactionIdBytes), nil
+}
+
 func setupExplorerRoutes(routeGroup *echo.Group) {
+	routeGroup.GET("/tx/:hash", func(c echo.Context) (err error) {
+		transactionId, err := transactionIdFromString(c.Param("hash"))
+		if err != nil {
+			return
+		}
 
-	routeGroup.GET("/tx/:hash", func(c echo.Context) error {
-		t, err := findTransaction(c.Param("hash"))
+		t, err := findTransaction(transactionId)
 		if err != nil {
-			return err
+			return
 		}
+
 		return c.JSON(http.StatusOK, t)
 	})
 
@@ -94,14 +101,17 @@ func setupExplorerRoutes(routeGroup *echo.Group) {
 			return errors.Wrapf(ErrInvalidParameter, "search hash invalid: %s", search)
 		}
 
-		// auto. remove checksum
-		search = search[:81]
-
 		wg := sync.WaitGroup{}
 		wg.Add(2)
 		go func() {
 			defer wg.Done()
-			tx, err := findTransaction(search)
+
+			transactionId, err := transactionIdFromString(search)
+			if err != nil {
+				return
+			}
+
+			tx, err := findTransaction(transactionId)
 			if err == nil {
 				result.Tx = tx
 			}
@@ -120,50 +130,56 @@ func setupExplorerRoutes(routeGroup *echo.Group) {
 	})
 }
 
-func findTransaction(transactionId transaction.Id) (*ExplorerTx, error) {
+func findTransaction(transactionId transaction.Id) (explorerTx *ExplorerTx, err error) {
 	if !tangle.Instance.GetTransaction(transactionId).Consume(func(transaction *transaction.Transaction) {
-		t, err := createExplorerTx(transaction)
+		explorerTx, err = createExplorerTx(transaction)
 	}) {
-		return nil, errors.Wrapf(ErrNotFound, "tx hash: %s", transactionId.String())
+		err = errors.Wrapf(ErrNotFound, "tx hash: %s", transactionId.String())
 	}
 
-	return t, err
+	return
 }
 
-func findAddress(hash Hash) (*ExplorerAdress, error) {
-	if len(hash) > 81 {
-		hash = hash[:81]
-	}
-	if !guards.IsTrytesOfExactLength(hash, consts.HashTrytesSize) {
-		return nil, errors.Wrapf(ErrInvalidParameter, "hash invalid: %s", hash)
-	}
-
-	txHashes, err := tangle_old.ReadTransactionHashesForAddressFromDatabase(hash)
-	if err != nil {
-		return nil, ErrInternalError
-	}
+func findAddress(address string) (*ExplorerAdress, error) {
+	return nil, errors.Wrapf(ErrNotFound, "address %s not found", address)
 
-	if len(txHashes) == 0 {
-		return nil, errors.Wrapf(ErrNotFound, "address %s not found", hash)
-	}
+	// TODO: ADD ADDRESS LOOKUPS ONCE THE VALUE TRANSFER ONTOLOGY IS MERGED
 
-	txs := make([]*ExplorerTx, 0, len(txHashes))
-	for i := 0; i < len(txHashes); i++ {
-		txHash := txHashes[i]
+	/*
+		if len(hash) > 81 {
+			hash = hash[:81]
+		}
+		if !guards.IsTrytesOfExactLength(hash, consts.HashTrytesSize) {
+			return nil, errors.Wrapf(ErrInvalidParameter, "hash invalid: %s", hash)
+		}
 
-		tx, err := tangle_old.GetTransaction(hash)
+		txHashes, err := tangle_old.ReadTransactionHashesForAddressFromDatabase(hash)
 		if err != nil {
-			continue
+			return nil, ErrInternalError
 		}
-		if tx == nil {
-			continue
+
+		if len(txHashes) == 0 {
+			return nil, errors.Wrapf(ErrNotFound, "address %s not found", hash)
 		}
-		expTx, err := createExplorerTx(txHash, tx)
-		if err != nil {
-			return nil, err
+
+		txs := make([]*ExplorerTx, 0, len(txHashes))
+		for i := 0; i < len(txHashes); i++ {
+			txHash := txHashes[i]
+
+			tx, err := tangle_old.GetTransaction(hash)
+			if err != nil {
+				continue
+			}
+			if tx == nil {
+				continue
+			}
+			expTx, err := createExplorerTx(txHash, tx)
+			if err != nil {
+				return nil, err
+			}
+			txs = append(txs, expTx)
 		}
-		txs = append(txs, expTx)
-	}
 
-	return &ExplorerAdress{Txs: txs}, nil
+		return &ExplorerAdress{Txs: txs}, nil
+	*/
 }
diff --git a/plugins/spa/livefeed.go b/plugins/spa/livefeed.go
index 159cb4cb..7a6674c9 100644
--- a/plugins/spa/livefeed.go
+++ b/plugins/spa/livefeed.go
@@ -3,12 +3,14 @@ package spa
 import (
 	"time"
 
-	"github.com/iotaledger/goshimmer/packages/model/value_transaction"
-	"github.com/iotaledger/goshimmer/packages/shutdown"
-	tangle_plugin "github.com/iotaledger/goshimmer/plugins/tangle"
 	"github.com/iotaledger/hive.go/daemon"
 	"github.com/iotaledger/hive.go/events"
 	"github.com/iotaledger/hive.go/workerpool"
+
+	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction"
+	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transactionmetadata"
+	"github.com/iotaledger/goshimmer/packages/shutdown"
+	"github.com/iotaledger/goshimmer/plugins/tangle"
 )
 
 var liveFeedWorkerCount = 1
@@ -17,28 +19,33 @@ var liveFeedWorkerPool *workerpool.WorkerPool
 
 func configureLiveFeed() {
 	liveFeedWorkerPool = workerpool.New(func(task workerpool.Task) {
-		t := task.Param(0).(*value_transaction.ValueTransaction)
-		sendToAllWSClient(&msg{MsgTypeTx, &tx{t.GetHash(), t.GetValue()}})
+		task.Param(0).(*transaction.CachedTransaction).Consume(func(transaction *transaction.Transaction) {
+			sendToAllWSClient(&msg{MsgTypeTx, &tx{transaction.GetId().String(), 0}})
+		})
+
 		task.Return(nil)
 	}, workerpool.WorkerCount(liveFeedWorkerCount), workerpool.QueueSize(liveFeedWorkerQueueSize))
 }
 
 func runLiveFeed() {
 	newTxRateLimiter := time.NewTicker(time.Second / 10)
-	notifyNewTx := events.NewClosure(func(tx *value_transaction.ValueTransaction) {
+	notifyNewTx := events.NewClosure(func(tx *transaction.CachedTransaction, metadata *transactionmetadata.CachedTransactionMetadata) {
+		metadata.Release()
+
 		select {
 		case <-newTxRateLimiter.C:
 			liveFeedWorkerPool.TrySubmit(tx)
 		default:
+			tx.Release()
 		}
 	})
 
 	daemon.BackgroundWorker("SPA[TxUpdater]", func(shutdownSignal <-chan struct{}) {
-		tangle_plugin.Events.TransactionStored.Attach(notifyNewTx)
+		tangle.Instance.Events.TransactionAttached.Attach(notifyNewTx)
 		liveFeedWorkerPool.Start()
 		<-shutdownSignal
 		log.Info("Stopping SPA[TxUpdater] ...")
-		tangle_plugin.Events.TransactionStored.Detach(notifyNewTx)
+		tangle.Instance.Events.TransactionAttached.Detach(notifyNewTx)
 		newTxRateLimiter.Stop()
 		liveFeedWorkerPool.Stop()
 		log.Info("Stopping SPA[TxUpdater] ... done")
diff --git a/plugins/spa/plugin.go b/plugins/spa/plugin.go
index aacc71b4..89889e96 100644
--- a/plugins/spa/plugin.go
+++ b/plugins/spa/plugin.go
@@ -14,7 +14,7 @@ import (
 	"github.com/iotaledger/goshimmer/packages/shutdown"
 	"github.com/iotaledger/goshimmer/plugins/autopeering"
 	"github.com/iotaledger/goshimmer/plugins/autopeering/local"
-	"github.com/iotaledger/goshimmer/plugins/cli"
+	"github.com/iotaledger/goshimmer/plugins/banner"
 	"github.com/iotaledger/goshimmer/plugins/config"
 	"github.com/iotaledger/goshimmer/plugins/gossip"
 	"github.com/iotaledger/goshimmer/plugins/metrics"
@@ -33,7 +33,7 @@ var (
 	nodeStartAt = time.Now()
 
 	clientsMu    sync.Mutex
-	clients             = make(map[uint64]chan interface{}, 0)
+	clients             = make(map[uint64]chan interface{})
 	nextClientID uint64 = 0
 
 	wsSendWorkerCount     = 1
@@ -202,7 +202,7 @@ func currentNodeStatus() *nodestatus {
 	status.ID = local.GetInstance().ID().String()
 
 	// node status
-	status.Version = cli.AppVersion
+	status.Version = banner.AppVersion
 	status.Uptime = time.Since(nodeStartAt).Milliseconds()
 
 	// memory metrics
diff --git a/plugins/tangle/plugin.go b/plugins/tangle/plugin.go
index 53c0dc44..0d5c16d9 100644
--- a/plugins/tangle/plugin.go
+++ b/plugins/tangle/plugin.go
@@ -1,8 +1,6 @@
 package tangle
 
 import (
-	"fmt"
-
 	"github.com/iotaledger/goshimmer/packages/binary/storageprefix"
 	"github.com/iotaledger/goshimmer/packages/binary/tangle"
 	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction"
@@ -57,8 +55,6 @@ func configure(*node.Plugin) {
 
 	// setup TipSelector
 	Instance.Events.TransactionSolid.Attach(events.NewClosure(func(cachedTransaction *transaction.CachedTransaction, cachedTransactionMetadata *transactionmetadata.CachedTransactionMetadata) {
-		fmt.Println(cachedTransaction.Unwrap())
-
 		cachedTransactionMetadata.Release()
 
 		cachedTransaction.Consume(TipSelector.AddTip)
diff --git a/plugins/webapi/spammer/plugin.go b/plugins/webapi/spammer/plugin.go
index a5d9345b..a545fdeb 100644
--- a/plugins/webapi/spammer/plugin.go
+++ b/plugins/webapi/spammer/plugin.go
@@ -13,7 +13,7 @@ var transactionSpammer *spammer.Spammer
 var PLUGIN = node.NewPlugin("Spammer", node.Disabled, configure)
 
 func configure(plugin *node.Plugin) {
-	transactionSpammer = spammer.New(tangle.Instance, tangle.TipSelector)
+	transactionSpammer = spammer.New(tangle.TransactionParser, tangle.TipSelector)
 
 	webapi.Server.GET("spammer", handleRequest)
 }
diff --git a/plugins/webapi/spammer/webapi.go b/plugins/webapi/spammer/webapi.go
index f85f6e62..c62d3304 100644
--- a/plugins/webapi/spammer/webapi.go
+++ b/plugins/webapi/spammer/webapi.go
@@ -2,6 +2,7 @@ package spammer
 
 import (
 	"net/http"
+	"strconv"
 
 	"github.com/labstack/echo"
 )
@@ -21,6 +22,14 @@ func handleRequest(c echo.Context) error {
 		transactionSpammer.Shutdown()
 		transactionSpammer.Start(request.Tps)
 		return c.JSON(http.StatusOK, Response{Message: "started spamming transactions"})
+	case "burst":
+		if request.Tps == 0 {
+			return c.JSON(http.StatusBadRequest, Response{Error: "burst requires the tps to be set"})
+		}
+
+		transactionSpammer.Burst(request.Tps)
+
+		return c.JSON(http.StatusOK, Response{Message: "sent a burst of " + strconv.Itoa(request.Tps) + " transactions"})
 	case "stop":
 		transactionSpammer.Shutdown()
 		return c.JSON(http.StatusOK, Response{Message: "stopped spamming transactions"})
-- 
GitLab