From be5848e6d24e060b4786bc8db2937a4844639fea Mon Sep 17 00:00:00 2001
From: lunfardo314 <evaldas.drasutis@iota.org>
Date: Wed, 20 May 2020 22:08:32 +0300
Subject: [PATCH] - fixed bug of non-deterministic NewOutputs function - added
 function to sort slice of addresses - minor edits

---
 dapps/valuetransfers/packages/address/sort.go | 25 +++++++++++
 .../packages/transaction/outputs.go           | 24 ++++++----
 .../packages/transaction/outputs_test.go      | 45 +++++++++++++++++++
 3 files changed, 85 insertions(+), 9 deletions(-)
 create mode 100644 dapps/valuetransfers/packages/address/sort.go
 create mode 100644 dapps/valuetransfers/packages/transaction/outputs_test.go

diff --git a/dapps/valuetransfers/packages/address/sort.go b/dapps/valuetransfers/packages/address/sort.go
new file mode 100644
index 00000000..933d74dd
--- /dev/null
+++ b/dapps/valuetransfers/packages/address/sort.go
@@ -0,0 +1,25 @@
+package address
+
+import (
+	"bytes"
+	"sort"
+)
+
+type sortedAddresses []Address
+
+func (s sortedAddresses) Len() int {
+	return len(s)
+}
+
+func (s sortedAddresses) Less(i, j int) bool {
+	return bytes.Compare(s[i][:], s[j][:]) < 0
+}
+
+func (s sortedAddresses) Swap(i, j int) {
+	s[i], s[j] = s[j], s[i]
+}
+
+// Sort function sorts the slice of addresses
+func Sort(addresses []Address) {
+	sort.Sort(sortedAddresses(addresses))
+}
diff --git a/dapps/valuetransfers/packages/transaction/outputs.go b/dapps/valuetransfers/packages/transaction/outputs.go
index fd825d65..25511b27 100644
--- a/dapps/valuetransfers/packages/transaction/outputs.go
+++ b/dapps/valuetransfers/packages/transaction/outputs.go
@@ -16,11 +16,17 @@ type Outputs struct {
 
 // NewOutputs is the constructor of the Outputs struct and creates the list of Outputs from the given details.
 func NewOutputs(outputs map[address.Address][]*balance.Balance) (result *Outputs) {
-	result = &Outputs{orderedmap.New()}
-	for address, balances := range outputs {
-		result.Add(address, balances)
+	// sorting outputs first before adding to the ordered map to have a deterministic order
+	toSort := make([]address.Address, 0, len(outputs))
+	for a := range outputs {
+		toSort = append(toSort, a)
 	}
+	address.Sort(toSort)
 
+	result = &Outputs{orderedmap.New()}
+	for _, addr := range toSort {
+		result.Add(addr, outputs[addr])
+	}
 	return
 }
 
@@ -49,7 +55,7 @@ func OutputsFromBytes(bytes []byte, optionalTargetObject ...*Outputs) (result *O
 	// iterate the corresponding times and collect addresses + their details
 	for i := uint32(0); i < addressCount; i++ {
 		// read address
-		address, addressErr := address.Parse(marshalUtil)
+		addr, addressErr := address.Parse(marshalUtil)
 		if addressErr != nil {
 			err = addressErr
 
@@ -78,7 +84,7 @@ func OutputsFromBytes(bytes []byte, optionalTargetObject ...*Outputs) (result *O
 		}
 
 		// add the gathered information as an output
-		result.Add(address, coloredBalances)
+		result.Add(addr, coloredBalances)
 	}
 
 	// return the number of bytes we processed
@@ -117,8 +123,8 @@ func (outputs *Outputs) Bytes() []byte {
 		marshalUtil.WriteBytes(address.Bytes())
 		marshalUtil.WriteUint32(uint32(len(balances)))
 
-		for _, balance := range balances {
-			marshalUtil.WriteBytes(balance.Bytes())
+		for _, bal := range balances {
+			marshalUtil.WriteBytes(bal.Bytes())
 		}
 
 		return true
@@ -141,10 +147,10 @@ func (outputs *Outputs) String() string {
 		result += "    " + address.String() + ": [\n"
 
 		balancesEmpty := true
-		for _, balance := range balances {
+		for _, bal := range balances {
 			balancesEmpty = false
 
-			result += "        " + balance.String() + ",\n"
+			result += "        " + bal.String() + ",\n"
 		}
 
 		if balancesEmpty {
diff --git a/dapps/valuetransfers/packages/transaction/outputs_test.go b/dapps/valuetransfers/packages/transaction/outputs_test.go
new file mode 100644
index 00000000..65c05160
--- /dev/null
+++ b/dapps/valuetransfers/packages/transaction/outputs_test.go
@@ -0,0 +1,45 @@
+package transaction
+
+import (
+	"bytes"
+	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/address"
+	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/balance"
+	"github.com/magiconair/properties/assert"
+	"golang.org/x/crypto/blake2b"
+	"testing"
+)
+
+func TestOutputs(t *testing.T) {
+	rndAddrs := make([]address.Address, 15)
+	for i := range rndAddrs {
+		rndAddrs[i] = address.RandomOfType(address.VersionED25519)
+	}
+
+	theMap1 := make(map[address.Address][]*balance.Balance)
+	for i := 0; i < len(rndAddrs); i++ {
+		theMap1[rndAddrs[i]] = []*balance.Balance{balance.New(balance.ColorIOTA, int64(i))}
+	}
+	out1 := NewOutputs(theMap1)
+
+	theMap2 := make(map[address.Address][]*balance.Balance)
+	for i := len(rndAddrs) - 1; i >= 0; i-- {
+		theMap2[rndAddrs[i]] = []*balance.Balance{balance.New(balance.ColorIOTA, int64(i))}
+	}
+	out2 := NewOutputs(theMap2)
+
+	//t.Logf("%s", out1.String())
+	//t.Logf("%s", out2.String())
+
+	h1 := hashOutputs(t, out1)
+	h2 := hashOutputs(t, out2)
+
+	assert.Equal(t, bytes.Equal(h1, h2), true)
+}
+
+func hashOutputs(t *testing.T, out *Outputs) []byte {
+	h, err := blake2b.New256(nil)
+	assert.Equal(t, err, nil)
+
+	h.Write(out.Bytes())
+	return h.Sum(nil)
+}
-- 
GitLab