diff --git a/dapps/valuetransfers/packages/address/sort.go b/dapps/valuetransfers/packages/address/sort.go new file mode 100644 index 0000000000000000000000000000000000000000..933d74dd6df828c66c53b9cce05bcdf5044455cd --- /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 fd825d65e73b57855c813e5926ccea32b507e1af..25511b27d5d6888e4ac64258d62c7ef94e4bae89 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 0000000000000000000000000000000000000000..65c05160bbd45ff5ac55abdd1204107b9a0c0244 --- /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) +}