From 74ff353924e9baa8f09e892481091433c42d9f06 Mon Sep 17 00:00:00 2001
From: Jono Wenger <jono@elixxir.io>
Date: Wed, 29 Dec 2021 10:29:04 -0800
Subject: [PATCH] Make sentRoundTracker that tracks rounds that file parts were
 recently sent on

---
 fileTransfer/sentRoundTracker.go      | 101 ++++++++++++++
 fileTransfer/sentRoundTracker_test.go | 184 ++++++++++++++++++++++++++
 2 files changed, 285 insertions(+)
 create mode 100644 fileTransfer/sentRoundTracker.go
 create mode 100644 fileTransfer/sentRoundTracker_test.go

diff --git a/fileTransfer/sentRoundTracker.go b/fileTransfer/sentRoundTracker.go
new file mode 100644
index 000000000..05ce4b1c3
--- /dev/null
+++ b/fileTransfer/sentRoundTracker.go
@@ -0,0 +1,101 @@
+////////////////////////////////////////////////////////////////////////////////
+// Copyright © 2020 xx network SEZC                                           //
+//                                                                            //
+// Use of this source code is governed by a license that can be found in the  //
+// LICENSE file                                                               //
+////////////////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////////////////////
+// Copyright © 2020 xx network SEZC                                           //
+//                                                                            //
+// Use of this source code is governed by a license that can be found in the  //
+// LICENSE file                                                               //
+////////////////////////////////////////////////////////////////////////////////
+
+package fileTransfer
+
+import (
+	jww "github.com/spf13/jwalterweatherman"
+	"gitlab.com/xx_network/primitives/id"
+	"gitlab.com/xx_network/primitives/netTime"
+	"sync"
+	"time"
+)
+
+// sentRoundTracker keeps track of rounds that file parts were sent on and when
+// those rounds occurred. Rounds past the given age can be deleted manually.
+type sentRoundTracker struct {
+	rounds map[id.Round]time.Time
+	age    time.Duration
+	mux    sync.RWMutex
+}
+
+// newSentRoundTracker returns an empty sentRoundTracker.
+func newSentRoundTracker(interval time.Duration) *sentRoundTracker {
+	return &sentRoundTracker{
+		rounds: make(map[id.Round]time.Time),
+		age:    interval,
+	}
+}
+
+// removeOldRounds removes any rounds that are older than the max round age.
+func (srt *sentRoundTracker) removeOldRounds() {
+	srt.mux.Lock()
+	defer srt.mux.Unlock()
+	deleteBefore := netTime.Now().Add(-srt.age)
+
+	for rid, timeStamp := range srt.rounds {
+		if timeStamp.Before(deleteBefore) {
+			delete(srt.rounds, rid)
+		}
+	}
+}
+
+// Has indicates if the round ID is in the tracker.
+func (srt *sentRoundTracker) Has(rid id.Round) bool {
+	srt.mux.RLock()
+	defer srt.mux.RUnlock()
+
+	_, exists := srt.rounds[rid]
+	return exists
+}
+
+// Insert adds the round to the tracker with the current time.
+func (srt *sentRoundTracker) Insert(rid id.Round) {
+	timeNow := netTime.Now()
+	srt.mux.Lock()
+	defer srt.mux.Unlock()
+
+	jww.DEBUG.Printf("[FT]\tInsert round %d into tracker at time %s\n", rid, timeNow.Format("03:04:05.9999999 PM"))
+
+	srt.rounds[rid] = timeNow
+}
+
+// Remove deletes a round ID from the tracker.
+func (srt *sentRoundTracker) Remove(rid id.Round) {
+	srt.mux.Lock()
+	defer srt.mux.Unlock()
+	delete(srt.rounds, rid)
+}
+
+// Len returns the number of round IDs in the tracker.
+func (srt *sentRoundTracker) Len() int {
+	srt.mux.RLock()
+	defer srt.mux.RUnlock()
+
+	return len(srt.rounds)
+}
+
+// GetRoundIDs returns a list of all round IDs in the tracker.
+func (srt *sentRoundTracker) GetRoundIDs() []id.Round {
+	srt.mux.RLock()
+	defer srt.mux.RUnlock()
+
+	roundIDs := make([]id.Round, 0, len(srt.rounds))
+
+	for rid := range srt.rounds {
+		roundIDs = append(roundIDs, rid)
+	}
+
+	return roundIDs
+}
diff --git a/fileTransfer/sentRoundTracker_test.go b/fileTransfer/sentRoundTracker_test.go
new file mode 100644
index 000000000..451ec1eff
--- /dev/null
+++ b/fileTransfer/sentRoundTracker_test.go
@@ -0,0 +1,184 @@
+////////////////////////////////////////////////////////////////////////////////
+// Copyright © 2020 xx network SEZC                                           //
+//                                                                            //
+// Use of this source code is governed by a license that can be found in the  //
+// LICENSE file                                                               //
+////////////////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////////////////////
+// Copyright © 2020 xx network SEZC                                           //
+//                                                                            //
+// Use of this source code is governed by a license that can be found in the  //
+// LICENSE file                                                               //
+////////////////////////////////////////////////////////////////////////////////
+
+package fileTransfer
+
+import (
+	"gitlab.com/xx_network/primitives/id"
+	"reflect"
+	"testing"
+	"time"
+)
+
+// Tests that newSentRoundTracker returns the expected new sentRoundTracker.
+func Test_newSentRoundTracker(t *testing.T) {
+	interval := 10 * time.Millisecond
+	expected := &sentRoundTracker{
+		rounds: make(map[id.Round]time.Time),
+		age:    interval,
+	}
+
+	srt := newSentRoundTracker(interval)
+
+	if !reflect.DeepEqual(expected, srt) {
+		t.Errorf("New sentRoundTracker does not match expected."+
+			"\nexpected: %+v\nreceived: %+v", expected, srt)
+	}
+}
+
+// Tests that sentRoundTracker.removeOldRounds removes only old rounds and not
+// newer rounds.
+func Test_sentRoundTracker_removeOldRounds(t *testing.T) {
+	srt := newSentRoundTracker(50 * time.Millisecond)
+
+	// Add odd round to tracker
+	for rid := id.Round(0); rid < 100; rid++ {
+		if rid%2 != 0 {
+			srt.Insert(rid)
+		}
+	}
+
+	time.Sleep(50 * time.Millisecond)
+
+	// Add even round to tracker
+	for rid := id.Round(0); rid < 100; rid++ {
+		if rid%2 == 0 {
+			srt.Insert(rid)
+		}
+	}
+
+	// Remove all old rounds (should be all odd rounds)
+	srt.removeOldRounds()
+
+	// Check that only even rounds exist
+	for rid := id.Round(0); rid < 100; rid++ {
+		if srt.Has(rid) {
+			if rid%2 != 0 {
+				t.Errorf("Round %d exists.", rid)
+			}
+		} else if rid%2 == 0 {
+			t.Errorf("Round %d does not exist.", rid)
+		}
+	}
+}
+
+// Tests that sentRoundTracker.Has returns true for all the even rounds and
+// false for all odd rounds.
+func Test_sentRoundTracker_Has(t *testing.T) {
+	srt := newSentRoundTracker(0)
+
+	// Insert even rounds into the tracker
+	for rid := id.Round(0); rid < 100; rid++ {
+		if rid%2 == 0 {
+			srt.Insert(rid)
+		}
+	}
+
+	// Check that only even rounds exist
+	for rid := id.Round(0); rid < 100; rid++ {
+		if srt.Has(rid) {
+			if rid%2 != 0 {
+				t.Errorf("Round %d exists.", rid)
+			}
+		} else if rid%2 == 0 {
+			t.Errorf("Round %d does not exist.", rid)
+		}
+	}
+}
+
+// Tests that sentRoundTracker.Insert adds all the expected rounds.
+func Test_sentRoundTracker_Insert(t *testing.T) {
+	srt := newSentRoundTracker(0)
+
+	// Insert even rounds into the tracker
+	for rid := id.Round(0); rid < 100; rid++ {
+		if rid%2 == 0 {
+			srt.Insert(rid)
+		}
+	}
+
+	// Check that only even rounds were added
+	for rid := id.Round(0); rid < 100; rid++ {
+		_, exists := srt.rounds[rid]
+		if exists {
+			if rid%2 != 0 {
+				t.Errorf("Round %d exists.", rid)
+			}
+		} else if rid%2 == 0 {
+			t.Errorf("Round %d does not exist.", rid)
+		}
+	}
+}
+
+// Tests that sentRoundTracker.Remove removes all even rounds.
+func Test_sentRoundTracker_Remove(t *testing.T) {
+	srt := newSentRoundTracker(0)
+
+	// Add all round to tracker
+	for rid := id.Round(0); rid < 100; rid++ {
+		srt.Insert(rid)
+	}
+
+	// Remove even rounds from the tracker
+	for rid := id.Round(0); rid < 100; rid++ {
+		if rid%2 == 0 {
+			srt.Remove(rid)
+		}
+	}
+
+	// Check that only even rounds were removed
+	for rid := id.Round(0); rid < 100; rid++ {
+		_, exists := srt.rounds[rid]
+		if exists {
+			if rid%2 == 0 {
+				t.Errorf("Round %d does not exist.", rid)
+			}
+		} else if rid%2 != 0 {
+			t.Errorf("Round %d exists.", rid)
+		}
+	}
+}
+
+// Tests that sentRoundTracker.Len returns the expected length when the tracker
+// is empty, filled, and then modified.
+func Test_sentRoundTracker_Len(t *testing.T) {
+	srt := newSentRoundTracker(0)
+
+	if srt.Len() != 0 {
+		t.Errorf("Length of tracker incorrect.\nexpected: %d\nreceived: %d",
+			0, srt.Len())
+	}
+
+	// Add all round to tracker
+	for rid := id.Round(0); rid < 100; rid++ {
+		srt.Insert(rid)
+	}
+
+	if srt.Len() != 100 {
+		t.Errorf("Length of tracker incorrect.\nexpected: %d\nreceived: %d",
+			100, srt.Len())
+	}
+
+	// Remove even rounds from the tracker
+	for rid := id.Round(0); rid < 100; rid++ {
+		if rid%2 == 0 {
+			srt.Remove(rid)
+		}
+	}
+
+	if srt.Len() != 50 {
+		t.Errorf("Length of tracker incorrect.\nexpected: %d\nreceived: %d",
+			50, srt.Len())
+	}
+}
-- 
GitLab