From 2c6c6d455d59f25a1c78eba68b858a4b47b1f20f Mon Sep 17 00:00:00 2001 From: Hans Moog <hm@mkjc.net> Date: Sat, 15 Jun 2019 01:58:55 +0200 Subject: [PATCH] Fix: fixed memory leak --- main.go | 9 + packages/curl/batch_hasher.go | 19 +- packages/database/database.go | 2 + packages/datastructure/doubly_linked_list.go | 10 +- packages/datastructure/lru_cache.go | 34 +- packages/datastructure/lru_cache_options.go | 15 + packages/datastructure/random_map.go | 121 ++++ packages/events/event_test.go | 15 + packages/model/meta_transaction/constants.go | 12 +- .../meta_transaction/meta_transaction.go | 8 +- .../meta_transaction/meta_transaction_test.go | 4 +- packages/model/value_transaction/constants.go | 23 + .../value_transaction/value_transaction.go | 266 ++++++- .../value_transaction_test.go | 22 +- packages/ternary/conversion.go | 56 +- packages/ternary/conversion_test.go | 21 + packages/ternary/ternary.go | 8 + packages/transaction/constants.go | 45 -- packages/transaction/transaction.go | 56 -- plugins/gossip/events.go | 4 +- plugins/gossip/neighbors.go | 10 +- plugins/gossip/plugin.go | 6 - plugins/gossip/protocol_v1.go | 14 +- plugins/gossip/send_queue.go | 12 +- plugins/gossip/transaction_processor.go | 6 +- plugins/gossip/transaction_processor_test.go | 8 +- plugins/spammer/spammer.go | 86 +++ plugins/tangle/api.go | 53 +- plugins/tangle/approvers.go | 14 +- plugins/tangle/approvers_test.go | 19 +- plugins/tangle/database.go | 46 +- plugins/tangle/events.go | 3 +- plugins/tangle/solidifier.go | 73 +- plugins/tangle/transaction.go | 685 ------------------ plugins/tangle/transaction_metadata.go | 17 - plugins/tipselection/plugin.go | 23 + plugins/tipselection/tipselection.go | 23 + plugins/tipselection/tipselection_test.go | 24 + plugins/webapi-gtta/plugin.go | 35 + plugins/webapi/api.go | 9 + plugins/webapi/endpoints.go | 11 + plugins/webapi/plugin.go | 43 ++ plugins/webapi/routes.go | 9 + 43 files changed, 1043 insertions(+), 936 deletions(-) create mode 100644 packages/datastructure/lru_cache_options.go create mode 100644 packages/datastructure/random_map.go create mode 100644 packages/model/value_transaction/constants.go create mode 100644 packages/ternary/conversion_test.go delete mode 100644 packages/transaction/constants.go delete mode 100644 packages/transaction/transaction.go create mode 100644 plugins/spammer/spammer.go delete mode 100644 plugins/tangle/transaction.go create mode 100644 plugins/tipselection/plugin.go create mode 100644 plugins/tipselection/tipselection.go create mode 100644 plugins/tipselection/tipselection_test.go create mode 100644 plugins/webapi-gtta/plugin.go create mode 100644 plugins/webapi/api.go create mode 100644 plugins/webapi/endpoints.go create mode 100644 plugins/webapi/plugin.go create mode 100644 plugins/webapi/routes.go diff --git a/main.go b/main.go index 514f031a..7e8099a4 100644 --- a/main.go +++ b/main.go @@ -7,8 +7,12 @@ import ( "github.com/iotaledger/goshimmer/plugins/cli" "github.com/iotaledger/goshimmer/plugins/gossip" "github.com/iotaledger/goshimmer/plugins/gracefulshutdown" + "github.com/iotaledger/goshimmer/plugins/spammer" "github.com/iotaledger/goshimmer/plugins/statusscreen" "github.com/iotaledger/goshimmer/plugins/tangle" + "github.com/iotaledger/goshimmer/plugins/tipselection" + "github.com/iotaledger/goshimmer/plugins/webapi" + webapi_gtta "github.com/iotaledger/goshimmer/plugins/webapi-gtta" ) func main() { @@ -20,5 +24,10 @@ func main() { analysis.PLUGIN, statusscreen.PLUGIN, gracefulshutdown.PLUGIN, + tipselection.PLUGIN, + spammer.PLUGIN, + + webapi.PLUGIN, + webapi_gtta.PLUGIN, ) } diff --git a/packages/curl/batch_hasher.go b/packages/curl/batch_hasher.go index 649550a0..c139d50f 100644 --- a/packages/curl/batch_hasher.go +++ b/packages/curl/batch_hasher.go @@ -15,6 +15,7 @@ type HashRequest struct { type BatchHasher struct { hashRequests chan HashRequest + tasks chan []HashRequest hashLength int rounds int } @@ -24,13 +25,25 @@ func NewBatchHasher(hashLength int, rounds int) *BatchHasher { hashLength: hashLength, rounds: rounds, hashRequests: make(chan HashRequest), + tasks: make(chan []HashRequest, NUMBER_OF_WORKERS), } go this.startDispatcher() + this.startWorkers() return this } +func (this *BatchHasher) startWorkers() { + for i := 0; i < NUMBER_OF_WORKERS; i++ { + go func() { + for { + this.processHashes(<-this.tasks) + } + }() + } +} + func (this *BatchHasher) startDispatcher() { for { collectedHashRequests := make([]HashRequest, 0) @@ -53,7 +66,7 @@ func (this *BatchHasher) startDispatcher() { } } - go this.processHashes(collectedHashRequests) + this.tasks <- collectedHashRequests } } @@ -102,3 +115,7 @@ func (this *BatchHasher) Hash(trinary ternary.Trits) chan ternary.Trits { return hashRequest.output } + +const ( + NUMBER_OF_WORKERS = 100 +) diff --git a/packages/database/database.go b/packages/database/database.go index 9ac44a31..d1fe569f 100644 --- a/packages/database/database.go +++ b/packages/database/database.go @@ -6,6 +6,7 @@ import ( "sync" "github.com/dgraph-io/badger" + "github.com/dgraph-io/badger/options" ) var databasesByName = make(map[string]*databaseImpl) @@ -58,6 +59,7 @@ func (this *databaseImpl) Open() error { opts.ValueDir = opts.Dir opts.Logger = &logger{} opts.Truncate = true + opts.TableLoadingMode = options.MemoryMap db, err := badger.Open(opts) if err != nil { diff --git a/packages/datastructure/doubly_linked_list.go b/packages/datastructure/doubly_linked_list.go index 34793818..91d2e067 100644 --- a/packages/datastructure/doubly_linked_list.go +++ b/packages/datastructure/doubly_linked_list.go @@ -238,22 +238,26 @@ func (list *DoublyLinkedList) removeEntry(entry *DoublyLinkedListEntry) errors.I return ErrNoSuchElement.Derive("the entry is not part of the list") } + prevEntry := entry.GetPrev() nextEntry := entry.GetNext() + if nextEntry != nil { - nextEntry.SetPrev(entry.GetPrev()) + nextEntry.SetPrev(prevEntry) } if list.head == entry { list.head = nextEntry } - prevEntry := entry.GetPrev() if prevEntry != nil { - prevEntry.SetNext(entry.GetNext()) + prevEntry.SetNext(nextEntry) } if list.tail == entry { list.tail = prevEntry } + entry.SetNext(nil) + entry.SetPrev(nil) + list.count-- return nil diff --git a/packages/datastructure/lru_cache.go b/packages/datastructure/lru_cache.go index a9e9c406..c07da3ba 100644 --- a/packages/datastructure/lru_cache.go +++ b/packages/datastructure/lru_cache.go @@ -14,14 +14,23 @@ type LRUCache struct { doublyLinkedList *DoublyLinkedList capacity int size int + options *LRUCacheOptions mutex sync.RWMutex } -func NewLRUCache(capacity int) *LRUCache { +func NewLRUCache(capacity int, options ...*LRUCacheOptions) *LRUCache { + var currentOptions *LRUCacheOptions + if len(options) < 1 || options[0] == nil { + currentOptions = DEFAULT_OPTIONS + } else { + currentOptions = options[0] + } + return &LRUCache{ directory: make(map[interface{}]*DoublyLinkedListEntry, capacity), doublyLinkedList: &DoublyLinkedList{}, capacity: capacity, + options: currentOptions, } } @@ -49,7 +58,14 @@ func (cache *LRUCache) set(key interface{}, value interface{}) { if element, err := cache.doublyLinkedList.removeLastEntry(); err != nil { panic(err) } else { - delete(directory, element.value.(*lruCacheElement).key) + lruCacheElement := element.value.(*lruCacheElement) + removedElementKey := lruCacheElement.key + + delete(directory, removedElementKey) + + if cache.options.EvictionCallback != nil { + cache.options.EvictionCallback(removedElementKey, lruCacheElement.value) + } } } else { cache.size++ @@ -85,8 +101,10 @@ func (cache *LRUCache) ComputeIfPresent(key interface{}, callback func(value int if entry, exists := cache.directory[key]; exists { result = entry.GetValue().(*lruCacheElement).value - if result = callback(result); result != nil { - cache.set(key, result) + if callbackResult := callback(result); result != nil { + result = callbackResult + + cache.set(key, callbackResult) } else { if err := cache.doublyLinkedList.removeEntry(entry); err != nil { panic(err) @@ -94,6 +112,10 @@ func (cache *LRUCache) ComputeIfPresent(key interface{}, callback func(value int delete(cache.directory, key) cache.size-- + + if cache.options.EvictionCallback != nil { + cache.options.EvictionCallback(key, result) + } } } else { result = nil @@ -166,6 +188,10 @@ func (cache *LRUCache) Delete(key interface{}) bool { cache.size-- + if cache.options.EvictionCallback != nil { + cache.options.EvictionCallback(key, entry.GetValue().(*lruCacheElement).key) + } + return true } diff --git a/packages/datastructure/lru_cache_options.go b/packages/datastructure/lru_cache_options.go new file mode 100644 index 00000000..61f0e9c1 --- /dev/null +++ b/packages/datastructure/lru_cache_options.go @@ -0,0 +1,15 @@ +package datastructure + +import ( + "time" +) + +type LRUCacheOptions struct { + EvictionCallback func(key interface{}, value interface{}) + IdleTimeout time.Duration +} + +var DEFAULT_OPTIONS = &LRUCacheOptions{ + EvictionCallback: nil, + IdleTimeout: 30 * time.Second, +} diff --git a/packages/datastructure/random_map.go b/packages/datastructure/random_map.go new file mode 100644 index 00000000..7e432307 --- /dev/null +++ b/packages/datastructure/random_map.go @@ -0,0 +1,121 @@ +package datastructure + +import ( + "math/rand" + "sync" + "time" +) + +func init() { + rand.Seed(time.Now().UnixNano()) +} + +type randomMapEntry struct { + key interface{} + value interface{} + keyIndex int +} + +type RandomMap struct { + rawMap map[interface{}]*randomMapEntry + keys []interface{} + size int + mutex sync.RWMutex +} + +func NewRandomMap() *RandomMap { + return &RandomMap{ + rawMap: make(map[interface{}]*randomMapEntry), + keys: make([]interface{}, 0), + } +} + +func (rmap *RandomMap) Set(key interface{}, value interface{}) { + rmap.mutex.Lock() + + if entry, exists := rmap.rawMap[key]; exists { + entry.value = value + } else { + rmap.rawMap[key] = &randomMapEntry{ + key: key, + value: value, + keyIndex: rmap.size, + } + + rmap.keys = append(rmap.keys, key) + + rmap.size++ + } + + rmap.mutex.Unlock() +} + +func (rmap *RandomMap) Get(key interface{}) (result interface{}, exists bool) { + rmap.mutex.RLock() + + if entry, entryExists := rmap.rawMap[key]; entryExists { + result = entry.value + exists = entryExists + } + + rmap.mutex.RUnlock() + + return +} + +func (rmap *RandomMap) Delete(key interface{}) (result interface{}, exists bool) { + rmap.mutex.RLock() + + if _, entryExists := rmap.rawMap[key]; entryExists { + rmap.mutex.RUnlock() + rmap.mutex.Lock() + + if entry, entryExists := rmap.rawMap[key]; entryExists { + delete(rmap.rawMap, key) + + rmap.size-- + + if entry.keyIndex != rmap.size { + oldKey := entry.keyIndex + movedKey := rmap.keys[rmap.size] + + rmap.rawMap[movedKey].keyIndex = oldKey + + rmap.keys[oldKey] = movedKey + } + + rmap.keys = rmap.keys[:rmap.size] + + result = entry.value + exists = true + } + + rmap.mutex.Unlock() + } else { + rmap.mutex.RUnlock() + } + + return +} + +func (rmap *RandomMap) Size() (result int) { + rmap.mutex.RLock() + + result = rmap.size + + rmap.mutex.RUnlock() + + return +} + +func (rmap *RandomMap) RandomEntry() (result interface{}) { + rmap.mutex.RLock() + + if rmap.size >= 1 { + result = rmap.rawMap[rmap.keys[rand.Intn(rmap.size)]].value + } + + rmap.mutex.RUnlock() + + return +} diff --git a/packages/events/event_test.go b/packages/events/event_test.go index b153e4ac..45084ba6 100644 --- a/packages/events/event_test.go +++ b/packages/events/event_test.go @@ -3,8 +3,23 @@ package events import ( "fmt" "strconv" + "testing" ) +func BenchmarkEvent_Trigger(b *testing.B) { + event := NewEvent(intStringCaller) + + event.Attach(NewClosure(func(param1 int, param2 string) { + // do nothing just get called + })) + + b.ResetTimer() + + for i := 0; i < b.N; i++ { + event.Trigger(4, "test") + } +} + // define how the event converts the generic parameters to the typed params - ugly but go has no generics :( func intStringCaller(handler interface{}, params ...interface{}) { handler.(func(int, string))(params[0].(int), params[1].(string)) diff --git a/packages/model/meta_transaction/constants.go b/packages/model/meta_transaction/constants.go index 3db3cb05..498b5798 100644 --- a/packages/model/meta_transaction/constants.go +++ b/packages/model/meta_transaction/constants.go @@ -1,5 +1,9 @@ package meta_transaction +import ( + "github.com/iotaledger/goshimmer/packages/ternary" +) + const ( SHARD_MARKER_OFFSET = 0 TRUNK_TRANSACTION_HASH_OFFSET = SHARD_MARKER_END @@ -9,13 +13,13 @@ const ( TRANSACTION_TYPE_OFFSET = TAIL_END DATA_OFFSET = TRANSACTION_TYPE_END - SHARD_MARKER_SIZE = 12 + SHARD_MARKER_SIZE = 11 TRUNK_TRANSACTION_HASH_SIZE = 243 BRANCH_TRANSACTION_HASH_SIZE = 243 HEAD_SIZE = 1 TAIL_SIZE = 1 - TRANSACTION_TYPE_SIZE = 22 - DATA_SIZE = 5000 + TRANSACTION_TYPE_SIZE = 8 + DATA_SIZE = 6993 SHARD_MARKER_END = SHARD_MARKER_OFFSET + SHARD_MARKER_SIZE TRUNK_TRANSACTION_HASH_END = TRUNK_TRANSACTION_HASH_OFFSET + TRUNK_TRANSACTION_HASH_SIZE @@ -26,4 +30,6 @@ const ( DATA_END = DATA_OFFSET + DATA_SIZE MARSHALLED_TOTAL_SIZE = DATA_END + + BRANCH_NULL_HASH = ternary.Trinary("999999999999999999999999999999999999999999999999999999999999999999999999999999999") ) diff --git a/packages/model/meta_transaction/meta_transaction.go b/packages/model/meta_transaction/meta_transaction.go index ea8e83b8..456e7e36 100644 --- a/packages/model/meta_transaction/meta_transaction.go +++ b/packages/model/meta_transaction/meta_transaction.go @@ -53,11 +53,11 @@ func FromBytes(bytes []byte) (result *MetaTransaction) { return } -func (this *MetaTransaction) LockHasher() { +func (this *MetaTransaction) BlockHasher() { this.hasherMutex.RLock() } -func (this *MetaTransaction) UnlockHasher() { +func (this *MetaTransaction) UnblockHasher() { this.hasherMutex.RUnlock() } @@ -120,8 +120,6 @@ func (this *MetaTransaction) parseHashRelatedDetails() { this.hash = &hashTrinary this.weightMagnitude = hashTrits.TrailingZeroes() - - return } // getter for the shard marker (supports concurrency) @@ -454,7 +452,7 @@ func (this *MetaTransaction) GetBytes() (result []byte) { this.bytes = this.trits.ToBytes() this.hasherMutex.Unlock() } else { - this.hasherMutex.RUnlock() + this.bytesMutex.RUnlock() } result = make([]byte, len(this.bytes)) diff --git a/packages/model/meta_transaction/meta_transaction_test.go b/packages/model/meta_transaction/meta_transaction_test.go index dd54c2b2..1c73e4b4 100644 --- a/packages/model/meta_transaction/meta_transaction_test.go +++ b/packages/model/meta_transaction/meta_transaction_test.go @@ -41,9 +41,9 @@ func BenchmarkMetaTransaction_GetHash(b *testing.B) { var waitGroup sync.WaitGroup for i := 0; i < b.N; i++ { - go func() { - waitGroup.Add(1) + waitGroup.Add(1) + go func() { New().GetHash() waitGroup.Done() diff --git a/packages/model/value_transaction/constants.go b/packages/model/value_transaction/constants.go new file mode 100644 index 00000000..3025e1c7 --- /dev/null +++ b/packages/model/value_transaction/constants.go @@ -0,0 +1,23 @@ +package value_transaction + +const ( + ADDRESS_OFFSET = 0 + VALUE_OFFSET = ADDRESS_END + TIMESTAMP_OFFSET = VALUE_END + NONCE_OFFSET = TIMESTAMP_END + SIGNATURE_MESSAGE_FRAGMENT_OFFSET = NONCE_END + + ADDRESS_SIZE = 243 + VALUE_SIZE = 81 + TIMESTAMP_SIZE = 27 + NONCE_SIZE = 81 + SIGNATURE_MESSAGE_FRAGMENT_SIZE = 6561 + + ADDRESS_END = ADDRESS_OFFSET + ADDRESS_SIZE + VALUE_END = VALUE_OFFSET + VALUE_SIZE + TIMESTAMP_END = TIMESTAMP_OFFSET + TIMESTAMP_SIZE + NONCE_END = NONCE_OFFSET + NONCE_SIZE + SIGNATURE_MESSAGE_FRAGMENT_END = SIGNATURE_MESSAGE_FRAGMENT_OFFSET + SIGNATURE_MESSAGE_FRAGMENT_SIZE + + TOTAL_SIZE = SIGNATURE_MESSAGE_FRAGMENT_END +) diff --git a/packages/model/value_transaction/value_transaction.go b/packages/model/value_transaction/value_transaction.go index 337f003c..fe124679 100644 --- a/packages/model/value_transaction/value_transaction.go +++ b/packages/model/value_transaction/value_transaction.go @@ -1,6 +1,8 @@ package value_transaction import ( + "sync" + "github.com/iotaledger/goshimmer/packages/model/meta_transaction" "github.com/iotaledger/goshimmer/packages/ternary" ) @@ -8,7 +10,18 @@ import ( type ValueTransaction struct { *meta_transaction.MetaTransaction - data ternary.Trits + address *ternary.Trinary + addressMutex sync.RWMutex + value *int64 + valueMutex sync.RWMutex + timestamp *uint + timestampMutex sync.RWMutex + nonce *ternary.Trinary + nonceMutex sync.RWMutex + signatureMessageFragment *ternary.Trinary + signatureMessageFragmentMutex sync.RWMutex + + trits ternary.Trits } func New() (result *ValueTransaction) { @@ -16,7 +29,256 @@ func New() (result *ValueTransaction) { MetaTransaction: meta_transaction.New(), } - result.data = result.MetaTransaction.GetData() + result.trits = result.MetaTransaction.GetData() + + return +} + +func FromMetaTransaction(metaTransaction *meta_transaction.MetaTransaction) *ValueTransaction { + return &ValueTransaction{ + MetaTransaction: metaTransaction, + } +} + +func FromBytes(bytes []byte) (result *ValueTransaction) { + result = &ValueTransaction{ + MetaTransaction: meta_transaction.FromTrits(ternary.BytesToTrits(bytes)[:meta_transaction.MARSHALLED_TOTAL_SIZE]), + } return } + +// getter for the address (supports concurrency) +func (this *ValueTransaction) GetAddress() (result ternary.Trinary) { + this.addressMutex.RLock() + if this.address == nil { + this.addressMutex.RUnlock() + this.addressMutex.Lock() + defer this.addressMutex.Unlock() + if this.address == nil { + address := this.trits[ADDRESS_OFFSET:ADDRESS_END].ToTrinary() + + this.address = &address + } + } else { + defer this.addressMutex.RUnlock() + } + + result = *this.address + + return +} + +// setter for the address (supports concurrency) +func (this *ValueTransaction) SetAddress(address ternary.Trinary) bool { + this.addressMutex.RLock() + if this.address == nil || *this.address != address { + this.addressMutex.RUnlock() + this.addressMutex.Lock() + defer this.addressMutex.Unlock() + if this.address == nil || *this.address != address { + this.address = &address + + this.BlockHasher() + copy(this.trits[ADDRESS_OFFSET:ADDRESS_END], address.ToTrits()[:ADDRESS_SIZE]) + this.UnblockHasher() + + this.SetModified(true) + this.ReHash() + + return true + } + } else { + this.addressMutex.RUnlock() + } + + return false +} + +// getter for the value (supports concurrency) +func (this *ValueTransaction) GetValue() (result int64) { + this.valueMutex.RLock() + if this.value == nil { + this.valueMutex.RUnlock() + this.valueMutex.Lock() + defer this.valueMutex.Unlock() + if this.value == nil { + value := this.trits[VALUE_OFFSET:VALUE_END].ToInt64() + + this.value = &value + } + } else { + defer this.valueMutex.RUnlock() + } + + result = *this.value + + return +} + +// setter for the value (supports concurrency) +func (this *ValueTransaction) SetValue(value int64) bool { + this.valueMutex.RLock() + if this.value == nil || *this.value != value { + this.valueMutex.RUnlock() + this.valueMutex.Lock() + defer this.valueMutex.Unlock() + if this.value == nil || *this.value != value { + this.value = &value + + this.BlockHasher() + copy(this.trits[VALUE_OFFSET:VALUE_END], ternary.Int64ToTrits(value)[:VALUE_SIZE]) + this.UnblockHasher() + + this.SetModified(true) + this.ReHash() + + return true + } + } else { + this.valueMutex.RUnlock() + } + + return false +} + +// getter for the timestamp (supports concurrency) +func (this *ValueTransaction) GetTimestamp() (result uint) { + this.timestampMutex.RLock() + if this.timestamp == nil { + this.timestampMutex.RUnlock() + this.timestampMutex.Lock() + defer this.timestampMutex.Unlock() + if this.timestamp == nil { + timestamp := this.trits[TIMESTAMP_OFFSET:TIMESTAMP_END].ToUint() + + this.timestamp = ×tamp + } + } else { + defer this.timestampMutex.RUnlock() + } + + result = *this.timestamp + + return +} + +// setter for the timestamp (supports concurrency) +func (this *ValueTransaction) SetTimestamp(timestamp uint) bool { + this.timestampMutex.RLock() + if this.timestamp == nil || *this.timestamp != timestamp { + this.timestampMutex.RUnlock() + this.timestampMutex.Lock() + defer this.timestampMutex.Unlock() + if this.timestamp == nil || *this.timestamp != timestamp { + this.timestamp = ×tamp + + this.BlockHasher() + copy(this.trits[TIMESTAMP_OFFSET:TIMESTAMP_END], ternary.UintToTrits(timestamp)[:TIMESTAMP_SIZE]) + this.UnblockHasher() + + this.SetModified(true) + this.ReHash() + + return true + } + } else { + this.timestampMutex.RUnlock() + } + + return false +} + +// getter for the nonce (supports concurrency) +func (this *ValueTransaction) GetNonce() (result ternary.Trinary) { + this.nonceMutex.RLock() + if this.nonce == nil { + this.nonceMutex.RUnlock() + this.nonceMutex.Lock() + defer this.nonceMutex.Unlock() + if this.nonce == nil { + nonce := this.trits[NONCE_OFFSET:NONCE_END].ToTrinary() + + this.nonce = &nonce + } + } else { + defer this.nonceMutex.RUnlock() + } + + result = *this.nonce + + return +} + +// setter for the nonce (supports concurrency) +func (this *ValueTransaction) SetNonce(nonce ternary.Trinary) bool { + this.nonceMutex.RLock() + if this.nonce == nil || *this.nonce != nonce { + this.nonceMutex.RUnlock() + this.nonceMutex.Lock() + defer this.nonceMutex.Unlock() + if this.nonce == nil || *this.nonce != nonce { + this.nonce = &nonce + + this.BlockHasher() + copy(this.trits[NONCE_OFFSET:NONCE_END], nonce.ToTrits()[:NONCE_SIZE]) + this.UnblockHasher() + + this.SetModified(true) + this.ReHash() + + return true + } + } else { + this.nonceMutex.RUnlock() + } + + return false +} + +// getter for the signatureMessageFragmetn (supports concurrency) +func (this *ValueTransaction) GetSignatureMessageFragment() (result ternary.Trinary) { + this.signatureMessageFragmentMutex.RLock() + if this.signatureMessageFragment == nil { + this.signatureMessageFragmentMutex.RUnlock() + this.signatureMessageFragmentMutex.Lock() + defer this.signatureMessageFragmentMutex.Unlock() + if this.signatureMessageFragment == nil { + signatureMessageFragment := this.trits[SIGNATURE_MESSAGE_FRAGMENT_OFFSET:SIGNATURE_MESSAGE_FRAGMENT_END].ToTrinary() + + this.signatureMessageFragment = &signatureMessageFragment + } + } else { + defer this.signatureMessageFragmentMutex.RUnlock() + } + + result = *this.signatureMessageFragment + + return +} + +// setter for the nonce (supports concurrency) +func (this *ValueTransaction) SetSignatureMessageFragment(signatureMessageFragment ternary.Trinary) bool { + this.signatureMessageFragmentMutex.RLock() + if this.signatureMessageFragment == nil || *this.signatureMessageFragment != signatureMessageFragment { + this.signatureMessageFragmentMutex.RUnlock() + this.signatureMessageFragmentMutex.Lock() + defer this.signatureMessageFragmentMutex.Unlock() + if this.signatureMessageFragment == nil || *this.signatureMessageFragment != signatureMessageFragment { + this.signatureMessageFragment = &signatureMessageFragment + + this.BlockHasher() + copy(this.trits[SIGNATURE_MESSAGE_FRAGMENT_OFFSET:SIGNATURE_MESSAGE_FRAGMENT_END], signatureMessageFragment.ToTrits()[:SIGNATURE_MESSAGE_FRAGMENT_SIZE]) + this.UnblockHasher() + + this.SetModified(true) + this.ReHash() + + return true + } + } else { + this.signatureMessageFragmentMutex.RUnlock() + } + + return false +} diff --git a/packages/model/value_transaction/value_transaction_test.go b/packages/model/value_transaction/value_transaction_test.go index f81aa1ab..eb8310be 100644 --- a/packages/model/value_transaction/value_transaction_test.go +++ b/packages/model/value_transaction/value_transaction_test.go @@ -8,27 +8,15 @@ import ( "github.com/magiconair/properties/assert" ) -func TestMetaTransaction_SettersGetters(t *testing.T) { - shardMarker := ternary.Trinary("NPHTQORL9XKA") - trunkTransactionHash := ternary.Trinary("99999999999999999999999999999999999999999999999999999999999999999999999999999999A") - branchTransactionHash := ternary.Trinary("99999999999999999999999999999999999999999999999999999999999999999999999999999999B") - head := true - tail := true +func TestValueTransaction_SettersGetters(t *testing.T) { + address := ternary.Trinary("A9999999999999999999999999999999999999999999999999999999999999999999999999999999F") transaction := New() - transaction.SetShardMarker(shardMarker) - transaction.SetTrunkTransactionHash(trunkTransactionHash) - transaction.SetBranchTransactionHash(branchTransactionHash) - transaction.SetHead(head) - transaction.SetTail(tail) + transaction.SetAddress(address) - assert.Equal(t, transaction.GetWeightMagnitude(), 0) - assert.Equal(t, transaction.GetShardMarker(), shardMarker) - assert.Equal(t, transaction.GetTrunkTransactionHash(), trunkTransactionHash) - assert.Equal(t, transaction.GetBranchTransactionHash(), branchTransactionHash) - assert.Equal(t, transaction.GetHead(), head) - assert.Equal(t, transaction.GetTail(), tail) + assert.Equal(t, transaction.GetAddress(), address) //assert.Equal(t, transaction.GetHash(), FromBytes(transaction.GetBytes()).GetHash()) fmt.Println(transaction.GetHash()) + fmt.Println(transaction.GetAddress()) } diff --git a/packages/ternary/conversion.go b/packages/ternary/conversion.go index afb96cf7..7cc01646 100644 --- a/packages/ternary/conversion.go +++ b/packages/ternary/conversion.go @@ -1,6 +1,8 @@ package ternary -import "bytes" +import ( + "bytes" +) const ( NUMBER_OF_TRITS_IN_A_BYTE = 5 @@ -111,6 +113,58 @@ func BytesToTrits(bytes []byte) Trits { return trits } +func Int64ToTrits(value int64) (result Trits) { + negative := value < 0 + shiftedValue := value >> 63 + valueAbs := (value ^ shiftedValue) - shiftedValue + + for valueAbs != 0 { + trit := Trit((valueAbs+1)%3 - 1) + if negative { + trit = -trit + } + result = append(result, trit) + valueAbs++ + valueAbs /= 3 + } + + for i := len(result); i < 81; i++ { + result = append(result, 0) + } + + return +} + +func UintToTrits(value uint) (result Trits) { + for value != 0 { + trit := Trit((value+1)%3 - 1) + result = append(result, trit) + value++ + value /= 3 + } + + for i := len(result); i < 27; i++ { + result = append(result, 0) + } + + return +} + +func Uint64ToTrits(value uint64) (result Trits) { + for value != 0 { + trit := Trit((value+1)%3 - 1) + result = append(result, trit) + value++ + value /= 3 + } + + for i := len(result); i < 81; i++ { + result = append(result, 0) + } + + return +} + func TritsToString(trits Trits, offset int, size int) string { var buffer bytes.Buffer for i := 0; i < (size+NUMBER_OF_TRITS_IN_A_TRYTE-1)/NUMBER_OF_TRITS_IN_A_TRYTE; i++ { diff --git a/packages/ternary/conversion_test.go b/packages/ternary/conversion_test.go new file mode 100644 index 00000000..d7d16655 --- /dev/null +++ b/packages/ternary/conversion_test.go @@ -0,0 +1,21 @@ +package ternary + +import ( + "testing" + + "github.com/magiconair/properties/assert" +) + +func TestInt64Conversion(t *testing.T) { + assert.Equal(t, Int64ToTrits(9223372036854775807).ToInt64(), int64(9223372036854775807)) + assert.Equal(t, Int64ToTrits(1337).ToInt64(), int64(1337)) + assert.Equal(t, Int64ToTrits(0).ToInt64(), int64(0)) + assert.Equal(t, Int64ToTrits(-1337).ToInt64(), int64(-1337)) + assert.Equal(t, Int64ToTrits(-9223372036854775808).ToInt64(), int64(-9223372036854775808)) +} + +func TestUInt64Conversion(t *testing.T) { + assert.Equal(t, Uint64ToTrits(18446744073709551615).ToUint64(), uint64(18446744073709551615)) + assert.Equal(t, Uint64ToTrits(1337).ToUint64(), uint64(1337)) + assert.Equal(t, Uint64ToTrits(0).ToUint64(), uint64(0)) +} diff --git a/packages/ternary/ternary.go b/packages/ternary/ternary.go index 1e4ba027..745ca354 100644 --- a/packages/ternary/ternary.go +++ b/packages/ternary/ternary.go @@ -66,6 +66,14 @@ func (this Trits) TrailingZeroes() int { return zeros } +func (this Trits) ToUint() (result uint) { + for i := len(this) - 1; i >= 0; i-- { + result = result*3 + uint(this[i]) + } + + return +} + func (this Trits) ToInt64() int64 { var val int64 for i := len(this) - 1; i >= 0; i-- { diff --git a/packages/transaction/constants.go b/packages/transaction/constants.go deleted file mode 100644 index dd329015..00000000 --- a/packages/transaction/constants.go +++ /dev/null @@ -1,45 +0,0 @@ -package transaction - -const ( - // sizes of the transaction fields - SIGNATURE_MESSAGE_FRAGMENT_SIZE = 6561 - ADDRESS_SIZE = 243 - VALUE_SIZE = 81 - TIMESTAMP_SIZE = 27 - CURRENT_INDEX_SIZE = 27 - LATEST_INDEX_SIZE = 27 - BUNDLE_HASH_SIZE = 243 - TRUNK_TRANSACTION_HASH_SIZE = 243 - BRANCH_TRANSACTION_HASH_SIZE = 243 - TAG_SIZE = 81 - NONCE_SIZE = 81 - - // offsets of the transaction fields - SIGNATURE_MESSAGE_FRAGMENT_OFFSET = 0 - ADDRESS_OFFSET = SIGNATURE_MESSAGE_FRAGMENT_END - VALUE_OFFSET = ADDRESS_END - TIMESTAMP_OFFSET = VALUE_END - CURRENT_INDEX_OFFSET = TIMESTAMP_END - LATEST_INDEX_OFFSET = CURRENT_INDEX_END - BUNDLE_HASH_OFFSET = LATEST_INDEX_END - TRUNK_TRANSACTION_HASH_OFFSET = BUNDLE_HASH_END - BRANCH_TRANSACTION_HASH_OFFSET = TRUNK_TRANSACTION_HASH_END - TAG_OFFSET = BRANCH_TRANSACTION_HASH_END - NONCE_OFFSET = TAG_END - - // ends of the transaction fields - SIGNATURE_MESSAGE_FRAGMENT_END = SIGNATURE_MESSAGE_FRAGMENT_OFFSET + SIGNATURE_MESSAGE_FRAGMENT_SIZE - ADDRESS_END = ADDRESS_OFFSET + ADDRESS_SIZE - VALUE_END = VALUE_OFFSET + VALUE_SIZE - TIMESTAMP_END = TIMESTAMP_OFFSET + TIMESTAMP_SIZE - CURRENT_INDEX_END = CURRENT_INDEX_OFFSET + CURRENT_INDEX_SIZE - LATEST_INDEX_END = LATEST_INDEX_OFFSET + LATEST_INDEX_SIZE - BUNDLE_HASH_END = BUNDLE_HASH_OFFSET + BUNDLE_HASH_SIZE - TRUNK_TRANSACTION_HASH_END = TRUNK_TRANSACTION_HASH_OFFSET + TRUNK_TRANSACTION_HASH_SIZE - BRANCH_TRANSACTION_HASH_END = BRANCH_TRANSACTION_HASH_OFFSET + BRANCH_TRANSACTION_HASH_SIZE - TAG_END = TAG_OFFSET + TAG_SIZE - NONCE_END = NONCE_OFFSET + NONCE_SIZE - - // the full size of a transaction - MARSHALLED_TOTAL_SIZE = NONCE_END -) diff --git a/packages/transaction/transaction.go b/packages/transaction/transaction.go deleted file mode 100644 index 9d703309..00000000 --- a/packages/transaction/transaction.go +++ /dev/null @@ -1,56 +0,0 @@ -package transaction - -import ( - "github.com/iotaledger/goshimmer/packages/curl" - "github.com/iotaledger/goshimmer/packages/ternary" -) - -type Transaction struct { - SignatureMessageFragment ternary.Trits - Address ternary.Trits - Value ternary.Trits - Timestamp ternary.Trits - CurrentIndex ternary.Trits - LatestIndex ternary.Trits - BundleHash ternary.Trits - TrunkTransactionHash ternary.Trits - BranchTransactionHash ternary.Trits - Tag ternary.Trits - Nonce ternary.Trits - - Hash ternary.Trits - WeightMagnitude int - Bytes []byte - Trits ternary.Trits -} - -func FromTrits(trits ternary.Trits, optionalHash ...ternary.Trits) *Transaction { - hash := <-curl.CURLP81.Hash(trits) - - transaction := &Transaction{ - SignatureMessageFragment: trits[SIGNATURE_MESSAGE_FRAGMENT_OFFSET:SIGNATURE_MESSAGE_FRAGMENT_END], - Address: trits[ADDRESS_OFFSET:ADDRESS_END], - Value: trits[VALUE_OFFSET:VALUE_END], - Timestamp: trits[TIMESTAMP_OFFSET:TIMESTAMP_END], - CurrentIndex: trits[CURRENT_INDEX_OFFSET:CURRENT_INDEX_END], - LatestIndex: trits[LATEST_INDEX_OFFSET:LATEST_INDEX_END], - BundleHash: trits[BUNDLE_HASH_OFFSET:BUNDLE_HASH_END], - TrunkTransactionHash: trits[TRUNK_TRANSACTION_HASH_OFFSET:TRUNK_TRANSACTION_HASH_END], - BranchTransactionHash: trits[BRANCH_TRANSACTION_HASH_OFFSET:BRANCH_TRANSACTION_HASH_END], - Tag: trits[TAG_OFFSET:TAG_END], - Nonce: trits[NONCE_OFFSET:NONCE_END], - - Hash: hash, - WeightMagnitude: hash.TrailingZeroes(), - Trits: trits, - } - - return transaction -} - -func FromBytes(bytes []byte) *Transaction { - transaction := FromTrits(ternary.BytesToTrits(bytes)[:MARSHALLED_TOTAL_SIZE]) - transaction.Bytes = bytes - - return transaction -} diff --git a/plugins/gossip/events.go b/plugins/gossip/events.go index 765e106f..6d318382 100644 --- a/plugins/gossip/events.go +++ b/plugins/gossip/events.go @@ -4,8 +4,8 @@ import ( "github.com/iotaledger/goshimmer/packages/errors" "github.com/iotaledger/goshimmer/packages/events" "github.com/iotaledger/goshimmer/packages/identity" + "github.com/iotaledger/goshimmer/packages/model/meta_transaction" "github.com/iotaledger/goshimmer/packages/network" - "github.com/iotaledger/goshimmer/packages/transaction" ) var Events = pluginEvents{ @@ -93,5 +93,5 @@ func dataCaller(handler interface{}, params ...interface{}) { } func transactionCaller(handler interface{}, params ...interface{}) { - handler.(func(*transaction.Transaction))(params[0].(*transaction.Transaction)) + handler.(func(*meta_transaction.MetaTransaction))(params[0].(*meta_transaction.MetaTransaction)) } diff --git a/plugins/gossip/neighbors.go b/plugins/gossip/neighbors.go index 47aacbbd..f268cf4c 100644 --- a/plugins/gossip/neighbors.go +++ b/plugins/gossip/neighbors.go @@ -181,7 +181,7 @@ func (neighbor *Neighbor) Marshal() []byte { } func (neighbor *Neighbor) Equals(other *Neighbor) bool { - return neighbor.Identity.StringIdentifier == neighbor.Identity.StringIdentifier && + return neighbor.Identity.StringIdentifier == other.Identity.StringIdentifier && neighbor.Port == other.Port && neighbor.Address.String() == other.Address.String() } @@ -195,11 +195,11 @@ func AddNeighbor(newNeighbor *Neighbor) { Events.AddNeighbor.Trigger(newNeighbor) } else { if !newNeighbor.Equals(neighbor) { - neighbor.Identity = neighbor.Identity - neighbor.Port = neighbor.Port - neighbor.Address = neighbor.Address + neighbor.Identity = newNeighbor.Identity + neighbor.Port = newNeighbor.Port + neighbor.Address = newNeighbor.Address - Events.UpdateNeighbor.Trigger(newNeighbor) + Events.UpdateNeighbor.Trigger(neighbor) } } } diff --git a/plugins/gossip/plugin.go b/plugins/gossip/plugin.go index d647a906..0f02bd14 100644 --- a/plugins/gossip/plugin.go +++ b/plugins/gossip/plugin.go @@ -1,9 +1,7 @@ package gossip import ( - "github.com/iotaledger/goshimmer/packages/events" "github.com/iotaledger/goshimmer/packages/node" - "github.com/iotaledger/goshimmer/packages/transaction" ) var PLUGIN = node.NewPlugin("Gossip", configure, run) @@ -12,10 +10,6 @@ func configure(plugin *node.Plugin) { configureNeighbors(plugin) configureServer(plugin) configureSendQueue(plugin) - - Events.ReceiveTransaction.Attach(events.NewClosure(func(transaction *transaction.Transaction) { - - })) } func run(plugin *node.Plugin) { diff --git a/plugins/gossip/protocol_v1.go b/plugins/gossip/protocol_v1.go index 9b7677b3..62849297 100644 --- a/plugins/gossip/protocol_v1.go +++ b/plugins/gossip/protocol_v1.go @@ -9,8 +9,8 @@ import ( "github.com/iotaledger/goshimmer/packages/errors" "github.com/iotaledger/goshimmer/packages/events" "github.com/iotaledger/goshimmer/packages/identity" + "github.com/iotaledger/goshimmer/packages/model/meta_transaction" "github.com/iotaledger/goshimmer/packages/ternary" - "github.com/iotaledger/goshimmer/packages/transaction" ) // region protocolV1 /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -45,7 +45,7 @@ func protocolV1(protocol *protocol) errors.IdentifiableError { return nil } -func sendTransactionV1(protocol *protocol, tx *transaction.Transaction) { +func sendTransactionV1(protocol *protocol, tx *meta_transaction.MetaTransaction) { if _, ok := protocol.SendState.(*dispatchStateV1); ok { protocol.sendMutex.Lock() defer protocol.sendMutex.Unlock() @@ -303,7 +303,7 @@ type transactionStateV1 struct { func newTransactionStateV1(protocol *protocol) *transactionStateV1 { return &transactionStateV1{ protocol: protocol, - buffer: make([]byte, transaction.MARSHALLED_TOTAL_SIZE/ternary.NUMBER_OF_TRITS_IN_A_BYTE), + buffer: make([]byte, meta_transaction.MARSHALLED_TOTAL_SIZE/ternary.NUMBER_OF_TRITS_IN_A_BYTE), offset: 0, } } @@ -312,10 +312,10 @@ func (state *transactionStateV1) Receive(data []byte, offset int, length int) (i bytesRead := byteutils.ReadAvailableBytesToBuffer(state.buffer, state.offset, data, offset, length) state.offset += bytesRead - if state.offset == transaction.MARSHALLED_TOTAL_SIZE/ternary.NUMBER_OF_TRITS_IN_A_BYTE { + if state.offset == meta_transaction.MARSHALLED_TOTAL_SIZE/ternary.NUMBER_OF_TRITS_IN_A_BYTE { protocol := state.protocol - transactionData := make([]byte, transaction.MARSHALLED_TOTAL_SIZE/ternary.NUMBER_OF_TRITS_IN_A_BYTE) + transactionData := make([]byte, meta_transaction.MARSHALLED_TOTAL_SIZE/ternary.NUMBER_OF_TRITS_IN_A_BYTE) copy(transactionData, state.buffer) protocol.Events.ReceiveTransactionData.Trigger(transactionData) @@ -330,10 +330,10 @@ func (state *transactionStateV1) Receive(data []byte, offset int, length int) (i } func (state *transactionStateV1) Send(param interface{}) errors.IdentifiableError { - if tx, ok := param.(*transaction.Transaction); ok { + if tx, ok := param.(*meta_transaction.MetaTransaction); ok { protocol := state.protocol - if _, err := protocol.Conn.Write(tx.Bytes); err != nil { + if _, err := protocol.Conn.Write(tx.GetBytes()); err != nil { return ErrSendFailed.Derive(err, "failed to send transaction") } diff --git a/plugins/gossip/send_queue.go b/plugins/gossip/send_queue.go index 33709527..354c0c3b 100644 --- a/plugins/gossip/send_queue.go +++ b/plugins/gossip/send_queue.go @@ -5,8 +5,8 @@ import ( "github.com/iotaledger/goshimmer/packages/daemon" "github.com/iotaledger/goshimmer/packages/events" + "github.com/iotaledger/goshimmer/packages/model/meta_transaction" "github.com/iotaledger/goshimmer/packages/node" - "github.com/iotaledger/goshimmer/packages/transaction" ) // region plugin module setup ////////////////////////////////////////////////////////////////////////////////////////// @@ -63,11 +63,11 @@ func runSendQueue(plugin *node.Plugin) { // region public api /////////////////////////////////////////////////////////////////////////////////////////////////// -func SendTransaction(transaction *transaction.Transaction) { +func SendTransaction(transaction *meta_transaction.MetaTransaction) { sendQueue <- transaction } -func (neighbor *Neighbor) SendTransaction(transaction *transaction.Transaction) { +func (neighbor *Neighbor) SendTransaction(transaction *meta_transaction.MetaTransaction) { if queue, exists := neighborQueues[neighbor.Identity.StringIdentifier]; exists { select { case queue.queue <- transaction: @@ -87,7 +87,7 @@ func setupEventHandlers(neighbor *Neighbor) { neighbor.Events.ProtocolConnectionEstablished.Attach(events.NewClosure(func(protocol *protocol) { queue := &neighborQueue{ protocol: protocol, - queue: make(chan *transaction.Transaction, SEND_QUEUE_SIZE), + queue: make(chan *meta_transaction.MetaTransaction, SEND_QUEUE_SIZE), disconnectChan: make(chan int, 1), } @@ -135,7 +135,7 @@ func startNeighborSendQueue(neighborQueue *neighborQueue) { type neighborQueue struct { protocol *protocol - queue chan *transaction.Transaction + queue chan *meta_transaction.MetaTransaction disconnectChan chan int } @@ -147,7 +147,7 @@ var neighborQueues = make(map[string]*neighborQueue) var connectedNeighborsMutex sync.RWMutex -var sendQueue = make(chan *transaction.Transaction, SEND_QUEUE_SIZE) +var sendQueue = make(chan *meta_transaction.MetaTransaction, SEND_QUEUE_SIZE) const ( SEND_QUEUE_SIZE = 500 diff --git a/plugins/gossip/transaction_processor.go b/plugins/gossip/transaction_processor.go index 9a96fdb2..742ac369 100644 --- a/plugins/gossip/transaction_processor.go +++ b/plugins/gossip/transaction_processor.go @@ -2,14 +2,14 @@ package gossip import ( "github.com/iotaledger/goshimmer/packages/filter" - "github.com/iotaledger/goshimmer/packages/transaction" + "github.com/iotaledger/goshimmer/packages/model/meta_transaction" ) // region public api /////////////////////////////////////////////////////////////////////////////////////////////////// func ProcessReceivedTransactionData(transactionData []byte) { if transactionFilter.Add(transactionData) { - Events.ReceiveTransaction.Trigger(transaction.FromBytes(transactionData)) + Events.ReceiveTransaction.Trigger(meta_transaction.FromBytes(transactionData)) } } @@ -20,7 +20,7 @@ func ProcessReceivedTransactionData(transactionData []byte) { var transactionFilter = filter.NewByteArrayFilter(TRANSACTION_FILTER_SIZE) const ( - TRANSACTION_FILTER_SIZE = 5000 + TRANSACTION_FILTER_SIZE = 500 ) // endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/plugins/gossip/transaction_processor_test.go b/plugins/gossip/transaction_processor_test.go index 76862140..c07602df 100644 --- a/plugins/gossip/transaction_processor_test.go +++ b/plugins/gossip/transaction_processor_test.go @@ -4,12 +4,12 @@ import ( "sync" "testing" + "github.com/iotaledger/goshimmer/packages/model/meta_transaction" "github.com/iotaledger/goshimmer/packages/ternary" - "github.com/iotaledger/goshimmer/packages/transaction" ) func BenchmarkProcessSimilarTransactionsFiltered(b *testing.B) { - byteArray := setupTransaction(transaction.MARSHALLED_TOTAL_SIZE / ternary.NUMBER_OF_TRITS_IN_A_BYTE) + byteArray := setupTransaction(meta_transaction.MARSHALLED_TOTAL_SIZE / ternary.NUMBER_OF_TRITS_IN_A_BYTE) b.ResetTimer() @@ -19,7 +19,7 @@ func BenchmarkProcessSimilarTransactionsFiltered(b *testing.B) { } func BenchmarkProcessSimilarTransactionsUnfiltered(b *testing.B) { - byteArray := setupTransaction(transaction.MARSHALLED_TOTAL_SIZE / ternary.NUMBER_OF_TRITS_IN_A_BYTE) + byteArray := setupTransaction(meta_transaction.MARSHALLED_TOTAL_SIZE / ternary.NUMBER_OF_TRITS_IN_A_BYTE) b.ResetTimer() @@ -29,7 +29,7 @@ func BenchmarkProcessSimilarTransactionsUnfiltered(b *testing.B) { wg.Add(1) go func() { - Events.ReceiveTransaction.Trigger(transaction.FromBytes(byteArray)) + Events.ReceiveTransaction.Trigger(meta_transaction.FromBytes(byteArray)) wg.Done() }() diff --git a/plugins/spammer/spammer.go b/plugins/spammer/spammer.go new file mode 100644 index 00000000..44985dea --- /dev/null +++ b/plugins/spammer/spammer.go @@ -0,0 +1,86 @@ +package spammer + +import ( + "fmt" + "runtime" + "strconv" + "sync/atomic" + "time" + + "github.com/iotaledger/goshimmer/packages/daemon" + "github.com/iotaledger/goshimmer/packages/events" + "github.com/iotaledger/goshimmer/packages/model/meta_transaction" + "github.com/iotaledger/goshimmer/packages/model/value_transaction" + "github.com/iotaledger/goshimmer/packages/node" + "github.com/iotaledger/goshimmer/plugins/gossip" + "github.com/iotaledger/goshimmer/plugins/tangle" + "github.com/iotaledger/goshimmer/plugins/tipselection" +) + +var PLUGIN = node.NewPlugin("Spammer", configure, run) + +func bToMb(b uint64) uint64 { + return b / 1024 / 1024 +} + +func configure(plugin *node.Plugin) { + tpsCounter := 0 + solidCounter := 0 + var receivedCounter uint64 + + gossip.Events.ReceiveTransaction.Attach(events.NewClosure(func(transaction *meta_transaction.MetaTransaction) { + atomic.AddUint64(&receivedCounter, 1) + })) + + go func() { + for { + select { + case <-daemon.ShutdownSignal: + return + + case <-time.After(1 * time.Second): + fmt.Println("RECEIVED", receivedCounter, " / TPS "+strconv.Itoa(tpsCounter)+" / SOLID "+strconv.Itoa(solidCounter), " / TIPS ", tipselection.GetTipsCount()) + fmt.Println(runtime.NumGoroutine()) + + var m runtime.MemStats + runtime.ReadMemStats(&m) + // For info on each, see: https://golang.org/pkg/runtime/#MemStats + fmt.Printf("Alloc = %v MiB", bToMb(m.Alloc)) + fmt.Printf("\tTotalAlloc = %v MiB", bToMb(m.TotalAlloc)) + fmt.Printf("\tSys = %v MiB", bToMb(m.Sys)) + fmt.Printf("\tNumGC = %v\n", m.NumGC) + + tpsCounter = 0 + } + } + }() + + tangle.Events.TransactionSolid.Attach(events.NewClosure(func(transaction *value_transaction.ValueTransaction) { + solidCounter++ + tpsCounter++ + })) +} + +func run(plugin *node.Plugin) { + daemon.BackgroundWorker(func() { + for { + transactionCount := 100000 + + for i := 0; i < transactionCount; i++ { + select { + case <-daemon.ShutdownSignal: + return + default: + tx := value_transaction.New() + tx.SetValue(int64(i + 1)) + tx.SetBranchTransactionHash(tipselection.GetRandomTip()) + tx.SetTrunkTransactionHash(tipselection.GetRandomTip()) + + gossip.Events.ReceiveTransaction.Trigger(tx.MetaTransaction) + } + } + + //time.Sleep(5 * time.Second) + } + }) +} diff --git a/plugins/tangle/api.go b/plugins/tangle/api.go index 2a69fcc5..3e40d0ca 100644 --- a/plugins/tangle/api.go +++ b/plugins/tangle/api.go @@ -3,14 +3,27 @@ package tangle import ( "github.com/iotaledger/goshimmer/packages/datastructure" "github.com/iotaledger/goshimmer/packages/errors" + "github.com/iotaledger/goshimmer/packages/model/value_transaction" "github.com/iotaledger/goshimmer/packages/ternary" ) // region transaction api ////////////////////////////////////////////////////////////////////////////////////////////// -var transactionCache = datastructure.NewLRUCache(TRANSACTION_CACHE_SIZE) +var transactionCache = datastructure.NewLRUCache(TRANSACTION_CACHE_SIZE, &datastructure.LRUCacheOptions{ + EvictionCallback: func(key interface{}, value interface{}) { + go func(evictedTransaction *value_transaction.ValueTransaction) { + if err := storeTransactionInDatabase(evictedTransaction); err != nil { + panic(err) + } + }(value.(*value_transaction.ValueTransaction)) + }, +}) + +func StoreTransaction(transaction *value_transaction.ValueTransaction) { + transactionCache.Set(transaction.GetHash(), transaction) +} -func GetTransaction(transactionHash ternary.Trinary, computeIfAbsent ...func(ternary.Trinary) *Transaction) (result *Transaction, err errors.IdentifiableError) { +func GetTransaction(transactionHash ternary.Trinary, computeIfAbsent ...func(ternary.Trinary) *value_transaction.ValueTransaction) (result *value_transaction.ValueTransaction, err errors.IdentifiableError) { if cacheResult := transactionCache.ComputeIfAbsent(transactionHash, func() interface{} { if transaction, dbErr := getTransactionFromDatabase(transactionHash); dbErr != nil { err = dbErr @@ -26,7 +39,7 @@ func GetTransaction(transactionHash ternary.Trinary, computeIfAbsent ...func(ter return nil } }); cacheResult != nil { - result = cacheResult.(*Transaction) + result = cacheResult.(*value_transaction.ValueTransaction) } return @@ -46,20 +59,26 @@ func ContainsTransaction(transactionHash ternary.Trinary) (result bool, err erro // region transactionmetadata api ////////////////////////////////////////////////////////////////////////////////////// -var metadataCache = datastructure.NewLRUCache(METADATA_CACHE_SIZE) - -func GetTransactionMetadata(transactionHash ternary.Trinary) (result *TransactionMetadata, err errors.IdentifiableError) { - result = metadataCache.ComputeIfAbsent(transactionHash, func() interface{} { - if metadata, dbErr := getTransactionMetadataFromDatabase(transactionHash); dbErr != nil { - err = dbErr - - return nil - } else if metadata != nil { - return metadata +var metadataCache = datastructure.NewLRUCache(METADATA_CACHE_SIZE, &datastructure.LRUCacheOptions{ + EvictionCallback: func(key interface{}, value interface{}) { + go func(evictedMetadata *TransactionMetadata) { + if err := storeTransactionMetadataInDatabase(evictedMetadata); err != nil { + panic(err) + } + }(value.(*TransactionMetadata)) + }, +}) + +func GetTransactionMetadata(transactionHash ternary.Trinary, computeIfAbsent ...func(ternary.Trinary) *TransactionMetadata) (result *TransactionMetadata, err errors.IdentifiableError) { + if transactionMetadata := metadataCache.ComputeIfAbsent(transactionHash, func() interface{} { + if result, err = getTransactionMetadataFromDatabase(transactionHash); err == nil && result == nil && len(computeIfAbsent) >= 1 { + result = computeIfAbsent[0](transactionHash) } - return nil - }).(*TransactionMetadata) + return result + }); transactionMetadata != nil && transactionMetadata.(*TransactionMetadata) != nil { + result = transactionMetadata.(*TransactionMetadata) + } return } @@ -77,6 +96,6 @@ func ContainsTransactionMetadata(transactionHash ternary.Trinary) (result bool, // endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////// const ( - TRANSACTION_CACHE_SIZE = 1000 - METADATA_CACHE_SIZE = 1000 + TRANSACTION_CACHE_SIZE = 10000 + METADATA_CACHE_SIZE = 10000 ) diff --git a/plugins/tangle/approvers.go b/plugins/tangle/approvers.go index b7480dce..cdf3c7b4 100644 --- a/plugins/tangle/approvers.go +++ b/plugins/tangle/approvers.go @@ -23,12 +23,12 @@ func StoreApprovers(approvers *Approvers) { } func GetApprovers(transactionHash ternary.Trinary, computeIfAbsent ...func(ternary.Trinary) *Approvers) (result *Approvers, err errors.IdentifiableError) { - if approvers := approversCache.ComputeIfAbsent(transactionHash, func() (result interface{}) { - if result, err = getApproversFromDatabase(transactionHash); err == nil && (result == nil || result.(*Approvers) == nil) && len(computeIfAbsent) >= 1 { + if approvers := approversCache.ComputeIfAbsent(transactionHash, func() interface{} { + if result, err = getApproversFromDatabase(transactionHash); err == nil && result == nil && len(computeIfAbsent) >= 1 { result = computeIfAbsent[0](transactionHash) } - return + return result }); approvers != nil && approvers.(*Approvers) != nil { result = approvers.(*Approvers) } @@ -103,7 +103,7 @@ func (approvers *Approvers) Marshal() (result []byte) { copy(result[MARSHALLED_APPROVERS_HASH_START:MARSHALLED_APPROVERS_HASH_END], approvers.hash.CastToBytes()) i := 0 - for hash, _ := range approvers.hashes { + for hash := range approvers.hashes { var HASH_START = MARSHALLED_APPROVERS_HASHES_START + i*(MARSHALLED_APPROVERS_HASH_SIZE) var HASH_END = HASH_START * MARSHALLED_APPROVERS_HASH_SIZE @@ -181,7 +181,7 @@ func (approvers *Approvers) getHashes() (result []ternary.Trinary) { result = make([]ternary.Trinary, len(approvers.hashes)) counter := 0 - for hash, _ := range approvers.hashes { + for hash := range approvers.hashes { result[counter] = hash counter++ @@ -201,9 +201,7 @@ func (approvers *Approvers) Store(approverHash ternary.Trinary) { func getApproversFromDatabase(transactionHash ternary.Trinary) (result *Approvers, err errors.IdentifiableError) { approversData, dbErr := approversDatabase.Get(transactionHash.CastToBytes()) if dbErr != nil { - if dbErr == badger.ErrKeyNotFound { - err = nil - } else { + if dbErr != badger.ErrKeyNotFound { err = ErrDatabaseError.Derive(err, "failed to retrieve transaction") } diff --git a/plugins/tangle/approvers_test.go b/plugins/tangle/approvers_test.go index b7e4b684..d5f7cfcc 100644 --- a/plugins/tangle/approvers_test.go +++ b/plugins/tangle/approvers_test.go @@ -5,6 +5,7 @@ import ( "testing" "github.com/iotaledger/goshimmer/packages/events" + "github.com/iotaledger/goshimmer/packages/model/value_transaction" "github.com/iotaledger/goshimmer/packages/ternary" "github.com/iotaledger/goshimmer/plugins/gossip" ) @@ -15,27 +16,27 @@ func TestSolidifier(t *testing.T) { configureSolidifier(nil) // create transactions and chain them together - transaction1 := NewTransaction(nil) + transaction1 := value_transaction.New() transaction1.SetNonce(ternary.Trinary("99999999999999999999999999A")) - transaction2 := NewTransaction(nil) + transaction2 := value_transaction.New() transaction2.SetBranchTransactionHash(transaction1.GetHash()) - transaction3 := NewTransaction(nil) + transaction3 := value_transaction.New() transaction3.SetBranchTransactionHash(transaction2.GetHash()) - transaction4 := NewTransaction(nil) + transaction4 := value_transaction.New() transaction4.SetBranchTransactionHash(transaction3.GetHash()) // setup event handlers var wg sync.WaitGroup - Events.TransactionSolid.Attach(events.NewClosure(func(transaction *Transaction) { + Events.TransactionSolid.Attach(events.NewClosure(func(transaction *value_transaction.ValueTransaction) { wg.Done() })) // issue transactions wg.Add(4) - gossip.Events.ReceiveTransaction.Trigger(transaction1.Flush()) - gossip.Events.ReceiveTransaction.Trigger(transaction2.Flush()) - gossip.Events.ReceiveTransaction.Trigger(transaction3.Flush()) - gossip.Events.ReceiveTransaction.Trigger(transaction4.Flush()) + gossip.Events.ReceiveTransaction.Trigger(transaction1.MetaTransaction) + gossip.Events.ReceiveTransaction.Trigger(transaction2.MetaTransaction) + gossip.Events.ReceiveTransaction.Trigger(transaction3.MetaTransaction) + gossip.Events.ReceiveTransaction.Trigger(transaction4.MetaTransaction) // wait until all are solid wg.Wait() diff --git a/plugins/tangle/database.go b/plugins/tangle/database.go index fbfdc548..511d52d2 100644 --- a/plugins/tangle/database.go +++ b/plugins/tangle/database.go @@ -1,14 +1,12 @@ package tangle import ( - "fmt" - "github.com/dgraph-io/badger" "github.com/iotaledger/goshimmer/packages/database" "github.com/iotaledger/goshimmer/packages/errors" + "github.com/iotaledger/goshimmer/packages/model/value_transaction" "github.com/iotaledger/goshimmer/packages/node" "github.com/iotaledger/goshimmer/packages/ternary" - "github.com/iotaledger/goshimmer/packages/transaction" ) // region plugin module setup ////////////////////////////////////////////////////////////////////////////////////////// @@ -37,7 +35,19 @@ func configureDatabase(plugin *node.Plugin) { // region internal utility functions /////////////////////////////////////////////////////////////////////////////////// -func getTransactionFromDatabase(transactionHash ternary.Trinary) (*Transaction, errors.IdentifiableError) { +func storeTransactionInDatabase(transaction *value_transaction.ValueTransaction) errors.IdentifiableError { + if transaction.GetModified() { + if err := transactionDatabase.Set(transaction.GetHash().CastToBytes(), transaction.MetaTransaction.GetBytes()); err != nil { + return ErrDatabaseError.Derive(err, "failed to store transaction") + } + + transaction.SetModified(false) + } + + return nil +} + +func getTransactionFromDatabase(transactionHash ternary.Trinary) (*value_transaction.ValueTransaction, errors.IdentifiableError) { txData, err := transactionDatabase.Get(transactionHash.CastToBytes()) if err != nil { if err == badger.ErrKeyNotFound { @@ -47,9 +57,7 @@ func getTransactionFromDatabase(transactionHash ternary.Trinary) (*Transaction, } } - return &Transaction{ - rawTransaction: transaction.FromBytes(txData), - }, nil + return value_transaction.FromBytes(txData), nil } func databaseContainsTransaction(transactionHash ternary.Trinary) (bool, errors.IdentifiableError) { @@ -60,6 +68,23 @@ func databaseContainsTransaction(transactionHash ternary.Trinary) (bool, errors. } } +func storeTransactionMetadataInDatabase(metadata *TransactionMetadata) errors.IdentifiableError { + if metadata.GetModified() { + marshalledMetadata, err := metadata.Marshal() + if err != nil { + return err + } + + if err := transactionMetadataDatabase.Set(metadata.GetHash().CastToBytes(), marshalledMetadata); err != nil { + return ErrDatabaseError.Derive(err, "failed to store transaction metadata") + } + + metadata.SetModified(false) + } + + return nil +} + func getTransactionMetadataFromDatabase(transactionHash ternary.Trinary) (*TransactionMetadata, errors.IdentifiableError) { txMetadata, err := transactionMetadataDatabase.Get(transactionHash.CastToBytes()) if err != nil { @@ -70,11 +95,12 @@ func getTransactionMetadataFromDatabase(transactionHash ternary.Trinary) (*Trans } } - if false { - fmt.Println(txMetadata) + var result TransactionMetadata + if err := result.Unmarshal(txMetadata); err != nil { + panic(err) } - return &TransactionMetadata{}, nil + return &result, nil } func databaseContainsTransactionMetadata(transactionHash ternary.Trinary) (bool, errors.IdentifiableError) { diff --git a/plugins/tangle/events.go b/plugins/tangle/events.go index bb8883a5..e29ecd20 100644 --- a/plugins/tangle/events.go +++ b/plugins/tangle/events.go @@ -2,6 +2,7 @@ package tangle import ( "github.com/iotaledger/goshimmer/packages/events" + "github.com/iotaledger/goshimmer/packages/model/value_transaction" ) var Events = pluginEvents{ @@ -15,5 +16,5 @@ type pluginEvents struct { } func transactionCaller(handler interface{}, params ...interface{}) { - handler.(func(*Transaction))(params[0].(*Transaction)) + handler.(func(*value_transaction.ValueTransaction))(params[0].(*value_transaction.ValueTransaction)) } diff --git a/plugins/tangle/solidifier.go b/plugins/tangle/solidifier.go index 8f7033cb..c5b0d38d 100644 --- a/plugins/tangle/solidifier.go +++ b/plugins/tangle/solidifier.go @@ -3,27 +3,65 @@ package tangle import ( "github.com/iotaledger/goshimmer/packages/errors" "github.com/iotaledger/goshimmer/packages/events" + "github.com/iotaledger/goshimmer/packages/model/meta_transaction" + "github.com/iotaledger/goshimmer/packages/model/value_transaction" "github.com/iotaledger/goshimmer/packages/node" "github.com/iotaledger/goshimmer/packages/ternary" - "github.com/iotaledger/goshimmer/packages/transaction" "github.com/iotaledger/goshimmer/plugins/gossip" ) // region plugin module setup ////////////////////////////////////////////////////////////////////////////////////////// +//var solidifierChan = make(chan *value_transaction.ValueTransaction, 1000) + +const NUMBER_OF_WORKERS = 300 + +var tasksChan = make(chan *meta_transaction.MetaTransaction, NUMBER_OF_WORKERS) + func configureSolidifier(plugin *node.Plugin) { - gossip.Events.ReceiveTransaction.Attach(events.NewClosure(func(rawTransaction *transaction.Transaction) { - go processRawTransaction(plugin, rawTransaction) + for i := 0; i < NUMBER_OF_WORKERS; i++ { + go func() { + for { + rawTransaction := <-tasksChan + + processMetaTransaction(plugin, rawTransaction) + } + }() + } + + gossip.Events.ReceiveTransaction.Attach(events.NewClosure(func(rawTransaction *meta_transaction.MetaTransaction) { + tasksChan <- rawTransaction + + //go processMetaTransaction(plugin, rawTransaction) })) + /* + for i := 0; i < NUMBER_OF_WORKERS; i++ { + go func() { + for transaction := range solidifierChan { + select { + case <-daemon.ShutdownSignal: + return + + default: + // update the solidity flags of this transaction and its approvers + if _, err := IsSolid(transaction); err != nil { + plugin.LogFailure(err.Error()) + + return + } + } + } + }() + }*/ } // endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////// // Checks and updates the solid flag of a single transaction. -func checkSolidity(transaction *Transaction) (result bool, err errors.IdentifiableError) { +func checkSolidity(transaction *value_transaction.ValueTransaction) (result bool, err errors.IdentifiableError) { // abort if transaction is solid already - txMetadata, metaDataErr := transaction.GetMetaData() - if err != nil { + txMetadata, metaDataErr := GetTransactionMetadata(transaction.GetHash(), NewTransactionMetadata) + if metaDataErr != nil { err = metaDataErr return @@ -34,15 +72,15 @@ func checkSolidity(transaction *Transaction) (result bool, err errors.Identifiab } // check solidity of branch transaction if it is not genesis - if branchTransactionHash := transaction.GetBranchTransactionHash(); branchTransactionHash != TRANSACTION_NULL_HASH { + if branchTransactionHash := transaction.GetBranchTransactionHash(); branchTransactionHash != meta_transaction.BRANCH_NULL_HASH { // abort if branch transaction is missing - if branchTransaction, branchErr := GetTransaction(branchTransactionHash); err != nil { + if branchTransaction, branchErr := GetTransaction(branchTransactionHash); branchErr != nil { err = branchErr return } else if branchTransaction == nil { return - } else if branchTransactionMetadata, branchErr := branchTransaction.GetMetaData(); branchErr != nil { + } else if branchTransactionMetadata, branchErr := GetTransactionMetadata(branchTransaction.GetHash(), NewTransactionMetadata); branchErr != nil { err = branchErr return @@ -52,14 +90,14 @@ func checkSolidity(transaction *Transaction) (result bool, err errors.Identifiab } // check solidity of branch transaction if it is not genesis - if trunkTransactionHash := transaction.GetBranchTransactionHash(); trunkTransactionHash != TRANSACTION_NULL_HASH { + if trunkTransactionHash := transaction.GetBranchTransactionHash(); trunkTransactionHash != meta_transaction.BRANCH_NULL_HASH { if trunkTransaction, trunkErr := GetTransaction(trunkTransactionHash); trunkErr != nil { err = trunkErr return } else if trunkTransaction == nil { return - } else if trunkTransactionMetadata, trunkErr := trunkTransaction.GetMetaData(); trunkErr != nil { + } else if trunkTransactionMetadata, trunkErr := GetTransactionMetadata(trunkTransaction.GetHash(), NewTransactionMetadata); trunkErr != nil { err = trunkErr return @@ -79,7 +117,7 @@ func checkSolidity(transaction *Transaction) (result bool, err errors.Identifiab } // Checks and updates the solid flag of a transaction and its approvers (future cone). -func IsSolid(transaction *Transaction) (bool, errors.IdentifiableError) { +func IsSolid(transaction *value_transaction.ValueTransaction) (bool, errors.IdentifiableError) { if isSolid, err := checkSolidity(transaction); err != nil { return false, err } else if isSolid { @@ -113,20 +151,20 @@ func propagateSolidity(transactionHash ternary.Trinary) errors.IdentifiableError return nil } -func processRawTransaction(plugin *node.Plugin, rawTransaction *transaction.Transaction) { +func processMetaTransaction(plugin *node.Plugin, metaTransaction *meta_transaction.MetaTransaction) { var newTransaction bool - if tx, err := GetTransaction(rawTransaction.Hash.ToTrinary(), func(transactionHash ternary.Trinary) *Transaction { + if tx, err := GetTransaction(metaTransaction.GetHash(), func(transactionHash ternary.Trinary) *value_transaction.ValueTransaction { newTransaction = true - return &Transaction{rawTransaction: rawTransaction} + return value_transaction.FromMetaTransaction(metaTransaction) }); err != nil { plugin.LogFailure(err.Error()) } else if newTransaction { - go processTransaction(plugin, tx) + processTransaction(plugin, tx) } } -func processTransaction(plugin *node.Plugin, transaction *Transaction) { +func processTransaction(plugin *node.Plugin, transaction *value_transaction.ValueTransaction) { transactionHash := transaction.GetHash() // register tx as approver for trunk @@ -153,4 +191,5 @@ func processTransaction(plugin *node.Plugin, transaction *Transaction) { return } + //solidifierChan <- transaction } diff --git a/plugins/tangle/transaction.go b/plugins/tangle/transaction.go deleted file mode 100644 index 6ee98b52..00000000 --- a/plugins/tangle/transaction.go +++ /dev/null @@ -1,685 +0,0 @@ -package tangle - -import ( - "math" - "sync" - - "github.com/iotaledger/goshimmer/packages/curl" - "github.com/iotaledger/goshimmer/packages/errors" - "github.com/iotaledger/goshimmer/packages/ternary" - "github.com/iotaledger/goshimmer/packages/transaction" -) - -// region type definition and constructor ////////////////////////////////////////////////////////////////////////////// - -type Transaction struct { - // wrapped objects - rawTransaction *transaction.Transaction - rawMetaData *TransactionMetadata - rawMetaDataMutex sync.RWMutex - - // mapped raw transaction properties - hash *ternary.Trinary - hashMutex sync.RWMutex - signatureMessageFragment *ternary.Trinary - signatureMessageFragmentMutex sync.RWMutex - address *ternary.Trinary - addressMutex sync.RWMutex - value *int64 - valueMutex sync.RWMutex - timestamp *uint64 - timestampMutex sync.RWMutex - currentIndex *uint64 - currentIndexMutex sync.RWMutex - latestIndex *uint64 - latestIndexMutex sync.RWMutex - bundleHash *ternary.Trinary - bundleHashMutex sync.RWMutex - trunkTransactionHash *ternary.Trinary - trunkTransactionHashMutex sync.RWMutex - branchTransactionHash *ternary.Trinary - branchTransactionHashMutex sync.RWMutex - tag *ternary.Trinary - tagMutex sync.RWMutex - nonce *ternary.Trinary - nonceMutex sync.RWMutex - - // additional runtime specific metadata - modified bool - modifiedMutex sync.RWMutex -} - -func NewTransaction(rawTransaction *transaction.Transaction) *Transaction { - if rawTransaction == nil { - rawTransaction = transaction.FromBytes(make([]byte, int(math.Ceil(float64(transaction.MARSHALLED_TOTAL_SIZE)/ternary.NUMBER_OF_TRITS_IN_A_BYTE)))) - } - - return &Transaction{rawTransaction: rawTransaction} -} - -// endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////// - -// region getters and setters ////////////////////////////////////////////////////////////////////////////////////////// - -func (transaction *Transaction) GetMetaData() (*TransactionMetadata, errors.IdentifiableError) { - transaction.rawMetaDataMutex.RLock() - if transaction.rawMetaData == nil { - transaction.rawMetaDataMutex.RUnlock() - transaction.rawMetaDataMutex.Lock() - defer transaction.rawMetaDataMutex.Unlock() - if transaction.rawMetaData == nil { - if metaData, err := getTransactionMetadataFromDatabase(transaction.GetHash()); err != nil { - return nil, err - } else if metaData == nil { - transaction.rawMetaData = NewTransactionMetadata(transaction.GetHash()) - } else { - transaction.rawMetaData = metaData - } - - return transaction.rawMetaData, nil - } - } - - defer transaction.rawMetaDataMutex.RUnlock() - - return transaction.rawMetaData, nil -} - -// getter for the hash (supports concurrency) -func (transaction *Transaction) GetHash() ternary.Trinary { - transaction.hashMutex.RLock() - if transaction.hash == nil { - transaction.hashMutex.RUnlock() - transaction.hashMutex.Lock() - defer transaction.hashMutex.Unlock() - if transaction.hash == nil { - return transaction.parseHash() - } - } - - defer transaction.hashMutex.RUnlock() - - return *transaction.hash -} - -// setter for the hash (supports concurrency) -func (transaction *Transaction) SetHash(hash ternary.Trinary) { - transaction.hashMutex.RLock() - if transaction.hash == nil || *transaction.hash != hash { - transaction.hashMutex.RUnlock() - transaction.hashMutex.Lock() - defer transaction.hashMutex.Unlock() - if transaction.hash == nil || *transaction.hash != hash { - *transaction.hash = hash - - transaction.SetModified(true) - } - } else { - transaction.hashMutex.RUnlock() - } -} - -// restores the hash from the underlying raw transaction (supports concurrency) -func (transaction *Transaction) ParseHash() ternary.Trinary { - transaction.hashMutex.Lock() - defer transaction.hashMutex.Unlock() - - return transaction.parseHash() -} - -// parses the hash from the underlying raw transaction (without locking - internal usage) -func (transaction *Transaction) parseHash() (result ternary.Trinary) { - if transaction.modified { - transaction.Flush() - } - - result = transaction.rawTransaction.Hash.ToTrinary() - - transaction.hash = &result - - return -} - -// getter for the address (supports concurrency) -func (transaction *Transaction) GetAddress() ternary.Trinary { - transaction.addressMutex.RLock() - if transaction.address == nil { - transaction.addressMutex.RUnlock() - transaction.addressMutex.Lock() - defer transaction.addressMutex.Unlock() - if transaction.address == nil { - return transaction.parseAddress() - } - } - - defer transaction.addressMutex.RUnlock() - - return *transaction.address -} - -// setter for the address (supports concurrency) -func (transaction *Transaction) SetAddress(address ternary.Trinary) { - transaction.addressMutex.RLock() - if transaction.address == nil || *transaction.address != address { - transaction.addressMutex.RUnlock() - transaction.addressMutex.Lock() - defer transaction.addressMutex.Unlock() - if transaction.address == nil || *transaction.address != address { - *transaction.address = address - - transaction.SetModified(true) - } - } else { - transaction.addressMutex.RUnlock() - } -} - -// restores the address from the underlying raw transaction (supports concurrency) -func (transaction *Transaction) ParseAddress() ternary.Trinary { - transaction.addressMutex.Lock() - defer transaction.addressMutex.Unlock() - - return transaction.parseAddress() -} - -// parses the address from the underlying raw transaction (without locking - internal usage) -func (transaction *Transaction) parseAddress() ternary.Trinary { - *transaction.address = transaction.rawTransaction.Hash.ToTrinary() - - return *transaction.address -} - -// getter for the value (supports concurrency) -func (transaction *Transaction) GetValue() int64 { - transaction.valueMutex.RLock() - if transaction.value == nil { - transaction.valueMutex.RUnlock() - transaction.valueMutex.Lock() - defer transaction.valueMutex.Unlock() - if transaction.value == nil { - return transaction.parseValue() - } - } - - defer transaction.valueMutex.RUnlock() - - return *transaction.value -} - -// setter for the value (supports concurrency) -func (transaction *Transaction) SetValue(value int64) { - transaction.valueMutex.RLock() - if transaction.value == nil || *transaction.value != value { - transaction.valueMutex.RUnlock() - transaction.valueMutex.Lock() - defer transaction.valueMutex.Unlock() - if transaction.value == nil || *transaction.value != value { - *transaction.value = value - - transaction.SetModified(true) - } - } else { - transaction.valueMutex.RUnlock() - } -} - -// restores the value from the underlying raw transaction (supports concurrency) -func (transaction *Transaction) ParseValue() int64 { - transaction.valueMutex.Lock() - defer transaction.valueMutex.Unlock() - - return transaction.parseValue() -} - -// parses the value from the underlying raw transaction (without locking - internal usage) -func (transaction *Transaction) parseValue() int64 { - *transaction.value = transaction.rawTransaction.Value.ToInt64() - - return *transaction.value -} - -// getter for the timestamp (supports concurrency) -func (transaction *Transaction) GetTimestamp() uint64 { - transaction.timestampMutex.RLock() - if transaction.timestamp == nil { - transaction.timestampMutex.RUnlock() - transaction.timestampMutex.Lock() - defer transaction.timestampMutex.Unlock() - if transaction.timestamp == nil { - return transaction.parseTimestamp() - } - } - - defer transaction.timestampMutex.RUnlock() - - return *transaction.timestamp -} - -// setter for the timestamp (supports concurrency) -func (transaction *Transaction) SetTimestamp(timestamp uint64) { - transaction.timestampMutex.RLock() - if transaction.timestamp == nil || *transaction.timestamp != timestamp { - transaction.timestampMutex.RUnlock() - transaction.timestampMutex.Lock() - defer transaction.timestampMutex.Unlock() - if transaction.timestamp == nil || *transaction.timestamp != timestamp { - *transaction.timestamp = timestamp - - transaction.SetModified(true) - } - } else { - transaction.timestampMutex.RUnlock() - } -} - -// restores the timestamp from the underlying raw transaction (supports concurrency) -func (transaction *Transaction) ParseTimestamp() uint64 { - transaction.timestampMutex.Lock() - defer transaction.timestampMutex.Unlock() - - return transaction.parseTimestamp() -} - -// parses the timestamp from the underlying raw transaction (without locking - internal usage) -func (transaction *Transaction) parseTimestamp() uint64 { - *transaction.timestamp = transaction.rawTransaction.Timestamp.ToUint64() - - return *transaction.timestamp -} - -// getter for the currentIndex (supports concurrency) -func (transaction *Transaction) GetCurrentIndex() uint64 { - transaction.currentIndexMutex.RLock() - if transaction.currentIndex == nil { - transaction.currentIndexMutex.RUnlock() - transaction.currentIndexMutex.Lock() - defer transaction.currentIndexMutex.Unlock() - if transaction.currentIndex == nil { - return transaction.parseCurrentIndex() - } - } - - defer transaction.currentIndexMutex.RUnlock() - - return *transaction.currentIndex -} - -// setter for the currentIndex (supports concurrency) -func (transaction *Transaction) SetCurrentIndex(currentIndex uint64) { - transaction.currentIndexMutex.RLock() - if transaction.currentIndex == nil || *transaction.currentIndex != currentIndex { - transaction.currentIndexMutex.RUnlock() - transaction.currentIndexMutex.Lock() - defer transaction.currentIndexMutex.Unlock() - if transaction.currentIndex == nil || *transaction.currentIndex != currentIndex { - *transaction.currentIndex = currentIndex - - transaction.SetModified(true) - } - } else { - transaction.currentIndexMutex.RUnlock() - } -} - -// restores the currentIndex from the underlying raw transaction (supports concurrency) -func (transaction *Transaction) ParseCurrentIndex() uint64 { - transaction.currentIndexMutex.Lock() - defer transaction.currentIndexMutex.Unlock() - - return transaction.parseCurrentIndex() -} - -// parses the currentIndex from the underlying raw transaction (without locking - internal usage) -func (transaction *Transaction) parseCurrentIndex() uint64 { - *transaction.currentIndex = transaction.rawTransaction.CurrentIndex.ToUint64() - - return *transaction.currentIndex -} - -// getter for the latestIndex (supports concurrency) -func (transaction *Transaction) GetLatestIndex() uint64 { - transaction.latestIndexMutex.RLock() - if transaction.latestIndex == nil { - transaction.latestIndexMutex.RUnlock() - transaction.latestIndexMutex.Lock() - defer transaction.latestIndexMutex.Unlock() - if transaction.latestIndex == nil { - return transaction.parseLatestIndex() - } - } - - defer transaction.latestIndexMutex.RUnlock() - - return *transaction.latestIndex -} - -// setter for the latestIndex (supports concurrency) -func (transaction *Transaction) SetLatestIndex(latestIndex uint64) { - transaction.latestIndexMutex.RLock() - if transaction.latestIndex == nil || *transaction.latestIndex != latestIndex { - transaction.latestIndexMutex.RUnlock() - transaction.latestIndexMutex.Lock() - defer transaction.latestIndexMutex.Unlock() - if transaction.latestIndex == nil || *transaction.latestIndex != latestIndex { - *transaction.latestIndex = latestIndex - - transaction.SetModified(true) - } - } else { - transaction.latestIndexMutex.RUnlock() - } -} - -// restores the latestIndex from the underlying raw transaction (supports concurrency) -func (transaction *Transaction) ParseLatestIndex() uint64 { - transaction.latestIndexMutex.Lock() - defer transaction.latestIndexMutex.Unlock() - - return transaction.parseLatestIndex() -} - -// parses the latestIndex from the underlying raw transaction (without locking - internal usage) -func (transaction *Transaction) parseLatestIndex() uint64 { - *transaction.latestIndex = transaction.rawTransaction.LatestIndex.ToUint64() - - return *transaction.latestIndex -} - -// getter for the bundleHash (supports concurrency) -func (transaction *Transaction) GetBundleHash() ternary.Trinary { - transaction.bundleHashMutex.RLock() - if transaction.bundleHash == nil { - transaction.bundleHashMutex.RUnlock() - transaction.bundleHashMutex.Lock() - defer transaction.bundleHashMutex.Unlock() - if transaction.bundleHash == nil { - return transaction.parseBundleHash() - } - } - - defer transaction.bundleHashMutex.RUnlock() - - return *transaction.bundleHash -} - -// setter for the bundleHash (supports concurrency) -func (transaction *Transaction) SetBundleHash(bundleHash ternary.Trinary) { - transaction.bundleHashMutex.RLock() - if transaction.bundleHash == nil || *transaction.bundleHash != bundleHash { - transaction.bundleHashMutex.RUnlock() - transaction.bundleHashMutex.Lock() - defer transaction.bundleHashMutex.Unlock() - if transaction.bundleHash == nil || *transaction.bundleHash != bundleHash { - *transaction.bundleHash = bundleHash - - transaction.SetModified(true) - } - } else { - transaction.bundleHashMutex.RUnlock() - } -} - -// restores the bundleHash from the underlying raw transaction (supports concurrency) -func (transaction *Transaction) ParseBundleHash() ternary.Trinary { - transaction.bundleHashMutex.Lock() - defer transaction.bundleHashMutex.Unlock() - - return transaction.parseBundleHash() -} - -// parses the bundleHash from the underlying raw transaction (without locking - internal usage) -func (transaction *Transaction) parseBundleHash() ternary.Trinary { - *transaction.bundleHash = transaction.rawTransaction.BundleHash.ToTrinary() - - return *transaction.bundleHash -} - -// getter for the trunkTransactionHash (supports concurrency) -func (transaction *Transaction) GetTrunkTransactionHash() ternary.Trinary { - transaction.trunkTransactionHashMutex.RLock() - if transaction.trunkTransactionHash == nil { - transaction.trunkTransactionHashMutex.RUnlock() - transaction.trunkTransactionHashMutex.Lock() - defer transaction.trunkTransactionHashMutex.Unlock() - if transaction.trunkTransactionHash == nil { - return transaction.parseTrunkTransactionHash() - } - } - - defer transaction.trunkTransactionHashMutex.RUnlock() - - return *transaction.trunkTransactionHash -} - -// setter for the trunkTransactionHash (supports concurrency) -func (transaction *Transaction) SetTrunkTransactionHash(trunkTransactionHash ternary.Trinary) { - transaction.trunkTransactionHashMutex.RLock() - if transaction.trunkTransactionHash == nil || *transaction.trunkTransactionHash != trunkTransactionHash { - transaction.trunkTransactionHashMutex.RUnlock() - transaction.trunkTransactionHashMutex.Lock() - defer transaction.trunkTransactionHashMutex.Unlock() - if transaction.trunkTransactionHash == nil || *transaction.trunkTransactionHash != trunkTransactionHash { - *transaction.trunkTransactionHash = trunkTransactionHash - - transaction.SetModified(true) - } - } else { - transaction.trunkTransactionHashMutex.RUnlock() - } -} - -// restores the trunkTransactionHash from the underlying raw transaction (supports concurrency) -func (transaction *Transaction) ParseTrunkTransactionHash() ternary.Trinary { - transaction.trunkTransactionHashMutex.Lock() - defer transaction.trunkTransactionHashMutex.Unlock() - - return transaction.parseTrunkTransactionHash() -} - -// parses the trunkTransactionHash from the underlying raw transaction (without locking - internal usage) -func (transaction *Transaction) parseTrunkTransactionHash() (result ternary.Trinary) { - result = transaction.rawTransaction.TrunkTransactionHash.ToTrinary() - - transaction.trunkTransactionHash = &result - - return -} - -// getter for the branchTransactionHash (supports concurrency) -func (transaction *Transaction) GetBranchTransactionHash() ternary.Trinary { - transaction.branchTransactionHashMutex.RLock() - if transaction.branchTransactionHash == nil { - transaction.branchTransactionHashMutex.RUnlock() - transaction.branchTransactionHashMutex.Lock() - defer transaction.branchTransactionHashMutex.Unlock() - if transaction.branchTransactionHash == nil { - return transaction.parseBranchTransactionHash() - } - } - - defer transaction.branchTransactionHashMutex.RUnlock() - - return *transaction.branchTransactionHash -} - -// setter for the branchTransactionHash (supports concurrency) -func (transaction *Transaction) SetBranchTransactionHash(branchTransactionHash ternary.Trinary) { - transaction.branchTransactionHashMutex.RLock() - if transaction.branchTransactionHash == nil || *transaction.branchTransactionHash != branchTransactionHash { - transaction.branchTransactionHashMutex.RUnlock() - transaction.branchTransactionHashMutex.Lock() - defer transaction.branchTransactionHashMutex.Unlock() - if transaction.branchTransactionHash == nil || *transaction.branchTransactionHash != branchTransactionHash { - transaction.branchTransactionHash = &branchTransactionHash - - transaction.SetModified(true) - } - } else { - transaction.branchTransactionHashMutex.RUnlock() - } -} - -// restores the branchTransactionHash from the underlying raw transaction (supports concurrency) -func (transaction *Transaction) ParseBranchTransactionHash() ternary.Trinary { - transaction.branchTransactionHashMutex.Lock() - defer transaction.branchTransactionHashMutex.Unlock() - - return transaction.parseBranchTransactionHash() -} - -// parses the branchTransactionHash from the underlying raw transaction (without locking - internal usage) -func (transaction *Transaction) parseBranchTransactionHash() (result ternary.Trinary) { - result = transaction.rawTransaction.BranchTransactionHash.ToTrinary() - - transaction.branchTransactionHash = &result - - return -} - -// getter for the tag (supports concurrency) -func (transaction *Transaction) GetTag() ternary.Trinary { - transaction.tagMutex.RLock() - if transaction.tag == nil { - transaction.tagMutex.RUnlock() - transaction.tagMutex.Lock() - defer transaction.tagMutex.Unlock() - if transaction.tag == nil { - return transaction.parseTag() - } - } - - defer transaction.tagMutex.RUnlock() - - return *transaction.tag -} - -// setter for the tag (supports concurrency) -func (transaction *Transaction) SetTag(tag ternary.Trinary) { - transaction.tagMutex.RLock() - if transaction.tag == nil || *transaction.tag != tag { - transaction.tagMutex.RUnlock() - transaction.tagMutex.Lock() - defer transaction.tagMutex.Unlock() - if transaction.tag == nil || *transaction.tag != tag { - *transaction.tag = tag - - transaction.SetModified(true) - } - } else { - transaction.tagMutex.RUnlock() - } -} - -// restores the tag from the underlying raw transaction (supports concurrency) -func (transaction *Transaction) ParseTag() ternary.Trinary { - transaction.tagMutex.Lock() - defer transaction.tagMutex.Unlock() - - return transaction.parseTag() -} - -// parses the tag from the underlying raw transaction (without locking - internal usage) -func (transaction *Transaction) parseTag() ternary.Trinary { - *transaction.tag = transaction.rawTransaction.Tag.ToTrinary() - - return *transaction.tag -} - -// getter for the nonce (supports concurrency) -func (transaction *Transaction) GetNonce() ternary.Trinary { - transaction.nonceMutex.RLock() - if transaction.nonce == nil { - transaction.nonceMutex.RUnlock() - transaction.nonceMutex.Lock() - defer transaction.nonceMutex.Unlock() - if transaction.nonce == nil { - return transaction.parseNonce() - } - } - - defer transaction.nonceMutex.RUnlock() - - return *transaction.nonce -} - -// setter for the nonce (supports concurrency) -func (transaction *Transaction) SetNonce(nonce ternary.Trinary) { - transaction.nonceMutex.RLock() - if transaction.nonce == nil || *transaction.nonce != nonce { - transaction.nonceMutex.RUnlock() - transaction.nonceMutex.Lock() - defer transaction.nonceMutex.Unlock() - if transaction.nonce == nil || *transaction.nonce != nonce { - transaction.nonce = &nonce - - transaction.SetModified(true) - } - } else { - transaction.nonceMutex.RUnlock() - } -} - -// restores the nonce from the underlying raw transaction (supports concurrency) -func (transaction *Transaction) ParseNonce() ternary.Trinary { - transaction.nonceMutex.Lock() - defer transaction.nonceMutex.Unlock() - - return transaction.parseNonce() -} - -// parses the nonce from the underlying raw transaction (without locking - internal usage) -func (transaction *Transaction) parseNonce() (result ternary.Trinary) { - result = transaction.rawTransaction.Nonce.ToTrinary() - - transaction.nonce = &result - - return *transaction.nonce -} - -// returns true if the transaction contains unsaved changes (supports concurrency) -func (transaction *Transaction) GetModified() bool { - transaction.modifiedMutex.RLock() - defer transaction.modifiedMutex.RUnlock() - - return transaction.modified -} - -// sets the modified flag which controls if a transaction is going to be saved (supports concurrency) -func (transaction *Transaction) SetModified(modified bool) { - transaction.modifiedMutex.Lock() - defer transaction.modifiedMutex.Unlock() - - transaction.modified = modified -} - -// endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////// - -// region database functions /////////////////////////////////////////////////////////////////////////////////////////// - -// Applies the current values to the -func (this *Transaction) Flush() *transaction.Transaction { - if this.branchTransactionHash != nil { - copy(this.rawTransaction.Trits[transaction.BRANCH_TRANSACTION_HASH_OFFSET:transaction.BRANCH_TRANSACTION_HASH_END], this.branchTransactionHash.ToTrits()[:transaction.BRANCH_TRANSACTION_HASH_SIZE]) - } - if this.nonce != nil { - copy(this.rawTransaction.Trits[transaction.NONCE_OFFSET:transaction.NONCE_END], this.nonce.ToTrits()[:transaction.NONCE_SIZE]) - } - - this.rawTransaction.Hash = <-curl.CURLP81.Hash(this.rawTransaction.Trits) - - return this.rawTransaction -} - -func (transaction *Transaction) Store() errors.IdentifiableError { - if err := transactionDatabase.Set(transaction.rawTransaction.Hash.ToBytes(), transaction.rawTransaction.Bytes); err != nil { - return ErrDatabaseError.Derive(err, "failed to store the transaction") - } - - return nil -} - -// endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////// - -const ( - TRANSACTION_NULL_HASH = ternary.Trinary("999999999999999999999999999999999999999999999999999999999999999999999999999999999") -) diff --git a/plugins/tangle/transaction_metadata.go b/plugins/tangle/transaction_metadata.go index 97869f88..c617f0c9 100644 --- a/plugins/tangle/transaction_metadata.go +++ b/plugins/tangle/transaction_metadata.go @@ -252,23 +252,6 @@ func (metadata *TransactionMetadata) Unmarshal(data []byte) errors.IdentifiableE // region database functions /////////////////////////////////////////////////////////////////////////////////////////// -func (metadata *TransactionMetadata) Store() errors.IdentifiableError { - if metadata.GetModified() { - marshalledMetadata, err := metadata.Marshal() - if err != nil { - return err - } - - if err := transactionMetadataDatabase.Set(metadata.GetHash().CastToBytes(), marshalledMetadata); err != nil { - return ErrDatabaseError.Derive(err, "failed to store the transaction") - } - - metadata.SetModified(false) - } - - return nil -} - // endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////// // region constants and variables ////////////////////////////////////////////////////////////////////////////////////// diff --git a/plugins/tipselection/plugin.go b/plugins/tipselection/plugin.go new file mode 100644 index 00000000..7980a595 --- /dev/null +++ b/plugins/tipselection/plugin.go @@ -0,0 +1,23 @@ +package tipselection + +import ( + "github.com/iotaledger/goshimmer/packages/events" + "github.com/iotaledger/goshimmer/packages/model/value_transaction" + "github.com/iotaledger/goshimmer/packages/node" + "github.com/iotaledger/goshimmer/plugins/tangle" +) + +var PLUGIN = node.NewPlugin("Tipselection", configure, run) + +func configure(node *node.Plugin) { + tangle.Events.TransactionSolid.Attach(events.NewClosure(func(transaction *value_transaction.ValueTransaction) { + go func() { + tips.Delete(transaction.GetBranchTransactionHash()) + tips.Delete(transaction.GetTrunkTransactionHash()) + tips.Set(transaction.GetHash(), transaction.GetHash()) + }() + })) +} + +func run(run *node.Plugin) { +} diff --git a/plugins/tipselection/tipselection.go b/plugins/tipselection/tipselection.go new file mode 100644 index 00000000..4457ddc0 --- /dev/null +++ b/plugins/tipselection/tipselection.go @@ -0,0 +1,23 @@ +package tipselection + +import ( + "github.com/iotaledger/goshimmer/packages/datastructure" + "github.com/iotaledger/goshimmer/packages/model/meta_transaction" + "github.com/iotaledger/goshimmer/packages/ternary" +) + +var tips = datastructure.NewRandomMap() + +func GetRandomTip() (result ternary.Trinary) { + if randomTipHash := tips.RandomEntry(); randomTipHash != nil { + result = randomTipHash.(ternary.Trinary) + } else { + result = meta_transaction.BRANCH_NULL_HASH + } + + return +} + +func GetTipsCount() int { + return tips.Size() +} diff --git a/plugins/tipselection/tipselection_test.go b/plugins/tipselection/tipselection_test.go new file mode 100644 index 00000000..cb8274bf --- /dev/null +++ b/plugins/tipselection/tipselection_test.go @@ -0,0 +1,24 @@ +package tipselection + +import ( + "fmt" + "testing" + + "github.com/iotaledger/goshimmer/packages/model/value_transaction" + "github.com/iotaledger/goshimmer/plugins/tangle" +) + +func Test(t *testing.T) { + configure(nil) + + for i := 0; i < 1000; i++ { + tx := value_transaction.New() + tx.SetValue(int64(i + 1)) + tx.SetBranchTransactionHash(GetRandomTip()) + tx.SetTrunkTransactionHash(GetRandomTip()) + + tangle.Events.TransactionSolid.Trigger(tx) + + fmt.Println(GetTipsCount()) + } +} diff --git a/plugins/webapi-gtta/plugin.go b/plugins/webapi-gtta/plugin.go new file mode 100644 index 00000000..b174e7ec --- /dev/null +++ b/plugins/webapi-gtta/plugin.go @@ -0,0 +1,35 @@ +package webapi_gtta + +import ( + "net/http" + "time" + + "github.com/iotaledger/goshimmer/packages/node" + "github.com/iotaledger/goshimmer/packages/ternary" + "github.com/iotaledger/goshimmer/plugins/tipselection" + "github.com/iotaledger/goshimmer/plugins/webapi" + "github.com/labstack/echo" +) + +var PLUGIN = node.NewPlugin("WebAPI GTTA Endpoint", func(plugin *node.Plugin) { + webapi.AddEndpoint("getTransactionsToApprove", Handler) +}) + +func Handler(c echo.Context) error { + start := time.Now() + + branchTransactionHash := tipselection.GetRandomTip() + trunkTransactionHash := tipselection.GetRandomTip() + + return c.JSON(http.StatusOK, response{ + Duration: time.Since(start).Nanoseconds() / 1e6, + BranchTransaction: branchTransactionHash, + TrunkTransaction: trunkTransactionHash, + }) +} + +type response struct { + Duration int64 `json:"duration"` + BranchTransaction ternary.Trinary `json:"branchTransaction"` + TrunkTransaction ternary.Trinary `json:"trunkTransaction"` +} diff --git a/plugins/webapi/api.go b/plugins/webapi/api.go new file mode 100644 index 00000000..5c50d71b --- /dev/null +++ b/plugins/webapi/api.go @@ -0,0 +1,9 @@ +package webapi + +import ( + "github.com/labstack/echo" +) + +func AddEndpoint(url string, handler func(c echo.Context) error) { + Server.GET(url, handler) +} diff --git a/plugins/webapi/endpoints.go b/plugins/webapi/endpoints.go new file mode 100644 index 00000000..c01bd84c --- /dev/null +++ b/plugins/webapi/endpoints.go @@ -0,0 +1,11 @@ +package webapi + +import ( + "net/http" + + "github.com/labstack/echo" +) + +func IndexRequest(c echo.Context) error { + return c.String(http.StatusOK, "INDEX") +} diff --git a/plugins/webapi/plugin.go b/plugins/webapi/plugin.go new file mode 100644 index 00000000..ee2010c4 --- /dev/null +++ b/plugins/webapi/plugin.go @@ -0,0 +1,43 @@ +package webapi + +import ( + "context" + "time" + + "github.com/iotaledger/goshimmer/packages/daemon" + "github.com/iotaledger/goshimmer/packages/events" + "github.com/iotaledger/goshimmer/packages/node" + "github.com/labstack/echo" +) + +var PLUGIN = node.NewPlugin("WebAPI", configure, run) + +var Server = echo.New() + +func configure(plugin *node.Plugin) { + Server.HideBanner = true + Server.GET("/", IndexRequest) + + daemon.Events.Shutdown.Attach(events.NewClosure(func() { + plugin.LogInfo("Stopping Web Server ...") + + ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) + defer cancel() + + if err := Server.Shutdown(ctx); err != nil { + plugin.LogFailure(err.Error()) + } + })) +} + +func run(plugin *node.Plugin) { + plugin.LogInfo("Starting Web Server ...") + + daemon.BackgroundWorker(func() { + plugin.LogSuccess("Starting Web Server ... done") + + if err := Server.Start(":8080"); err != nil { + plugin.LogSuccess("Stopping Web Server ... done") + } + }) +} diff --git a/plugins/webapi/routes.go b/plugins/webapi/routes.go new file mode 100644 index 00000000..2f5e3754 --- /dev/null +++ b/plugins/webapi/routes.go @@ -0,0 +1,9 @@ +package webapi + +import ( + "github.com/labstack/echo" +) + +func setupRoutes(e *echo.Echo) { + +} -- GitLab