From 9f42d25a2f5d2faee6089167e7cf726f351bcdc6 Mon Sep 17 00:00:00 2001
From: Luca Moser <moser.luca@gmail.com>
Date: Sun, 14 Jun 2020 14:25:36 +0200
Subject: [PATCH] check transaction availability in partition

---
 .../tester/framework/framework.go             |  4 +-
 .../consensus/consensus_conflicts_test.go     | 48 ++++++++++++++++---
 .../tester/tests/testutil.go                  | 32 +++++++++++--
 3 files changed, 70 insertions(+), 14 deletions(-)

diff --git a/tools/integration-tests/tester/framework/framework.go b/tools/integration-tests/tester/framework/framework.go
index 4c19cc4a..f246f21f 100644
--- a/tools/integration-tests/tester/framework/framework.go
+++ b/tools/integration-tests/tester/framework/framework.go
@@ -80,7 +80,7 @@ func (f *Framework) CreateNetwork(name string, peers int, minimumNeighbors int,
 	// create peers/GoShimmer nodes
 	for i := 0; i < peers; i++ {
 		config := GoShimmerConfig{
-			Bootstrap:                             true,
+			Bootstrap:                             i == 0,
 			BootstrapInitialIssuanceTimePeriodSec: bootstrapInitialIssuanceTimePeriodSec,
 			Faucet:                                i == 0,
 		}
@@ -128,7 +128,7 @@ func (f *Framework) CreateNetworkWithPartitions(name string, peers, partitions,
 
 	// create peers/GoShimmer nodes
 	for i := 0; i < peers; i++ {
-		config := GoShimmerConfig{Bootstrap: true}
+		config := GoShimmerConfig{Bootstrap: i == 0}
 		if _, err = network.CreatePeer(config); err != nil {
 			return nil, err
 		}
diff --git a/tools/integration-tests/tester/tests/consensus/consensus_conflicts_test.go b/tools/integration-tests/tester/tests/consensus/consensus_conflicts_test.go
index 1c22d2e5..842e4780 100644
--- a/tools/integration-tests/tester/tests/consensus/consensus_conflicts_test.go
+++ b/tools/integration-tests/tester/tests/consensus/consensus_conflicts_test.go
@@ -21,14 +21,19 @@ import (
 // TestConsensusConflicts issues valid conflicting value objects and makes sure that
 // the conflicts are resolved via FPC.
 func TestConsensusConflicts(t *testing.T) {
-	n, err := f.CreateNetworkWithPartitions("consensus_TestConsensusConflicts", 8, 2, 2)
+	n, err := f.CreateNetworkWithPartitions("consensus_TestConsensusConflicts", 8, 2, 4)
 	require.NoError(t, err)
 	defer tests.ShutdownNetwork(t, n)
 
 	time.Sleep(10 * time.Second)
 
 	// split the network
-	//assert.NoError(t, n.Split(n.Peers()[:len(n.Peers())/2], n.Peers()[len(n.Peers())/2:]))
+	for i, partition := range n.Partitions() {
+		log.Printf("partition %d peers:", i)
+		for _, p := range partition.Peers() {
+			log.Println(p.ID().String())
+		}
+	}
 
 	// genesis wallet
 	genesisSeedBytes, err := base58.Decode("7R1itJx5hVuo9w9hjg5cwKFmek4HMSoBDgJZN8hKGxih")
@@ -44,6 +49,8 @@ func TestConsensusConflicts(t *testing.T) {
 	conflictingTxIDs := make([]string, len(n.Partitions()))
 	receiverWallets := make([]*wallet.Wallet, len(n.Partitions()))
 	for i, partition := range n.Partitions() {
+
+		// create a new receiver wallet for the given partition
 		partitionReceiverWallet := wallet.New()
 		destAddr := partitionReceiverWallet.Seed().Address(0)
 		receiverWallets[i] = partitionReceiverWallet
@@ -56,9 +63,27 @@ func TestConsensusConflicts(t *testing.T) {
 			}))
 		tx = tx.Sign(signaturescheme.ED25519(*genesisWallet.Seed().KeyPair(0)))
 		conflictingTxs[i] = tx
-		conflictingTxIDs[i] = tx.ID().String()
-		log.Println("issuing conflict transaction on partition", i, tx.ID().String())
-		_, err := partition.Peers()[0].SendTransaction(tx.Bytes())
+
+		// issue the transaction on the first peer of the partition
+		issuerPeer := partition.Peers()[0]
+		txID, err := issuerPeer.SendTransaction(tx.Bytes())
+		conflictingTxIDs[i] = txID
+		log.Printf("issued conflict transaction %s on partition %d on peer %s", txID, i, issuerPeer.ID().String())
+		assert.NoError(t, err)
+
+		// check that the transaction is actually available on all the peers of the partition
+		missing, err := tests.AwaitTransactionAvailability(partition.Peers(), []string{txID}, 4*time.Second)
+		if err != nil {
+			assert.NoError(t, err, "transactions should have been available in partition")
+			for p, missingOnPeer := range missing {
+				log.Printf("missing on peer %s:", p)
+				for missingTx := range missingOnPeer {
+					log.Println("tx id: ", missingTx)
+				}
+			}
+			return
+		}
+
 		require.NoError(t, err)
 	}
 
@@ -78,8 +103,17 @@ func TestConsensusConflicts(t *testing.T) {
 	}
 
 	log.Println("waiting for transactions to be available on all peers...")
-	err = tests.AwaitTransactionAvailability(n.Peers(), conflictingTxIDs, 30*time.Second)
-	assert.NoError(t, err, "transactions should have been available")
+	missing, err := tests.AwaitTransactionAvailability(n.Peers(), conflictingTxIDs, 30*time.Second)
+	if err != nil {
+		assert.NoError(t, err, "transactions should have been available")
+		for p, missingOnPeer := range missing {
+			log.Printf("missing on peer %s:", p)
+			for missingTx := range missingOnPeer {
+				log.Println("tx id: ", missingTx)
+			}
+		}
+		return
+	}
 
 	expectations := map[string]*tests.ExpectedTransaction{}
 	for _, conflictingTx := range conflictingTxs {
diff --git a/tools/integration-tests/tester/tests/testutil.go b/tools/integration-tests/tester/tests/testutil.go
index 38cee7a2..65ff8628 100644
--- a/tools/integration-tests/tester/tests/testutil.go
+++ b/tools/integration-tests/tester/tests/testutil.go
@@ -16,6 +16,7 @@ import (
 	"github.com/iotaledger/goshimmer/packages/binary/messagelayer/payload"
 	"github.com/iotaledger/goshimmer/plugins/webapi/value/utils"
 	"github.com/iotaledger/goshimmer/tools/integration-tests/tester/framework"
+	"github.com/iotaledger/hive.go/types"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 )
@@ -456,10 +457,13 @@ func CheckTransactions(t *testing.T, peers []*framework.Peer, transactionIDs map
 }
 
 // AwaitTransactionAvailability awaits until the given transaction IDs become available on all given peers or
-// the max duration is reached.
-func AwaitTransactionAvailability(peers []*framework.Peer, transactionIDs []string, maxAwait time.Duration) error {
+// the max duration is reached. Returns a map of missing transactions per peer. An error is returned if at least
+// one peer does not have all specified transactions available.
+func AwaitTransactionAvailability(peers []*framework.Peer, transactionIDs []string, maxAwait time.Duration) (missing map[string]map[string]types.Empty, err error) {
 	s := time.Now()
-	for ; time.Since(s) < maxAwait; {
+	var missingMu sync.Mutex
+	missing = map[string]map[string]types.Empty{}
+	for ; time.Since(s) < maxAwait; time.Sleep(1 * time.Second) {
 		var wg sync.WaitGroup
 		wg.Add(len(peers))
 		counter := int32(len(peers) * len(transactionIDs))
@@ -469,18 +473,36 @@ func AwaitTransactionAvailability(peers []*framework.Peer, transactionIDs []stri
 				for _, txID := range transactionIDs {
 					_, err := p.GetTransactionByID(txID)
 					if err == nil {
+						missingMu.Lock()
+						m, has := missing[p.ID().String()]
+						if has {
+							delete(m, txID)
+							if len(m) == 0 {
+								delete(missing, p.ID().String())
+							}
+						}
+						missingMu.Unlock()
 						atomic.AddInt32(&counter, -1)
+						continue
 					}
+					missingMu.Lock()
+					m, has := missing[p.ID().String()]
+					if !has {
+						m = map[string]types.Empty{}
+					}
+					m[txID] = types.Empty{}
+					missing[p.ID().String()] = m
+					missingMu.Unlock()
 				}
 			}(p)
 		}
 		wg.Wait()
 		if counter == 0 {
 			// everything available
-			return nil
+			return missing, nil
 		}
 	}
-	return ErrTransactionNotAvailableInTime
+	return missing, ErrTransactionNotAvailableInTime
 }
 
 // ShutdownNetwork shuts down the network and reports errors.
-- 
GitLab