diff --git a/packages/transactionspammer/transactionspammer.go b/packages/transactionspammer/transactionspammer.go index 67ba1fa87f950b6589a2186d47e0c104fc182ad1..00493158b87578f095deb48939f49321475ded94 100644 --- a/packages/transactionspammer/transactionspammer.go +++ b/packages/transactionspammer/transactionspammer.go @@ -63,7 +63,7 @@ func Start(tps uint64) { tx.SetTail(true) tx.SetAddress(targetAddress) tx.SetBranchTransactionHash(tipselection.GetRandomTip()) - tx.SetTrunkTransactionHash(tipselection.GetRandomTip()) + tx.SetTrunkTransactionHash(tipselection.GetRandomTip(tx.GetBranchTransactionHash())) tx.SetTimestamp(uint(time.Now().Unix())) if err := tx.DoProofOfWork(meta_transaction.MIN_WEIGHT_MAGNITUDE); err != nil { log.Warn("PoW failed", err) diff --git a/plugins/tipselection/plugin.go b/plugins/tipselection/plugin.go index eb983d4869cef8c4e5bef38d5958670368dbf985..7587075e303638b84e24fe51494eeff6e257ac8f 100644 --- a/plugins/tipselection/plugin.go +++ b/plugins/tipselection/plugin.go @@ -5,17 +5,23 @@ import ( "github.com/iotaledger/goshimmer/plugins/tangle" "github.com/iotaledger/hive.go/events" "github.com/iotaledger/hive.go/node" + "github.com/iotaledger/iota.go/trinary" ) var PLUGIN = node.NewPlugin("Tipselection", node.Enabled, configure, run) -func configure(node *node.Plugin) { +func configure(*node.Plugin) { + tipSet = make(map[trinary.Hash]struct{}) + tangle.Events.TransactionSolid.Attach(events.NewClosure(func(transaction *value_transaction.ValueTransaction) { - tips.Delete(transaction.GetBranchTransactionHash()) - tips.Delete(transaction.GetTrunkTransactionHash()) - tips.Set(transaction.GetHash(), transaction.GetHash()) + mutex.Lock() + defer mutex.Unlock() + + delete(tipSet, transaction.GetBranchTransactionHash()) + delete(tipSet, transaction.GetTrunkTransactionHash()) + tipSet[transaction.GetHash()] = struct{}{} })) } -func run(run *node.Plugin) { +func run(*node.Plugin) { } diff --git a/plugins/tipselection/tipselection.go b/plugins/tipselection/tipselection.go index 34eb31e60ca7636e49ce4e6813565f828088cd9e..95cb4bc1b523637286be4eabb9692458bc07627f 100644 --- a/plugins/tipselection/tipselection.go +++ b/plugins/tipselection/tipselection.go @@ -1,23 +1,54 @@ package tipselection import ( - "github.com/iotaledger/goshimmer/packages/datastructure" + "math/rand" + "sync" + "github.com/iotaledger/goshimmer/packages/model/meta_transaction" "github.com/iotaledger/iota.go/trinary" ) -var tips = datastructure.NewRandomMap() +var ( + tipSet map[trinary.Hash]struct{} + mutex sync.RWMutex +) + +func GetRandomTip(excluding ...trinary.Hash) trinary.Trytes { + mutex.RLock() + defer mutex.RUnlock() + + numTips := len(tipSet) + if numTips == 0 { + return meta_transaction.BRANCH_NULL_HASH + } -func GetRandomTip() (result trinary.Trytes) { - if randomTipHash := tips.RandomEntry(); randomTipHash != nil { - result = randomTipHash.(trinary.Trytes) - } else { - result = meta_transaction.BRANCH_NULL_HASH + var ignore trinary.Hash + if len(excluding) > 0 { + ignore = excluding[0] + } + if _, contains := tipSet[ignore]; contains { + if numTips == 1 { + return ignore + } + numTips -= 1 } - return + i := rand.Intn(numTips) + for k := range tipSet { + if k == ignore { + continue + } + if i == 0 { + return k + } + i-- + } + panic("unreachable") } func GetTipsCount() int { - return tips.Size() + mutex.RLock() + defer mutex.RUnlock() + + return len(tipSet) } diff --git a/plugins/tipselection/tipselection_test.go b/plugins/tipselection/tipselection_test.go index cb8274bfed54cab78ce985ab7ecd001795b52f2e..cb18e47815cde237bcfdedd9dac431d4156af5fb 100644 --- a/plugins/tipselection/tipselection_test.go +++ b/plugins/tipselection/tipselection_test.go @@ -1,24 +1,81 @@ package tipselection import ( - "fmt" + "log" "testing" + "github.com/iotaledger/goshimmer/packages/model/meta_transaction" "github.com/iotaledger/goshimmer/packages/model/value_transaction" "github.com/iotaledger/goshimmer/plugins/tangle" + "github.com/iotaledger/hive.go/logger" + "github.com/spf13/viper" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) -func Test(t *testing.T) { +func init() { + if err := logger.InitGlobalLogger(viper.New()); err != nil { + log.Fatal(err) + } +} + +func TestEmptyTipSet(t *testing.T) { + configure(nil) + assert.Equal(t, 0, GetTipsCount()) + assert.Equal(t, meta_transaction.BRANCH_NULL_HASH, GetRandomTip()) +} + +func TestSingleTip(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()) + tx := value_transaction.New() + tx.SetValue(int64(1)) + tx.SetBranchTransactionHash(meta_transaction.BRANCH_NULL_HASH) + tx.SetTrunkTransactionHash(meta_transaction.BRANCH_NULL_HASH) - tangle.Events.TransactionSolid.Trigger(tx) + tangle.Events.TransactionSolid.Trigger(tx) - fmt.Println(GetTipsCount()) - } + assert.Equal(t, 1, GetTipsCount()) + + tip1 := GetRandomTip() + assert.NotNil(t, tip1) + tip2 := GetRandomTip(tip1) + assert.NotNil(t, tip2) + assert.Equal(t, tip1, tip2) +} + +func TestGetRandomTip(t *testing.T) { + configure(nil) + + tx := value_transaction.New() + tx.SetValue(int64(1)) + tx.SetBranchTransactionHash(meta_transaction.BRANCH_NULL_HASH) + tx.SetTrunkTransactionHash(meta_transaction.BRANCH_NULL_HASH) + + tangle.Events.TransactionSolid.Trigger(tx) + + tx = value_transaction.New() + tx.SetValue(int64(2)) + tx.SetBranchTransactionHash(meta_transaction.BRANCH_NULL_HASH) + tx.SetTrunkTransactionHash(meta_transaction.BRANCH_NULL_HASH) + + tangle.Events.TransactionSolid.Trigger(tx) + + assert.Equal(t, 2, GetTipsCount()) + + tip1 := GetRandomTip() + require.NotNil(t, tip1) + tip2 := GetRandomTip(tip1) + require.NotNil(t, tip2) + require.NotEqual(t, tip1, tip2) + + tx = value_transaction.New() + tx.SetValue(int64(3)) + tx.SetBranchTransactionHash(tip1) + tx.SetTrunkTransactionHash(tip2) + + tangle.Events.TransactionSolid.Trigger(tx) + + assert.Equal(t, 1, GetTipsCount()) + assert.Equal(t, tx.GetHash(), GetRandomTip()) } diff --git a/plugins/webapi/broadcastData/plugin.go b/plugins/webapi/broadcastData/plugin.go index 2e30888e10d830d62765a3576b0f0efcf992a3c4..180144daece29905658d1e667c5521ed0cb838c2 100644 --- a/plugins/webapi/broadcastData/plugin.go +++ b/plugins/webapi/broadcastData/plugin.go @@ -66,7 +66,7 @@ func broadcastData(c echo.Context) error { tx.SetSignatureMessageFragment(trytes) tx.SetValue(0) tx.SetBranchTransactionHash(tipselection.GetRandomTip()) - tx.SetTrunkTransactionHash(tipselection.GetRandomTip()) + tx.SetTrunkTransactionHash(tipselection.GetRandomTip(tx.GetBranchTransactionHash())) tx.SetTimestamp(uint(time.Now().Unix())) if err := tx.DoProofOfWork(meta_transaction.MIN_WEIGHT_MAGNITUDE); err != nil { log.Warnf("PoW failed: %s", err) diff --git a/plugins/webapi/gtta/plugin.go b/plugins/webapi/gtta/plugin.go index 61217f8e6bde9b1c90cd48e3e51d0d5c6497a6f3..54444bce56bcbb2a7df88dc54816433f5871e762 100644 --- a/plugins/webapi/gtta/plugin.go +++ b/plugins/webapi/gtta/plugin.go @@ -17,7 +17,7 @@ var PLUGIN = node.NewPlugin("WebAPI GTTA Endpoint", node.Disabled, func(plugin * func Handler(c echo.Context) error { branchTransactionHash := tipselection.GetRandomTip() - trunkTransactionHash := tipselection.GetRandomTip() + trunkTransactionHash := tipselection.GetRandomTip(branchTransactionHash) return c.JSON(http.StatusOK, Response{ BranchTransaction: branchTransactionHash,