Skip to content

Commit

Permalink
Merge pull request #1082 from iotaledger/develop
Browse files Browse the repository at this point in the history
Merge v0.5.2 to master
  • Loading branch information
capossele authored Mar 17, 2021
2 parents 16ce42d + 1ef46e2 commit 2c5538c
Show file tree
Hide file tree
Showing 26 changed files with 357 additions and 44 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
# v0.5.2 - 2021-03-17
* Fix markers past cone check
* Add more information to explorer and message API
* Restrict Genesis attachment
* Fix parsing of GenesisNode public key
* Display mana histogram in log scale on dashboards
* **Breaking**: bumps network and database versions

# v0.5.1 - 2021-03-15
* Implement FCoB*
* Fix markers persistence bug
Expand Down
4 changes: 4 additions & 0 deletions packages/markers/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,10 @@ func (m *Manager) UpdateStructureDetails(structureDetailsToUpdate *StructureDeta

// IsInPastCone checks if the earlier node is directly or indirectly referenced by the later node in the DAG.
func (m *Manager) IsInPastCone(earlierStructureDetails *StructureDetails, laterStructureDetails *StructureDetails) (isInPastCone types.TriBool) {
if earlierStructureDetails.FutureMarkers.Size() == 0 && laterStructureDetails.FutureMarkers.Size() == 0 {
return types.Maybe
}

if earlierStructureDetails.Rank >= laterStructureDetails.Rank {
return types.False
}
Expand Down
23 changes: 23 additions & 0 deletions packages/markers/marker.go
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,29 @@ func (m *Markers) Clone() (clonedMarkers *Markers) {
return
}

// Equals is a comparator for two Markers.
func (m *Markers) Equals(other *Markers) (equals bool) {
m.markersMutex.RLock()
defer m.markersMutex.RUnlock()

if len(m.markers) != len(other.markers) {
return false
}

for sequenceID, index := range m.markers {
otherIndex, exists := other.markers[sequenceID]
if !exists {
return false
}

if otherIndex != index {
return false
}
}

return true
}

// Bytes returns the Markers in serialized byte form.
func (m *Markers) Bytes() (marshalMarkers []byte) {
m.markersMutex.RLock()
Expand Down
9 changes: 8 additions & 1 deletion packages/tangle/booker.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"
"sort"
"strconv"
"strings"
"sync"

"github.com/iotaledger/goshimmer/packages/ledgerstate"
Expand Down Expand Up @@ -96,7 +97,13 @@ func (b *Booker) Book(messageID MessageID) (err error) {

if !b.tangle.Utils.AllTransactionsApprovedByMessages(transaction.ReferencedTransactionIDs(), messageID) {
b.tangle.Events.MessageInvalid.Trigger(messageID)
err = fmt.Errorf("message does not reference all the transaction's dependencies")
refIds := transaction.ReferencedTransactionIDs()
var refIdsStrings strings.Builder
for key, _ := range refIds {
refIdsStrings.WriteString(key.Base58())
refIdsStrings.WriteString(", ")
}
err = fmt.Errorf("message %s does not reference all the transaction's dependencies: %s", messageID.String(), refIdsStrings.String())
return
}

Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
16 changes: 12 additions & 4 deletions packages/tangle/solidifier.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,20 +118,28 @@ func (s *Solidifier) isMessageMarkedAsSolid(messageID MessageID) (solid bool) {
func (s *Solidifier) areParentMessagesValid(message *Message) (valid bool) {
valid = true
message.ForEachParent(func(parent Parent) {
valid = valid && s.isParentMessageValid(parent.ID, message.IssuingTime())
valid = valid && s.isParentMessageValid(parent.ID, message)
})

return
}

// isParentMessageValid checks whether the given parent Message is valid.
func (s *Solidifier) isParentMessageValid(parentMessageID MessageID, childMessageIssuingTime time.Time) (valid bool) {
func (s *Solidifier) isParentMessageValid(parentMessageID MessageID, childMessage *Message) (valid bool) {
if parentMessageID == EmptyMessageID {
return true
if s.tangle.Options.GenesisNode != nil {
return *s.tangle.Options.GenesisNode == childMessage.IssuerPublicKey()
}

s.tangle.Storage.MessageMetadata(parentMessageID).Consume(func(messageMetadata *MessageMetadata) {
timeDifference := childMessage.IssuingTime().Sub(messageMetadata.SolidificationTime())
valid = timeDifference >= minParentsTimeDifference && timeDifference <= maxParentsTimeDifference
})
return
}

s.tangle.Storage.Message(parentMessageID).Consume(func(parentMessage *Message) {
timeDifference := childMessageIssuingTime.Sub(parentMessage.IssuingTime())
timeDifference := childMessage.IssuingTime().Sub(parentMessage.IssuingTime())

valid = timeDifference >= minParentsTimeDifference && timeDifference <= maxParentsTimeDifference
})
Expand Down
14 changes: 8 additions & 6 deletions packages/tangle/storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"strconv"
"time"

"github.com/iotaledger/goshimmer/packages/clock"
"github.com/iotaledger/goshimmer/packages/database"
"github.com/iotaledger/goshimmer/packages/ledgerstate"
"github.com/iotaledger/goshimmer/packages/markers"
Expand Down Expand Up @@ -255,25 +256,26 @@ func (s *Storage) MarkerIndexBranchIDMapping(sequenceID markers.SequenceID, comp
func (s *Storage) storeGenesis() {
s.MessageMetadata(EmptyMessageID, func() *MessageMetadata {
genesisMetadata := &MessageMetadata{
messageID: EmptyMessageID,
solid: true,
branchID: ledgerstate.MasterBranchID,
solidificationTime: clock.SyncedTime().Add(time.Duration(-20) * time.Minute),
messageID: EmptyMessageID,
solid: true,
branchID: ledgerstate.MasterBranchID,
structureDetails: &markers.StructureDetails{
Rank: 0,
IsPastMarker: false,
PastMarkers: markers.NewMarkers(),
FutureMarkers: markers.NewMarkers(),
},
booked: true,
eligible: true,
scheduled: true,
booked: true,
eligible: true,
}

genesisMetadata.Persist()
genesisMetadata.SetModified()

return genesisMetadata
}).Release()

}

// deleteStrongApprover deletes an Approver from the object storage that was created by a strong parent.
Expand Down
22 changes: 20 additions & 2 deletions packages/tangle/tangle.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ import (
"github.com/iotaledger/goshimmer/packages/markers"
"github.com/iotaledger/goshimmer/packages/tangle/payload"
"github.com/iotaledger/hive.go/autopeering/peer"
"github.com/iotaledger/hive.go/crypto/ed25519"
"github.com/iotaledger/hive.go/events"
"github.com/iotaledger/hive.go/identity"
"github.com/iotaledger/hive.go/kvstore"
"github.com/iotaledger/hive.go/kvstore/mapdb"
"github.com/mr-tron/base58"
"golang.org/x/xerrors"
)

Expand Down Expand Up @@ -191,6 +193,7 @@ type Options struct {
IncreaseMarkersIndexCallback markers.IncreaseIndexCallback
TangleWidth int
ConsensusMechanism ConsensusMechanism
GenesisNode *ed25519.PublicKey
}

// Store is an Option for the Tangle that allows to specify which storage layer is supposed to be used to persist data.
Expand Down Expand Up @@ -222,11 +225,26 @@ func IncreaseMarkersIndexCallback(callback markers.IncreaseIndexCallback) Option
}
}

// TangleWidth is an Option for the Tangle that allows to change the strategy how Tips get removed.
func TangleWidth(width int) Option {
// Width is an Option for the Tangle that allows to change the strategy how Tips get removed.
func Width(width int) Option {
return func(options *Options) {
options.TangleWidth = width
}
}

// GenesisNode is an Option for the Tangle that allows to set the GenesisNode, i.e., the node that is allowed to attach
// to the Genesis Message.
func GenesisNode(genesisNodeBase58 string) Option {
var genesisPublicKey *ed25519.PublicKey
pkBytes, _ := base58.Decode(genesisNodeBase58)
pk, _, err := ed25519.PublicKeyFromBytes(pkBytes)
if err == nil {
genesisPublicKey = &pk
}

return func(options *Options) {
options.GenesisNode = genesisPublicKey
}
}

// endregion ///////////////////////////////////////////////////////////////////////////////////////////////////////////
24 changes: 24 additions & 0 deletions packages/tangle/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,24 @@ func (u *Utils) TransactionApprovedByMessage(transactionID ledgerstate.Transacti
return true
}

var attachmentBooked bool
u.tangle.Storage.MessageMetadata(attachmentMessageID).Consume(func(attachmentMetadata *MessageMetadata) {
attachmentBooked = attachmentMetadata.IsBooked()
})
if !attachmentBooked {
continue
}

u.tangle.Storage.Message(messageID).Consume(func(message *Message) {
for _, parentID := range message.StrongParents() {
var parentBooked bool
u.tangle.Storage.MessageMetadata(parentID).Consume(func(parentMetadata *MessageMetadata) {
parentBooked = parentMetadata.IsBooked()
})
if !parentBooked {
continue
}

if u.MessageApprovedBy(attachmentMessageID, parentID) {
approved = true
return
Expand Down Expand Up @@ -145,6 +161,14 @@ func (u *Utils) MessageApprovedBy(approvedMessageID MessageID, approvingMessageI
continue
}

var weakApproverBooked bool
u.tangle.Storage.MessageMetadata(weakApprover.ApproverMessageID()).Consume(func(weakApproverMetadata *MessageMetadata) {
weakApproverBooked = weakApproverMetadata.IsBooked()
})
if !weakApproverBooked {
continue
}

if u.messageStronglyApprovedBy(weakApprover.ApproverMessageID(), approvingMessageID) {
return true
}
Expand Down
131 changes: 131 additions & 0 deletions packages/tangle/utils_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
package tangle

import (
"sync"
"testing"

"github.com/iotaledger/goshimmer/packages/ledgerstate"
"github.com/iotaledger/goshimmer/packages/markers"
"github.com/iotaledger/hive.go/events"
"github.com/stretchr/testify/assert"
)

func TestUtils_AllTransactionsApprovedByMessages(t *testing.T) {
// imgages/util-AllTransactionsApprovedByMessages-parallel-markers.png

tangle := New()
tangle.Setup()
defer tangle.Shutdown()

tangle.Events.Error.Attach(events.NewClosure(func(err error) {
panic(err)
}))

wallets := make(map[string]wallet)
walletsByAddress := make(map[ledgerstate.Address]wallet)
transactions := make(map[string]*ledgerstate.Transaction)
inputs := make(map[string]*ledgerstate.UTXOInput)
outputs := make(map[string]*ledgerstate.SigLockedSingleOutput)
outputsByID := make(map[ledgerstate.OutputID]ledgerstate.Output)
messages := make(map[string]*Message)

// Setup boilerplate
{
walletAliases := []string{"G1", "G2", "A", "B", "C", "D"}

for i, wallet := range createWallets(len(walletAliases)) {
wallets[walletAliases[i]] = wallet
}

for _, wallet := range wallets {
walletsByAddress[wallet.address] = wallet
}

g1Balance := ledgerstate.NewColoredBalances(map[ledgerstate.Color]uint64{
ledgerstate.ColorIOTA: 5,
})
g2Balance := ledgerstate.NewColoredBalances(map[ledgerstate.Color]uint64{
ledgerstate.ColorIOTA: 8,
})

tangle.LedgerState.LoadSnapshot(map[ledgerstate.TransactionID]map[ledgerstate.Address]*ledgerstate.ColoredBalances{
ledgerstate.GenesisTransactionID: {
wallets["G1"].address: g1Balance,
wallets["G2"].address: g2Balance,
},
})

// determine genesis index so that correct output can be referenced
var g1, g2 uint16
tangle.LedgerState.utxoDAG.Output(ledgerstate.NewOutputID(ledgerstate.GenesisTransactionID, 0)).Consume(func(output ledgerstate.Output) {
balance, _ := output.Balances().Get(ledgerstate.ColorIOTA)
if balance == uint64(5) {
g1 = 0
g2 = 1
} else {
g1 = 1
g2 = 0
}
})

inputs["G1"] = ledgerstate.NewUTXOInput(ledgerstate.NewOutputID(ledgerstate.GenesisTransactionID, g1))
inputs["G2"] = ledgerstate.NewUTXOInput(ledgerstate.NewOutputID(ledgerstate.GenesisTransactionID, g2))
}

messages["Message1"] = newTestParentsDataMessage("Message1", []MessageID{EmptyMessageID}, nil)

outputs["A"] = ledgerstate.NewSigLockedSingleOutput(5, wallets["A"].address)
transactions["TX1"] = makeTransaction(ledgerstate.NewInputs(inputs["G1"]), ledgerstate.NewOutputs(outputs["A"]), outputsByID, walletsByAddress, wallets["G1"])
messages["Message2"] = newTestParentsPayloadMessage(transactions["TX1"], []MessageID{messages["Message1"].ID()}, nil)

messages["Message3"] = newTestParentsDataMessage("Message3", []MessageID{messages["Message2"].ID()}, nil)

messages["Message4"] = newTestParentsDataMessage("Message4", []MessageID{EmptyMessageID}, nil)

outputs["B"] = ledgerstate.NewSigLockedSingleOutput(4, wallets["B"].address)
outputs["C"] = ledgerstate.NewSigLockedSingleOutput(4, wallets["C"].address)
transactions["TX2"] = makeTransaction(ledgerstate.NewInputs(inputs["G2"]), ledgerstate.NewOutputs(outputs["B"], outputs["C"]), outputsByID, walletsByAddress, wallets["G2"])
messages["Message5"] = newTestParentsPayloadMessage(transactions["TX2"], []MessageID{messages["Message4"].ID()}, nil)

messages["Message6"] = newTestParentsDataMessage("Message6", []MessageID{messages["Message5"].ID()}, nil)

inputs["A"] = ledgerstate.NewUTXOInput(ledgerstate.NewOutputID(transactions["TX1"].ID(), selectIndex(transactions["TX1"], wallets["A"])))
inputs["B"] = ledgerstate.NewUTXOInput(ledgerstate.NewOutputID(transactions["TX2"].ID(), selectIndex(transactions["TX2"], wallets["B"])))
inputs["C"] = ledgerstate.NewUTXOInput(ledgerstate.NewOutputID(transactions["TX2"].ID(), selectIndex(transactions["TX2"], wallets["C"])))
outputs["D"] = ledgerstate.NewSigLockedSingleOutput(13, wallets["D"].address)
outputsByID[inputs["A"].ReferencedOutputID()] = outputs["A"]
outputsByID[inputs["B"].ReferencedOutputID()] = outputs["B"]
outputsByID[inputs["C"].ReferencedOutputID()] = outputs["C"]
transactions["TX3"] = makeTransaction(ledgerstate.NewInputs(inputs["A"], inputs["B"], inputs["C"]), ledgerstate.NewOutputs(outputs["D"]), outputsByID, walletsByAddress)
messages["Message7"] = newTestParentsPayloadMessage(transactions["TX3"], []MessageID{messages["Message3"].ID(), messages["Message6"].ID()}, nil)

var waitGroup sync.WaitGroup
tangle.Booker.Events.MessageBooked.Attach(events.NewClosure(func(messageID MessageID) {
waitGroup.Done()
}))

waitGroup.Add(len(messages))
tangle.Storage.StoreMessage(messages["Message1"])
tangle.Storage.StoreMessage(messages["Message2"])
tangle.Storage.StoreMessage(messages["Message3"])
tangle.Storage.StoreMessage(messages["Message4"])
tangle.Storage.StoreMessage(messages["Message5"])
tangle.Storage.StoreMessage(messages["Message6"])
tangle.Storage.StoreMessage(messages["Message7"])

waitGroup.Wait()

for messageName, expectedMarkers := range map[string]*markers.Markers{
"Message1": markers.NewMarkers(markers.NewMarker(1, 1)),
"Message2": markers.NewMarkers(markers.NewMarker(1, 2)),
"Message3": markers.NewMarkers(markers.NewMarker(1, 3)),
"Message4": markers.NewMarkers(markers.NewMarker(0, 0)),
"Message5": markers.NewMarkers(markers.NewMarker(0, 0)),
"Message6": markers.NewMarkers(markers.NewMarker(0, 0)),
"Message7": markers.NewMarkers(markers.NewMarker(1, 4)),
} {
tangle.Storage.MessageMetadata(messages[messageName].ID()).Consume(func(messageMetadata *MessageMetadata) {
assert.True(t, messageMetadata.StructureDetails().PastMarkers.Equals(expectedMarkers))
})
}
}
2 changes: 1 addition & 1 deletion pkged.go

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,12 @@ export default class ManaHistogram extends React.Component<Props, any> {
chartType="Histogram"
loader={<div>Loading Chart</div>}
data={[
['NodeID', {type: 'number', label: 'Mana' }],
['NodeID', {type: 'number', label: 'log10(Mana)' }],
...this.props.data
]}
options={{
title: 'Mana Histogram',
hAxis: { title: 'Mana Holding of Nodes', titleTextStyle: { color: '#333' } },
hAxis: { title: 'log10(Mana) of Nodes ', titleTextStyle: { color: '#333' } },
vAxis: { title: 'Number of Nodes in Bucket', titleTextStyle: { color: '#333' } },
// By default, Google Charts will choose the bucket size automatically,
// using a well-known algorithm for histograms.
Expand All @@ -37,7 +37,7 @@ export default class ManaHistogram extends React.Component<Props, any> {
// percentage you specify. The values are still included in the histogram, but do
// not affect how they're bucketed. This is useful when you don't want outliers to
// land in their own buckets; they will be grouped with the first or last buckets instead.
histogram: { lastBucketPercentile: 5 },
//histogram: { lastBucketPercentile: 5 },
legend: {position: 'none'},
colors: ['#41aea9']
}}
Expand Down
Loading

0 comments on commit 2c5538c

Please sign in to comment.