diff --git a/go.mod b/go.mod new file mode 100644 index 0000000000000000000000000000000000000000..af293b4992dc6b3f39c22d87d49c315d040fcb00 --- /dev/null +++ b/go.mod @@ -0,0 +1,20 @@ +module github.com/iotaledger/goshimmer + +go 1.12 + +require ( + github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9 // indirect + github.com/dgraph-io/badger v2.0.0-rc.2.0.20190610165348-a804ffa35fb9+incompatible + github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 // indirect + github.com/dustin/go-humanize v1.0.0 // indirect + github.com/ethereum/go-ethereum v1.8.27 + github.com/gdamore/tcell v1.1.2 + github.com/golang/protobuf v1.3.1 // indirect + github.com/google/open-location-code/go v0.0.0-20190603181809-cf814bded323 + github.com/magiconair/properties v1.8.1 + github.com/pkg/errors v0.8.1 + github.com/rivo/tview v0.0.0-20190609162513-b62197ade412 + github.com/stretchr/testify v1.3.0 // indirect + golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5 + golang.org/x/net v0.0.0-20190611141213-3f473d35a33a +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000000000000000000000000000000000000..63f8201a71105e02dd25090000c3cb389a239901 --- /dev/null +++ b/go.sum @@ -0,0 +1,50 @@ +github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9 h1:HD8gA2tkByhMAwYaFAX9w2l7vxvBQ5NMoxDrkhqhtn4= +github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= +github.com/DATA-DOG/go-sqlmock v1.3.3 h1:CWUqKXe0s8A2z6qCgkP4Kru7wC11YoAnoupUKFDnH08= +github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgraph-io/badger v2.0.0-rc.2.0.20190610165348-a804ffa35fb9+incompatible h1:FE+hrPTLWIHxQzGTbjReHgpkj7+hFWC+L6b2fAxBG+I= +github.com/dgraph-io/badger v2.0.0-rc.2.0.20190610165348-a804ffa35fb9+incompatible/go.mod h1:VZxzAIRPHRVNRKRo6AXrX9BJegn6il06VMTZVJYCIjQ= +github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA= +github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= +github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/ethereum/go-ethereum v1.8.27 h1:d+gkiLaBDk5fn3Pe/xNVaMrB/ozI+AUB2IlVBp29IrY= +github.com/ethereum/go-ethereum v1.8.27/go.mod h1:PwpWDrCLZrV+tfrhqqF6kPknbISMHaJv9Ln3kPCZLwY= +github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko= +github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg= +github.com/gdamore/tcell v1.1.2 h1:Afe8cU6SECC06UmvaJ55Jr3Eh0tz/ywLjqWYqjGZp3s= +github.com/gdamore/tcell v1.1.2/go.mod h1:h3kq4HO9l2On+V9ed8w8ewqQEmGCSSHOgQ+2h8uzurE= +github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/google/open-location-code/go v0.0.0-20190603181809-cf814bded323 h1:s+TV4ZPsmXNvFxr9gzRTOlqf7DT862ieiOGRWA9M3oM= +github.com/google/open-location-code/go v0.0.0-20190603181809-cf814bded323/go.mod h1:eJfRN6aj+kR/rnua/rw9jAgYhqoMHldQkdTi+sePRKk= +github.com/lucasb-eyer/go-colorful v1.0.2 h1:mCMFu6PgSozg9tDNMMK3g18oJBX7oYGrC09mS6CXfO4= +github.com/lucasb-eyer/go-colorful v1.0.2/go.mod h1:0MS4r+7BZKSJ5mw4/S5MPN+qHFF1fYclkSPilDOKW0s= +github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= +github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y= +github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rivo/tview v0.0.0-20190609162513-b62197ade412 h1:muOFMct2jVhlSw9S3MRrxevpsAIPJeTh4e1Z7pEEQhE= +github.com/rivo/tview v0.0.0-20190609162513-b62197ade412/go.mod h1:+rKjP5+h9HMwWRpAfhIkkQ9KE3m3Nz5rwn7YtUpwgqk= +github.com/rivo/uniseg v0.0.0-20190513083848-b9f5b9457d44 h1:XKCbzPvK4/BbMXoMJOkYP2ANxiAEO0HM1xn6psSbXxY= +github.com/rivo/uniseg v0.0.0-20190513083848-b9f5b9457d44/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5 h1:58fnuSXlxZmFdJyvtTFVmVhcMLU6v5fEb/ok4wyqtNU= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190611141213-3f473d35a33a h1:+KkCgOMgnKSgenxTBoiwkMqTiouMIy/3o8RLdmSbGoY= +golang.org/x/net v0.0.0-20190611141213-3f473d35a33a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/main.go b/main.go index 514f031a620ef1fd5af3b65c3178aa0f4c8a0300..2eb9d49f9bc77f40923a2f0f0846d38ffab50591 100644 --- a/main.go +++ b/main.go @@ -6,9 +6,15 @@ import ( "github.com/iotaledger/goshimmer/plugins/autopeering" "github.com/iotaledger/goshimmer/plugins/cli" "github.com/iotaledger/goshimmer/plugins/gossip" + gossip_on_solidification "github.com/iotaledger/goshimmer/plugins/gossip-on-solidification" "github.com/iotaledger/goshimmer/plugins/gracefulshutdown" "github.com/iotaledger/goshimmer/plugins/statusscreen" + statusscreen_tps "github.com/iotaledger/goshimmer/plugins/statusscreen-tps" "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" + webapi_spammer "github.com/iotaledger/goshimmer/plugins/webapi-spammer" ) func main() { @@ -16,9 +22,17 @@ func main() { cli.PLUGIN, autopeering.PLUGIN, gossip.PLUGIN, + gossip_on_solidification.PLUGIN, tangle.PLUGIN, analysis.PLUGIN, - statusscreen.PLUGIN, gracefulshutdown.PLUGIN, + tipselection.PLUGIN, + + statusscreen.PLUGIN, + statusscreen_tps.PLUGIN, + + webapi.PLUGIN, + webapi_gtta.PLUGIN, + webapi_spammer.PLUGIN, ) } diff --git a/packages/curl/batch_hasher.go b/packages/curl/batch_hasher.go index 649550a0c1266d0d6a0b2b7f704e8745997c5f79..c139d50f9923a40d7ca3aa6507fd3a447809133d 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 61d0fcbe031ec2c232c653130b8fcccb6d49f2c3..5efe6e63403010e870b275e5d80fc121d089b6fe 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 34793818e926a513d659b71fec5afcf53a0565fd..91d2e067e6b7cb4a3f2a0aae850ec52a65b34ba3 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 a9e9c406eb9774b44eba80c58b10ddccf174824b..f2701bcc27e51d3b40318d3e9c99a1ae2ba855c4 100644 --- a/packages/datastructure/lru_cache.go +++ b/packages/datastructure/lru_cache.go @@ -2,6 +2,8 @@ package datastructure import ( "sync" + + "github.com/iotaledger/goshimmer/packages/typeutils" ) type lruCacheElement struct { @@ -14,14 +16,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 +60,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 +103,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); !typeutils.IsInterfaceNil(callbackResult) { + result = callbackResult + + cache.set(key, callbackResult) } else { if err := cache.doublyLinkedList.removeEntry(entry); err != nil { panic(err) @@ -94,6 +114,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 +190,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 0000000000000000000000000000000000000000..61f0e9c1fabd4697e16ce48e58f24ce2c07d7238 --- /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 0000000000000000000000000000000000000000..7e43230797dae96ac5fe2eb342276acd151e210c --- /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 b153e4aca89e1af3efdc3dcfa0fa9cd4e8e5136f..45084ba6faa58371eb5eb55c2ba2d1b084f95a85 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/filter/byte_array_filter.go b/packages/filter/byte_array_filter.go index ba5aca0a4910142c3c570849b2861203466367b8..d8cb89fe010d8571331a2f4dbc0e31b328cbf275 100644 --- a/packages/filter/byte_array_filter.go +++ b/packages/filter/byte_array_filter.go @@ -3,7 +3,7 @@ package filter import ( "sync" - "github.com/iotaledger/goshimmer/packages/typeconversion" + "github.com/iotaledger/goshimmer/packages/typeutils" ) type ByteArrayFilter struct { @@ -25,20 +25,20 @@ func (filter *ByteArrayFilter) Contains(byteArray []byte) bool { filter.mutex.RLock() defer filter.mutex.RUnlock() - _, exists := filter.byteArraysByKey[typeconversion.BytesToString(byteArray)] + _, exists := filter.byteArraysByKey[typeutils.BytesToString(byteArray)] return exists } func (filter *ByteArrayFilter) Add(byteArray []byte) bool { - key := typeconversion.BytesToString(byteArray) + key := typeutils.BytesToString(byteArray) filter.mutex.Lock() defer filter.mutex.Unlock() if _, exists := filter.byteArraysByKey[key]; !exists { if len(filter.byteArrays) == filter.size { - delete(filter.byteArraysByKey, typeconversion.BytesToString(filter.byteArrays[0])) + delete(filter.byteArraysByKey, typeutils.BytesToString(filter.byteArrays[0])) filter.byteArrays = append(filter.byteArrays[1:], byteArray) } else { diff --git a/packages/model/approvers/approvers.go b/packages/model/approvers/approvers.go new file mode 100644 index 0000000000000000000000000000000000000000..be5f753073d7adc2b1f1b9b754aa8fb957114253 --- /dev/null +++ b/packages/model/approvers/approvers.go @@ -0,0 +1,22 @@ +package approvers + +import ( + "sync" + + "github.com/iotaledger/goshimmer/packages/ternary" +) + +type Approvers struct { + hash ternary.Trinary + hashes map[ternary.Trinary]bool + hashesMutex sync.RWMutex + modified bool +} + +func NewApprovers(hash ternary.Trinary) *Approvers { + return &Approvers{ + hash: hash, + hashes: make(map[ternary.Trinary]bool), + modified: false, + } +} diff --git a/packages/model/meta_transaction/constants.go b/packages/model/meta_transaction/constants.go new file mode 100644 index 0000000000000000000000000000000000000000..498b5798160d59feb6b1bc0690404078675428ba --- /dev/null +++ b/packages/model/meta_transaction/constants.go @@ -0,0 +1,35 @@ +package meta_transaction + +import ( + "github.com/iotaledger/goshimmer/packages/ternary" +) + +const ( + SHARD_MARKER_OFFSET = 0 + TRUNK_TRANSACTION_HASH_OFFSET = SHARD_MARKER_END + BRANCH_TRANSACTION_HASH_OFFSET = TRUNK_TRANSACTION_HASH_END + HEAD_OFFSET = BRANCH_TRANSACTION_HASH_END + TAIL_OFFSET = HEAD_END + TRANSACTION_TYPE_OFFSET = TAIL_END + DATA_OFFSET = TRANSACTION_TYPE_END + + SHARD_MARKER_SIZE = 11 + TRUNK_TRANSACTION_HASH_SIZE = 243 + BRANCH_TRANSACTION_HASH_SIZE = 243 + HEAD_SIZE = 1 + TAIL_SIZE = 1 + 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 + BRANCH_TRANSACTION_HASH_END = BRANCH_TRANSACTION_HASH_OFFSET + BRANCH_TRANSACTION_HASH_SIZE + HEAD_END = HEAD_OFFSET + HEAD_SIZE + TAIL_END = TAIL_OFFSET + TAIL_SIZE + TRANSACTION_TYPE_END = TRANSACTION_TYPE_OFFSET + TRANSACTION_TYPE_SIZE + 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 new file mode 100644 index 0000000000000000000000000000000000000000..456e7e36f57dd58278ba81f8354671caacd9a69e --- /dev/null +++ b/packages/model/meta_transaction/meta_transaction.go @@ -0,0 +1,478 @@ +package meta_transaction + +import ( + "sync" + + "github.com/iotaledger/goshimmer/packages/curl" + "github.com/iotaledger/goshimmer/packages/ternary" +) + +type MetaTransaction struct { + hash *ternary.Trinary + weightMagnitude int + + shardMarker *ternary.Trinary + trunkTransactionHash *ternary.Trinary + branchTransactionHash *ternary.Trinary + head *bool + tail *bool + transactionType *ternary.Trinary + data ternary.Trits + modified bool + + hasherMutex sync.RWMutex + hashMutex sync.RWMutex + shardMarkerMutex sync.RWMutex + trunkTransactionHashMutex sync.RWMutex + branchTransactionHashMutex sync.RWMutex + headMutex sync.RWMutex + tailMutex sync.RWMutex + transactionTypeMutex sync.RWMutex + dataMutex sync.RWMutex + bytesMutex sync.RWMutex + modifiedMutex sync.RWMutex + + trits ternary.Trits + bytes []byte +} + +func New() *MetaTransaction { + return FromTrits(make(ternary.Trits, MARSHALLED_TOTAL_SIZE)) +} + +func FromTrits(trits ternary.Trits) *MetaTransaction { + return &MetaTransaction{ + trits: trits, + } +} + +func FromBytes(bytes []byte) (result *MetaTransaction) { + result = FromTrits(ternary.BytesToTrits(bytes)[:MARSHALLED_TOTAL_SIZE]) + result.bytes = bytes + + return +} + +func (this *MetaTransaction) BlockHasher() { + this.hasherMutex.RLock() +} + +func (this *MetaTransaction) UnblockHasher() { + this.hasherMutex.RUnlock() +} + +func (this *MetaTransaction) ReHash() { + this.hashMutex.Lock() + defer this.hashMutex.Unlock() + this.hash = nil + + this.bytesMutex.Lock() + defer this.bytesMutex.Unlock() + this.bytes = nil +} + +// retrieves the hash of the transaction +func (this *MetaTransaction) GetHash() (result ternary.Trinary) { + this.hashMutex.RLock() + if this.hash == nil { + this.hashMutex.RUnlock() + this.hashMutex.Lock() + defer this.hashMutex.Unlock() + if this.hash == nil { + this.hasherMutex.Lock() + this.parseHashRelatedDetails() + this.hasherMutex.Unlock() + } + } else { + defer this.hashMutex.RUnlock() + } + + result = *this.hash + + return +} + +// retrieves weight magnitude of the transaction (amount of pow invested) +func (this *MetaTransaction) GetWeightMagnitude() (result int) { + this.hashMutex.RLock() + if this.hash == nil { + this.hashMutex.RUnlock() + this.hashMutex.Lock() + defer this.hashMutex.Unlock() + if this.hash == nil { + this.hasherMutex.Lock() + this.parseHashRelatedDetails() + this.hasherMutex.Unlock() + } + } else { + defer this.hashMutex.RUnlock() + } + + result = this.weightMagnitude + + return +} + +// hashes the transaction using curl (without locking - internal usage) +func (this *MetaTransaction) parseHashRelatedDetails() { + hashTrits := <-curl.CURLP81.Hash(this.trits) + hashTrinary := hashTrits.ToTrinary() + + this.hash = &hashTrinary + this.weightMagnitude = hashTrits.TrailingZeroes() +} + +// getter for the shard marker (supports concurrency) +func (this *MetaTransaction) GetShardMarker() (result ternary.Trinary) { + this.shardMarkerMutex.RLock() + if this.shardMarker == nil { + this.shardMarkerMutex.RUnlock() + this.shardMarkerMutex.Lock() + defer this.shardMarkerMutex.Unlock() + if this.shardMarker == nil { + shardMarker := this.trits[SHARD_MARKER_OFFSET:SHARD_MARKER_END].ToTrinary() + + this.shardMarker = &shardMarker + } + } else { + defer this.shardMarkerMutex.RUnlock() + } + + result = *this.shardMarker + + return +} + +// setter for the shard marker (supports concurrency) +func (this *MetaTransaction) SetShardMarker(shardMarker ternary.Trinary) bool { + this.shardMarkerMutex.RLock() + if this.shardMarker == nil || *this.shardMarker != shardMarker { + this.shardMarkerMutex.RUnlock() + this.shardMarkerMutex.Lock() + defer this.shardMarkerMutex.Unlock() + if this.shardMarker == nil || *this.shardMarker != shardMarker { + this.shardMarker = &shardMarker + + this.hasherMutex.RLock() + copy(this.trits[SHARD_MARKER_OFFSET:SHARD_MARKER_END], shardMarker.ToTrits()[:SHARD_MARKER_SIZE]) + this.hasherMutex.RUnlock() + + this.SetModified(true) + this.ReHash() + + return true + } + } else { + this.shardMarkerMutex.RUnlock() + } + + return false +} + +// getter for the bundleHash (supports concurrency) +func (this *MetaTransaction) GetTrunkTransactionHash() (result ternary.Trinary) { + this.trunkTransactionHashMutex.RLock() + if this.trunkTransactionHash == nil { + this.trunkTransactionHashMutex.RUnlock() + this.trunkTransactionHashMutex.Lock() + defer this.trunkTransactionHashMutex.Unlock() + if this.trunkTransactionHash == nil { + trunkTransactionHash := this.trits[TRUNK_TRANSACTION_HASH_OFFSET:TRUNK_TRANSACTION_HASH_END].ToTrinary() + + this.trunkTransactionHash = &trunkTransactionHash + } + } else { + defer this.trunkTransactionHashMutex.RUnlock() + } + + result = *this.trunkTransactionHash + + return +} + +// setter for the trunkTransactionHash (supports concurrency) +func (this *MetaTransaction) SetTrunkTransactionHash(trunkTransactionHash ternary.Trinary) bool { + this.trunkTransactionHashMutex.RLock() + if this.trunkTransactionHash == nil || *this.trunkTransactionHash != trunkTransactionHash { + this.trunkTransactionHashMutex.RUnlock() + this.trunkTransactionHashMutex.Lock() + defer this.trunkTransactionHashMutex.Unlock() + if this.trunkTransactionHash == nil || *this.trunkTransactionHash != trunkTransactionHash { + this.trunkTransactionHash = &trunkTransactionHash + + this.hasherMutex.RLock() + copy(this.trits[TRUNK_TRANSACTION_HASH_OFFSET:TRUNK_TRANSACTION_HASH_END], trunkTransactionHash.ToTrits()[:TRUNK_TRANSACTION_HASH_SIZE]) + this.hasherMutex.RUnlock() + + this.SetModified(true) + this.ReHash() + + return true + } + } else { + this.trunkTransactionHashMutex.RUnlock() + } + + return false +} + +// getter for the bundleHash (supports concurrency) +func (this *MetaTransaction) GetBranchTransactionHash() (result ternary.Trinary) { + this.branchTransactionHashMutex.RLock() + if this.branchTransactionHash == nil { + this.branchTransactionHashMutex.RUnlock() + this.branchTransactionHashMutex.Lock() + defer this.branchTransactionHashMutex.Unlock() + if this.branchTransactionHash == nil { + branchTransactionHash := this.trits[BRANCH_TRANSACTION_HASH_OFFSET:BRANCH_TRANSACTION_HASH_END].ToTrinary() + + this.branchTransactionHash = &branchTransactionHash + } + } else { + defer this.branchTransactionHashMutex.RUnlock() + } + + result = *this.branchTransactionHash + + return +} + +// setter for the trunkTransactionHash (supports concurrency) +func (this *MetaTransaction) SetBranchTransactionHash(branchTransactionHash ternary.Trinary) bool { + this.branchTransactionHashMutex.RLock() + if this.branchTransactionHash == nil || *this.branchTransactionHash != branchTransactionHash { + this.branchTransactionHashMutex.RUnlock() + this.branchTransactionHashMutex.Lock() + defer this.branchTransactionHashMutex.Unlock() + if this.branchTransactionHash == nil || *this.branchTransactionHash != branchTransactionHash { + this.branchTransactionHash = &branchTransactionHash + + this.hasherMutex.RLock() + copy(this.trits[BRANCH_TRANSACTION_HASH_OFFSET:BRANCH_TRANSACTION_HASH_END], branchTransactionHash.ToTrits()[:BRANCH_TRANSACTION_HASH_SIZE]) + this.hasherMutex.RUnlock() + + this.SetModified(true) + this.ReHash() + + return true + } + } else { + this.branchTransactionHashMutex.RUnlock() + } + + return false +} + +// getter for the head flag (supports concurrency) +func (this *MetaTransaction) GetHead() (result bool) { + this.headMutex.RLock() + if this.head == nil { + this.headMutex.RUnlock() + this.headMutex.Lock() + defer this.headMutex.Unlock() + if this.head == nil { + head := this.trits[HEAD_OFFSET] == 1 + + this.head = &head + } + } else { + defer this.headMutex.RUnlock() + } + + result = *this.head + + return +} + +// setter for the head flag (supports concurrency) +func (this *MetaTransaction) SetHead(head bool) bool { + this.headMutex.RLock() + if this.head == nil || *this.head != head { + this.headMutex.RUnlock() + this.headMutex.Lock() + defer this.headMutex.Unlock() + if this.head == nil || *this.head != head { + this.head = &head + + this.hasherMutex.RLock() + if head { + this.trits[HEAD_OFFSET] = 1 + } else { + this.trits[HEAD_OFFSET] = 0 + } + this.hasherMutex.RUnlock() + + this.SetModified(true) + this.ReHash() + + return true + } + } else { + this.headMutex.RUnlock() + } + + return false +} + +// getter for the tail flag (supports concurrency) +func (this *MetaTransaction) GetTail() (result bool) { + this.tailMutex.RLock() + if this.tail == nil { + this.tailMutex.RUnlock() + this.tailMutex.Lock() + defer this.tailMutex.Unlock() + if this.tail == nil { + tail := this.trits[TAIL_OFFSET] == 1 + + this.tail = &tail + } + } else { + defer this.tailMutex.RUnlock() + } + + result = *this.tail + + return +} + +// setter for the tail flag (supports concurrency) +func (this *MetaTransaction) SetTail(tail bool) bool { + this.tailMutex.RLock() + if this.tail == nil || *this.tail != tail { + this.tailMutex.RUnlock() + this.tailMutex.Lock() + defer this.tailMutex.Unlock() + if this.tail == nil || *this.tail != tail { + this.tail = &tail + + this.hasherMutex.RLock() + if tail { + this.trits[TAIL_OFFSET] = 1 + } else { + this.trits[TAIL_OFFSET] = 0 + } + this.hasherMutex.RUnlock() + + this.SetModified(true) + this.ReHash() + + return true + } + } else { + this.tailMutex.RUnlock() + } + + return false +} + +// getter for the transaction type (supports concurrency) +func (this *MetaTransaction) GetTransactionType() (result ternary.Trinary) { + this.transactionTypeMutex.RLock() + if this.transactionType == nil { + this.transactionTypeMutex.RUnlock() + this.transactionTypeMutex.Lock() + defer this.transactionTypeMutex.Unlock() + if this.transactionType == nil { + transactionType := this.trits[TRANSACTION_TYPE_OFFSET:TRANSACTION_TYPE_END].ToTrinary() + + this.transactionType = &transactionType + } + } else { + defer this.transactionTypeMutex.RUnlock() + } + + result = *this.transactionType + + return +} + +// setter for the transaction type (supports concurrency) +func (this *MetaTransaction) SetTransactionType(transactionType ternary.Trinary) bool { + this.transactionTypeMutex.RLock() + if this.transactionType == nil || *this.transactionType != transactionType { + this.transactionTypeMutex.RUnlock() + this.transactionTypeMutex.Lock() + defer this.transactionTypeMutex.Unlock() + if this.transactionType == nil || *this.transactionType != transactionType { + this.transactionType = &transactionType + + this.hasherMutex.RLock() + copy(this.trits[TRANSACTION_TYPE_OFFSET:TRANSACTION_TYPE_END], transactionType.ToTrits()[:TRANSACTION_TYPE_SIZE]) + this.hasherMutex.RUnlock() + + this.SetModified(true) + this.ReHash() + + return true + } + } else { + this.transactionTypeMutex.RUnlock() + } + + return false +} + +// getter for the data slice (supports concurrency) +func (this *MetaTransaction) GetData() (result ternary.Trits) { + this.dataMutex.RLock() + if this.data == nil { + this.dataMutex.RUnlock() + this.dataMutex.Lock() + defer this.dataMutex.Unlock() + if this.data == nil { + this.data = this.trits[DATA_OFFSET:DATA_END] + } + } else { + defer this.dataMutex.RUnlock() + } + + result = this.data + + return +} + +func (this *MetaTransaction) GetTrits() (result ternary.Trits) { + result = make(ternary.Trits, len(this.trits)) + + this.hasherMutex.Lock() + copy(result, this.trits) + this.hasherMutex.Unlock() + + return +} + +func (this *MetaTransaction) GetBytes() (result []byte) { + this.bytesMutex.RLock() + if this.bytes == nil { + this.bytesMutex.RUnlock() + this.bytesMutex.Lock() + defer this.bytesMutex.Unlock() + + this.hasherMutex.Lock() + this.bytes = this.trits.ToBytes() + this.hasherMutex.Unlock() + } else { + this.bytesMutex.RUnlock() + } + + result = make([]byte, len(this.bytes)) + copy(result, this.bytes) + + return +} + +// returns true if the transaction contains unsaved changes (supports concurrency) +func (this *MetaTransaction) GetModified() bool { + this.modifiedMutex.RLock() + defer this.modifiedMutex.RUnlock() + + return this.modified +} + +// sets the modified flag which controls if a transaction is going to be saved (supports concurrency) +func (this *MetaTransaction) SetModified(modified bool) { + this.modifiedMutex.Lock() + defer this.modifiedMutex.Unlock() + + this.modified = modified +} diff --git a/packages/model/meta_transaction/meta_transaction_test.go b/packages/model/meta_transaction/meta_transaction_test.go new file mode 100644 index 0000000000000000000000000000000000000000..1c73e4b4b03d97a61fe717fdd01c6ab01d9e66ba --- /dev/null +++ b/packages/model/meta_transaction/meta_transaction_test.go @@ -0,0 +1,54 @@ +package meta_transaction + +import ( + "fmt" + "sync" + "testing" + + "github.com/iotaledger/goshimmer/packages/ternary" + "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 + transactionType := ternary.Trinary("9999999999999999999999") + + transaction := New() + transaction.SetShardMarker(shardMarker) + transaction.SetTrunkTransactionHash(trunkTransactionHash) + transaction.SetBranchTransactionHash(branchTransactionHash) + transaction.SetHead(head) + transaction.SetTail(tail) + transaction.SetTransactionType(transactionType) + + 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.GetTransactionType(), transactionType) + assert.Equal(t, transaction.GetHash(), FromBytes(transaction.GetBytes()).GetHash()) + + fmt.Println(transaction.GetHash()) +} + +func BenchmarkMetaTransaction_GetHash(b *testing.B) { + var waitGroup sync.WaitGroup + + for i := 0; i < b.N; i++ { + waitGroup.Add(1) + + go func() { + New().GetHash() + + waitGroup.Done() + }() + } + + waitGroup.Wait() +} diff --git a/packages/model/value_transaction/constants.go b/packages/model/value_transaction/constants.go new file mode 100644 index 0000000000000000000000000000000000000000..3025e1c74d9526fbd10d88d5c217d9443fd797a6 --- /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 new file mode 100644 index 0000000000000000000000000000000000000000..6eff46bf9b0521cca88cf7552e549c8e7cafefd8 --- /dev/null +++ b/packages/model/value_transaction/value_transaction.go @@ -0,0 +1,287 @@ +package value_transaction + +import ( + "sync" + + "github.com/iotaledger/goshimmer/packages/model/meta_transaction" + "github.com/iotaledger/goshimmer/packages/ternary" +) + +type ValueTransaction struct { + *meta_transaction.MetaTransaction + + 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) { + result = &ValueTransaction{ + MetaTransaction: meta_transaction.New(), + } + + result.trits = result.MetaTransaction.GetData() + + return +} + +func FromMetaTransaction(metaTransaction *meta_transaction.MetaTransaction) *ValueTransaction { + return &ValueTransaction{ + MetaTransaction: metaTransaction, + trits: metaTransaction.GetData(), + } +} + +func FromBytes(bytes []byte) (result *ValueTransaction) { + result = &ValueTransaction{ + MetaTransaction: meta_transaction.FromTrits(ternary.BytesToTrits(bytes)[:meta_transaction.MARSHALLED_TOTAL_SIZE]), + } + + result.trits = result.MetaTransaction.GetData() + + 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 new file mode 100644 index 0000000000000000000000000000000000000000..35d3197f57e4d422dd4a528686d808cbadc2ce8c --- /dev/null +++ b/packages/model/value_transaction/value_transaction_test.go @@ -0,0 +1,25 @@ +package value_transaction + +import ( + "fmt" + "testing" + + "github.com/iotaledger/goshimmer/packages/ternary" + "github.com/magiconair/properties/assert" +) + +func TestValueTransaction_SettersGetters(t *testing.T) { + address := ternary.Trinary("A9999999999999999999999999999999999999999999999999999999999999999999999999999999F") + + transaction := New() + transaction.SetAddress(address) + + transactionCopy := FromMetaTransaction(transaction.MetaTransaction) + fmt.Println(transactionCopy.GetAddress()) + + 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 afb96cf790d4f97d15a465aac25df8cfc03f804a..7cc01646ce33e7aa9059ff5fec43e4a9e925c9fb 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 0000000000000000000000000000000000000000..d7d1665545cf142505afd9936048cc05dd3dfa3c --- /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 1e4ba027a57cbf2217ab7ed5d4346384b8cd30e2..745ca3542b18c9f8b0bb61ab2a804d18f7f036dd 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 0b821f5e6b998270843334086e001efa5af5ce8f..0000000000000000000000000000000000000000 --- 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 - MARSHALED_TOTAL_SIZE = NONCE_END -) diff --git a/packages/transaction/transaction.go b/packages/transaction/transaction.go deleted file mode 100644 index 0c606d8a8989c26cc86e6758431b8bf7de7700da..0000000000000000000000000000000000000000 --- 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)[:MARSHALED_TOTAL_SIZE]) - transaction.Bytes = bytes - - return transaction -} diff --git a/packages/transactionspammer/transactionspammer.go b/packages/transactionspammer/transactionspammer.go new file mode 100644 index 0000000000000000000000000000000000000000..503a0f5af72c6f3120b0749ebb30bf5778ba3cd5 --- /dev/null +++ b/packages/transactionspammer/transactionspammer.go @@ -0,0 +1,83 @@ +package transactionspammer + +import ( + "sync" + "time" + + "github.com/iotaledger/goshimmer/packages/daemon" + "github.com/iotaledger/goshimmer/packages/model/value_transaction" + "github.com/iotaledger/goshimmer/plugins/gossip" + "github.com/iotaledger/goshimmer/plugins/tipselection" +) + +var spamming = false + +var startMutex sync.Mutex + +var shutdownSignal chan int + +func Start(tps int64) { + startMutex.Lock() + + if !spamming { + shutdownSignal = make(chan int, 1) + + func(shutdownSignal chan int) { + daemon.BackgroundWorker(func() { + for { + start := time.Now() + sentCounter := int64(0) + totalSentCounter := int64(0) + + for { + select { + case <-daemon.ShutdownSignal: + return + + case <-shutdownSignal: + return + + default: + sentCounter++ + totalSentCounter++ + + tx := value_transaction.New() + tx.SetValue(totalSentCounter) + tx.SetBranchTransactionHash(tipselection.GetRandomTip()) + tx.SetTrunkTransactionHash(tipselection.GetRandomTip()) + + gossip.Events.ReceiveTransaction.Trigger(tx.MetaTransaction) + + if sentCounter >= tps { + duration := time.Since(start) + if duration < time.Second { + time.Sleep(time.Second - duration) + + start = time.Now() + } + + sentCounter = 0 + } + } + } + } + }) + }(shutdownSignal) + + spamming = true + } + + startMutex.Unlock() +} + +func Stop() { + startMutex.Lock() + + if spamming { + close(shutdownSignal) + + spamming = false + } + + startMutex.Unlock() +} diff --git a/packages/typeconversion/typeconversion.go b/packages/typeutils/typeutils.go similarity index 74% rename from packages/typeconversion/typeconversion.go rename to packages/typeutils/typeutils.go index 4ed9a5073dd7e14f5677ed19f8f71e3acc2a9fa6..eed90cf95478febc1fd3248a2d3ce5c90b7007bf 100644 --- a/packages/typeconversion/typeconversion.go +++ b/packages/typeutils/typeutils.go @@ -1,4 +1,4 @@ -package typeconversion +package typeutils import ( "reflect" @@ -16,3 +16,7 @@ func StringToBytes(str string) []byte { return *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{Data: hdr.Data, Len: hdr.Len, Cap: hdr.Len})) } + +func IsInterfaceNil(param interface{}) bool { + return param == nil || (*[2]uintptr)(unsafe.Pointer(¶m))[1] == 0 +} diff --git a/plugins/gossip-on-solidification/plugin.go b/plugins/gossip-on-solidification/plugin.go new file mode 100644 index 0000000000000000000000000000000000000000..1c2268007bd1336b3643f294d3572953c38fa60a --- /dev/null +++ b/plugins/gossip-on-solidification/plugin.go @@ -0,0 +1,15 @@ +package gossip_on_solidification + +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/gossip" + "github.com/iotaledger/goshimmer/plugins/tangle" +) + +var PLUGIN = node.NewPlugin("Gossip On Solidification", func(plugin *node.Plugin) { + tangle.Events.TransactionSolid.Attach(events.NewClosure(func(tx *value_transaction.ValueTransaction) { + gossip.SendTransaction(tx.MetaTransaction) + })) +}) diff --git a/plugins/gossip/events.go b/plugins/gossip/events.go index 765e106f0509a7856685c04b3f96d02d28fe4366..6d3183820d2f8c5986751e13e20896c3cc3f1e8b 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 dd03c22a161369ec919e4a22dfb9001dc3c2fe89..f61e08d86a6d0eac60eff86fe65df56acb75105c 100644 --- a/plugins/gossip/neighbors.go +++ b/plugins/gossip/neighbors.go @@ -199,7 +199,7 @@ func AddNeighbor(newNeighbor *Neighbor) { 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 d647a90686f3108ad7b1d6bac86617281ec68779..0f02bd1404f577c1d265e239493aeec712e97ead 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 e177271f4b096ff6553abbb30925926c1da4afbd..b0adc6addc6ce15fd4029e9acad0009fca7b805e 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.MARSHALED_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.MARSHALED_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.MARSHALED_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 337095271bf49fa1d7d1eb705307ac9e11418368..354c0c3bf0557265e7806d249a20b38e294ef667 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 9a96fdb25f475b11b64b77c74da22f215de03843..742ac36929dfabd2d64b293c8906f397b02575a1 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 b970119aef82076c098d3bfdf56ae49c5971af5e..86b6349a9f3b32d7b3c10d07331647929500d2bb 100644 --- a/plugins/gossip/transaction_processor_test.go +++ b/plugins/gossip/transaction_processor_test.go @@ -4,12 +4,16 @@ 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) { +<<<<<<< HEAD byteArray := setupTransaction(transaction.MARSHALED_TOTAL_SIZE / ternary.NUMBER_OF_TRITS_IN_A_BYTE) +======= + byteArray := setupTransaction(meta_transaction.MARSHALLED_TOTAL_SIZE / ternary.NUMBER_OF_TRITS_IN_A_BYTE) +>>>>>>> master b.ResetTimer() @@ -19,7 +23,11 @@ func BenchmarkProcessSimilarTransactionsFiltered(b *testing.B) { } func BenchmarkProcessSimilarTransactionsUnfiltered(b *testing.B) { +<<<<<<< HEAD byteArray := setupTransaction(transaction.MARSHALED_TOTAL_SIZE / ternary.NUMBER_OF_TRITS_IN_A_BYTE) +======= + byteArray := setupTransaction(meta_transaction.MARSHALLED_TOTAL_SIZE / ternary.NUMBER_OF_TRITS_IN_A_BYTE) +>>>>>>> master b.ResetTimer() @@ -29,7 +37,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/statusscreen-tps/plugin.go b/plugins/statusscreen-tps/plugin.go new file mode 100644 index 0000000000000000000000000000000000000000..eabb868f15e786ecea795ceac8173f2984d4a31c --- /dev/null +++ b/plugins/statusscreen-tps/plugin.go @@ -0,0 +1,56 @@ +package statusscreen_tps + +import ( + "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/statusscreen" + "github.com/iotaledger/goshimmer/plugins/tangle" +) + +var receivedTpsCounter uint64 + +var solidTpsCounter uint64 + +var receivedTps uint64 + +var solidTps uint64 + +var PLUGIN = node.NewPlugin("Statusscreen TPS", func(plugin *node.Plugin) { + gossip.Events.ReceiveTransaction.Attach(events.NewClosure(func(_ *meta_transaction.MetaTransaction) { + atomic.AddUint64(&receivedTpsCounter, 1) + })) + + tangle.Events.TransactionSolid.Attach(events.NewClosure(func(_ *value_transaction.ValueTransaction) { + atomic.AddUint64(&solidTpsCounter, 1) + })) + + statusscreen.AddHeaderInfo(func() (s string, s2 string) { + return "TPS", strconv.FormatUint(atomic.LoadUint64(&receivedTps), 10) + " received / " + strconv.FormatUint(atomic.LoadUint64(&solidTps), 10) + " new" + }) +}, func(plugin *node.Plugin) { + daemon.BackgroundWorker(func() { + ticker := time.NewTicker(time.Second) + + for { + select { + case <-daemon.ShutdownSignal: + return + + case <-ticker.C: + atomic.StoreUint64(&receivedTps, atomic.LoadUint64(&receivedTpsCounter)) + atomic.StoreUint64(&solidTps, atomic.LoadUint64(&solidTpsCounter)) + + atomic.StoreUint64(&receivedTpsCounter, 0) + atomic.StoreUint64(&solidTpsCounter, 0) + } + } + }) +}) diff --git a/plugins/statusscreen/ui_header_bar.go b/plugins/statusscreen/ui_header_bar.go index 8ea81c0614c3e07a3ac42d83451d92759d0dc2a5..66ab397c5c45f1f24c868611f6f966cf023fe7de 100644 --- a/plugins/statusscreen/ui_header_bar.go +++ b/plugins/statusscreen/ui_header_bar.go @@ -17,6 +17,12 @@ import ( var start = time.Now() +var headerInfos = make([]func() (string, string), 0) + +func AddHeaderInfo(generator func() (string, string)) { + headerInfos = append(headerInfos, generator) +} + type UIHeaderBar struct { Primitive *tview.Grid LogoContainer *tview.TextView @@ -62,9 +68,16 @@ func (headerBar *UIHeaderBar) Update() { fmt.Fprintln(headerBar.InfoContainer) fmt.Fprintln(headerBar.InfoContainer, "[::d]COO-LESS IOTA PROTOTYPE - [::b]Status: [green::b]SYNCED ") - fmt.Fprintln(headerBar.InfoContainer) - fmt.Fprintln(headerBar.InfoContainer) - fmt.Fprintln(headerBar.InfoContainer) + for i := 0; i < 3-len(headerInfos); i++ { + fmt.Fprintln(headerBar.InfoContainer) + } + + for _, infoGenerator := range headerInfos { + fieldName, fieldValue := infoGenerator() + fmt.Fprintf(headerBar.InfoContainer, "[::b]%v: [::d]%40v ", fieldName, fieldValue) + fmt.Fprintln(headerBar.InfoContainer) + } + fmt.Fprintf(headerBar.InfoContainer, "[::b]Node ID: [::d]%40v ", accountability.OwnId().StringIdentifier) fmt.Fprintln(headerBar.InfoContainer) fmt.Fprintf(headerBar.InfoContainer, "[::b]Neighbors: [::d]%40v ", strconv.Itoa(len(chosenneighbors.INSTANCE.Peers))+" chosen / "+strconv.Itoa(len(acceptedneighbors.INSTANCE.Peers))+" accepted") diff --git a/plugins/tangle/api.go b/plugins/tangle/api.go index 2a69fcc5db54efb08920798c2da4381cce64b8ac..3e40d0caa2ca91f45ff0d617d7ba178182b6963f 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 1fa2f0fbf9d3fc720b9c55b6de8ca77f571e59c1..6b2f03d9b3a209a36064ace7eb914f608b17e0f2 100644 --- a/plugins/tangle/approvers.go +++ b/plugins/tangle/approvers.go @@ -9,7 +9,7 @@ import ( "github.com/iotaledger/goshimmer/packages/datastructure" "github.com/iotaledger/goshimmer/packages/errors" "github.com/iotaledger/goshimmer/packages/ternary" - "github.com/iotaledger/goshimmer/packages/typeconversion" + "github.com/iotaledger/goshimmer/packages/typeutils" ) // region global public api //////////////////////////////////////////////////////////////////////////////////////////// @@ -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.(*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) } @@ -132,13 +132,13 @@ func (approvers *Approvers) Unmarshal(data []byte) (err errors.IdentifiableError approvers.hashesMutex.Lock() - approvers.hash = ternary.Trinary(typeconversion.BytesToString(data[MARSHALED_APPROVERS_HASH_START:MARSHALED_APPROVERS_HASH_END])) + approvers.hash = ternary.Trinary(typeutils.BytesToString(data[MARSHALED_APPROVERS_HASH_START:MARSHALED_APPROVERS_HASH_END])) approvers.hashes = make(map[ternary.Trinary]bool, hashesCount) for i := uint64(0); i < hashesCount; i++ { var HASH_START = MARSHALED_APPROVERS_HASHES_START + i*(MARSHALED_APPROVERS_HASH_SIZE) var HASH_END = HASH_START * MARSHALED_APPROVERS_HASH_SIZE - approvers.hashes[ternary.Trinary(typeconversion.BytesToString(data[HASH_START:HASH_END]))] = true + approvers.hashes[ternary.Trinary(typeutils.BytesToString(data[HASH_START:HASH_END]))] = true } approvers.hashesMutex.Unlock() @@ -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 b7e4b684e65b947bc14a1df4a4a4203434bb2baa..d5f7cfccb0db544d44c45a20205658952bcc0f66 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 fbfdc548c346587d7f8a0c2d6009c4c148b3cc2c..511d52d2cb5bc3d053a5ca96c11cfd6841c33a6a 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 bb8883a58a14f6fada96135e0a5cc24d36a29afc..e29ecd20c31dc13dbf3638cf5e880ae328411d37 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 d662d7cc7bd44e204c9cfd95740d7946c5836000..c5b0d38d90e949415adba6cfc095fbe7e572a978 100644 --- a/plugins/tangle/solidifier.go +++ b/plugins/tangle/solidifier.go @@ -3,26 +3,64 @@ 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() + txMetadata, metaDataErr := GetTransactionMetadata(transaction.GetHash(), NewTransactionMetadata) if metaDataErr != nil { err = metaDataErr @@ -34,7 +72,7 @@ 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); branchErr != nil { err = branchErr @@ -42,7 +80,7 @@ func checkSolidity(transaction *Transaction) (result bool, err errors.Identifiab 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 daa166c4d4ec8072faa451081ddf91e3e05d586c..0000000000000000000000000000000000000000 --- a/plugins/tangle/transaction.go +++ /dev/null @@ -1,683 +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 - 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.MARSHALED_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 d21b5ef8516adec1bb9d307dab29ac4a74e32663..7798e0396857691537b9423f7fa2faad25053cad 100644 --- a/plugins/tangle/transaction_metadata.go +++ b/plugins/tangle/transaction_metadata.go @@ -7,7 +7,7 @@ import ( "github.com/iotaledger/goshimmer/packages/bitutils" "github.com/iotaledger/goshimmer/packages/errors" "github.com/iotaledger/goshimmer/packages/ternary" - "github.com/iotaledger/goshimmer/packages/typeconversion" + "github.com/iotaledger/goshimmer/packages/typeutils" ) // region type definition and constructor ////////////////////////////////////////////////////////////////////////////// @@ -228,7 +228,7 @@ func (metadata *TransactionMetadata) Unmarshal(data []byte) errors.IdentifiableE metadata.finalizedMutex.Lock() defer metadata.finalizedMutex.Unlock() - metadata.hash = ternary.Trinary(typeconversion.BytesToString(data[MARSHALED_HASH_START:MARSHALED_HASH_END])) + metadata.hash = ternary.Trinary(typeutils.BytesToString(data[MARSHALED_HASH_START:MARSHALED_HASH_END])) if err := metadata.receivedTime.UnmarshalBinary(data[MARSHALED_RECEIVED_TIME_START:MARSHALED_RECEIVED_TIME_END]); err != nil { return ErrUnmarshalFailed.Derive(err, "could not unmarshal the received time") @@ -252,23 +252,6 @@ func (metadata *TransactionMetadata) Unmarshal(data []byte) errors.IdentifiableE // region database functions /////////////////////////////////////////////////////////////////////////////////////////// -func (metadata *TransactionMetadata) Store() errors.IdentifiableError { - if metadata.GetModified() { - marshaledMetadata, err := metadata.Marshal() - if err != nil { - return err - } - - if err := transactionMetadataDatabase.Set(metadata.GetHash().CastToBytes(), marshaledMetadata); 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 0000000000000000000000000000000000000000..7980a59557a1db7c66006c1ebd8a1e582a6d9016 --- /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 0000000000000000000000000000000000000000..4457ddc026b922a3802e844d6b416677ef2af345 --- /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 0000000000000000000000000000000000000000..cb8274bfed54cab78ce985ab7ecd001795b52f2e --- /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 0000000000000000000000000000000000000000..3a97085736a984f78ea8df9528ba8d53b68d9721 --- /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, webResponse{ + Duration: time.Since(start).Nanoseconds() / 1e6, + BranchTransaction: branchTransactionHash, + TrunkTransaction: trunkTransactionHash, + }) +} + +type webResponse struct { + Duration int64 `json:"duration"` + BranchTransaction ternary.Trinary `json:"branchTransaction"` + TrunkTransaction ternary.Trinary `json:"trunkTransaction"` +} diff --git a/plugins/webapi-spammer/plugin.go b/plugins/webapi-spammer/plugin.go new file mode 100644 index 0000000000000000000000000000000000000000..b216aca5e0f9d3e905eb30b898b6066f4dd42c72 --- /dev/null +++ b/plugins/webapi-spammer/plugin.go @@ -0,0 +1,73 @@ +package webapi_spammer + +import ( + "net/http" + "time" + + "github.com/iotaledger/goshimmer/packages/node" + "github.com/iotaledger/goshimmer/packages/transactionspammer" + "github.com/iotaledger/goshimmer/plugins/webapi" + "github.com/labstack/echo" +) + +var PLUGIN = node.NewPlugin("Spammer", configure) + +func configure(plugin *node.Plugin) { + webapi.AddEndpoint("spammer", WebApiHandler) +} + +func WebApiHandler(c echo.Context) error { + c.Set("requestStartTime", time.Now()) + + var request webRequest + if err := c.Bind(&request); err != nil { + return requestFailed(c, err.Error()) + } + + switch request.Cmd { + case "start": + if request.Tps == 0 { + request.Tps = 1000 + } + + transactionspammer.Stop() + transactionspammer.Start(request.Tps) + + return requestSuccessful(c, "started spamming transactions") + + case "stop": + transactionspammer.Stop() + + return requestSuccessful(c, "stopped spamming transactions") + + default: + return requestFailed(c, "invalid cmd in request") + } +} + +func requestSuccessful(c echo.Context, message string) error { + return c.JSON(http.StatusOK, webResponse{ + Duration: time.Since(c.Get("requestStartTime").(time.Time)).Nanoseconds() / 1e6, + Status: "success", + Message: message, + }) +} + +func requestFailed(c echo.Context, message string) error { + return c.JSON(http.StatusOK, webResponse{ + Duration: time.Since(c.Get("requestStartTime").(time.Time)).Nanoseconds() / 1e6, + Status: "failed", + Message: message, + }) +} + +type webResponse struct { + Duration int64 `json:"duration"` + Status string `json:"status"` + Message string `json:"message"` +} + +type webRequest struct { + Cmd string `json:"cmd"` + Tps int64 `json:"tps"` +} diff --git a/plugins/webapi/api.go b/plugins/webapi/api.go new file mode 100644 index 0000000000000000000000000000000000000000..5c50d71bc463fc56775777fe675a8f8ce1283fab --- /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 0000000000000000000000000000000000000000..c01bd84cca2f270568f5762d65fdf1d94d540d37 --- /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 0000000000000000000000000000000000000000..ee2010c4de024d622b2da51705db47604c581e4c --- /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 0000000000000000000000000000000000000000..2f5e375418c36e6690474c5d1490b63db0d6f465 --- /dev/null +++ b/plugins/webapi/routes.go @@ -0,0 +1,9 @@ +package webapi + +import ( + "github.com/labstack/echo" +) + +func setupRoutes(e *echo.Echo) { + +}