diff --git a/packages/ledgerstate/ledgerstate.go b/packages/ledgerstate/ledgerstate.go index 2270c86f1a03b9d641d1643ec0a9bdd2cfdbef45..fd824a19c900d9072eeec64b040e69974bcc4393 100644 --- a/packages/ledgerstate/ledgerstate.go +++ b/packages/ledgerstate/ledgerstate.go @@ -36,6 +36,7 @@ func NewLedgerState(storageId string) *LedgerState { mainReality := newReality(MAIN_REALITY_ID) mainReality.ledgerState = result + mainReality.SetPreferred() result.realities.Store(mainReality).Release() return result @@ -194,8 +195,7 @@ func (ledgerState *LedgerState) GenerateRealityVisualization(pngFilename string) realityNode.Attr("style", "filled") realityNode.Attr("shape", "rect") realityNode.Attr("color", "#9673A6") - realityNode.Attr("fillcolor", "#DAE8FC") - realityNode.Attr("penwidth", "2.0") + realityNode.Attr("fillcolor", "#E1D5E7") } else { realityNode = graph.Node("REALITY\n\n" + strings.Trim(reality.id.String(), "\x00") + " (" + strconv.Itoa(int(reality.GetTransferOutputCount())) + " / " + strconv.Itoa(len(reality.subRealityIds)) + ")") realityNode.Attr("style", "filled") @@ -204,7 +204,7 @@ func (ledgerState *LedgerState) GenerateRealityVisualization(pngFilename string) realityNode.Attr("fillcolor", "#DAE8FC") } - if reality.GetLiked() { + if reality.IsLiked() { realityNode.Attr("penwidth", "3.0") } @@ -313,12 +313,17 @@ func (ledgerState *LedgerState) AggregateRealities(realityIds ...RealityId) *obj parentConflictRealities := make(map[RealityId]*objectstorage.CachedObject) aggregatedRealityParentIds := make([]RealityId, len(aggregatedRealities)) + aggregatedRealityIsPreferred := true + counter := 0 for aggregatedRealityId, cachedAggregatedReality := range aggregatedRealities { aggregatedRealityParentIds[counter] = aggregatedRealityId counter++ aggregatedReality := cachedAggregatedReality.Get().(*Reality) + + aggregatedRealityIsPreferred = aggregatedRealityIsPreferred && aggregatedReality.IsPreferred() + if !aggregatedReality.IsAggregated() { parentConflictRealities[aggregatedRealityId] = cachedAggregatedReality } else { @@ -334,6 +339,7 @@ func (ledgerState *LedgerState) AggregateRealities(realityIds ...RealityId) *obj if newCachedAggregatedReality, err := ledgerState.realities.ComputeIfAbsent(aggregatedRealityId[:], func(key []byte) (object objectstorage.StorableObject, e error) { aggregatedReality := newReality(aggregatedRealityId, aggregatedRealityParentIds...) aggregatedReality.ledgerState = ledgerState + aggregatedReality.SetPreferred(aggregatedRealityIsPreferred) for _, parentRealityId := range aggregatedRealityParentIds { ledgerState.GetReality(parentRealityId).Consume(func(object objectstorage.StorableObject) { @@ -387,6 +393,7 @@ func (ledgerState *LedgerState) Prune() *LedgerState { mainReality := newReality(MAIN_REALITY_ID) mainReality.ledgerState = ledgerState + mainReality.SetPreferred() ledgerState.realities.Store(mainReality).Release() return ledgerState diff --git a/packages/ledgerstate/ledgerstate_test.go b/packages/ledgerstate/ledgerstate_test.go index 65b4e9fdc4eb0e69ae7878d1cb6f6f33acf807da..be5cdf81d3f91ae00ae2acb3b83a8d93b6e50f57 100644 --- a/packages/ledgerstate/ledgerstate_test.go +++ b/packages/ledgerstate/ledgerstate_test.go @@ -216,11 +216,11 @@ func multiSpend(ledgerState *LedgerState, outputCount int, transferOutputReferen func TestAggregateAggregatedRealities(t *testing.T) { ledgerState, transferOutputs := initializeLedgerStateWithBalances(3) - multiSpend(ledgerState, 1, transferOutputs[0]) outputs0 := multiSpend(ledgerState, 2, multiSpend(ledgerState, 1, transferOutputs[0])[0]) + multiSpend(ledgerState, 1, transferOutputs[0]) - multiSpend(ledgerState, 1, transferOutputs[1]) outputs1 := multiSpend(ledgerState, 2, multiSpend(ledgerState, 1, transferOutputs[1])[0]) + multiSpend(ledgerState, 1, transferOutputs[1]) multiSpend(ledgerState, 1, transferOutputs[2]) outputs2 := multiSpend(ledgerState, 2, multiSpend(ledgerState, 1, transferOutputs[2])[0]) @@ -236,8 +236,8 @@ func TestAggregateAggregatedRealities(t *testing.T) { objectstorage.WaitForWritesToFlush() - fmt.Println(ledgerState.GenerateRealityVisualization("realities1.png")) - NewVisualizer(ledgerState).RenderTransferOutputs("outputs1.png") + _ = ledgerState.GenerateRealityVisualization("realities1.png") + _ = NewVisualizer(ledgerState).RenderTransferOutputs("outputs1.png") multiSpend(ledgerState, 2, outputs0[0], outputs1[0]) @@ -245,8 +245,8 @@ func TestAggregateAggregatedRealities(t *testing.T) { objectstorage.WaitForWritesToFlush() - ledgerState.GenerateRealityVisualization("realities2.png") - NewVisualizer(ledgerState).RenderTransferOutputs("outputs2.png") + _ = ledgerState.GenerateRealityVisualization("realities2.png") + _ = NewVisualizer(ledgerState).RenderTransferOutputs("outputs2.png") } func TestElevateAggregatedReality(t *testing.T) { @@ -268,20 +268,20 @@ func TestElevateAggregatedReality(t *testing.T) { spend(ledgerState, doubleSpentOutputs1[1]) // double spend funds of aggregated reality - //spend(ledgerState, spentInput, doubleSpentOutputs2[0]) + // spend(ledgerState, spentInput, doubleSpentOutputs2[0]) // spend funds of conflict in aggregated reality further - //lastOutputOfAggregatedReality := spend(ledgerState, outputOfAggregatedReality) + // lastOutputOfAggregatedReality := spend(ledgerState, outputOfAggregatedReality) - //spend(ledgerState, lastOutputOfAggregatedReality, doubleSpentOutputs3[1]) + // spend(ledgerState, lastOutputOfAggregatedReality, doubleSpentOutputs3[1]) spend(ledgerState, spend(ledgerState, spend(ledgerState, outputOfAggregatedReality, spend(ledgerState, doubleSpentOutputs3[1])))) time.Sleep(1000 * time.Millisecond) objectstorage.WaitForWritesToFlush() - ledgerState.GenerateRealityVisualization("realities.png") - NewVisualizer(ledgerState).RenderTransferOutputs("outputs.png") + _ = ledgerState.GenerateRealityVisualization("realities.png") + _ = NewVisualizer(ledgerState).RenderTransferOutputs("outputs.png") } func TestElevate(t *testing.T) { diff --git a/packages/ledgerstate/outputs1.png b/packages/ledgerstate/outputs1.png index fbb292ef06d60e9e6ddf1bf269990516a389fcb7..dfd470d7176c3bbe1ba5a22a683236185ada77c0 100644 Binary files a/packages/ledgerstate/outputs1.png and b/packages/ledgerstate/outputs1.png differ diff --git a/packages/ledgerstate/outputs2.png b/packages/ledgerstate/outputs2.png index e179b2b414762ab36539dfaf7cc665670586d5bb..1091bb3948e28c5185f490646c85590d67c257aa 100644 Binary files a/packages/ledgerstate/outputs2.png and b/packages/ledgerstate/outputs2.png differ diff --git a/packages/ledgerstate/realities1.png b/packages/ledgerstate/realities1.png index 07550192e27c9ec302e2e8dd01d90c3883fd6141..632f38af62b17e2feb2609e6e40df9b14e7555ab 100644 Binary files a/packages/ledgerstate/realities1.png and b/packages/ledgerstate/realities1.png differ diff --git a/packages/ledgerstate/realities2.png b/packages/ledgerstate/realities2.png index f9c9fbd75bff8bbe0a2106a225f2f529ae0c7d0f..51d0e54970a4ad8e61be95e328557918dc07a4cc 100644 Binary files a/packages/ledgerstate/realities2.png and b/packages/ledgerstate/realities2.png differ diff --git a/packages/ledgerstate/reality.go b/packages/ledgerstate/reality.go index a1ff45dfcc2302f91cf8f85cbad5de8f03eff5e9..8f8e7501c99b1d5826e7c4fa8e5dedc26150404e 100644 --- a/packages/ledgerstate/reality.go +++ b/packages/ledgerstate/reality.go @@ -21,6 +21,8 @@ type Reality struct { conflictIds ConflictIdSet conflictIdsMutex sync.RWMutex transferOutputCount uint32 + preferred bool + preferredMutex sync.RWMutex liked bool likedMutex sync.RWMutex @@ -28,6 +30,135 @@ type Reality struct { ledgerState *LedgerState } +func (reality *Reality) areParentsLiked() (parentsLiked bool) { + parentsLiked = true + for _, cachedParentReality := range reality.GetParentRealities() { + if parentsLiked { + cachedParentReality.Consume(func(object objectstorage.StorableObject) { + parentsLiked = parentsLiked && object.(*Reality).IsLiked() + }) + } else { + cachedParentReality.Release() + } + } + + return +} + +func (reality *Reality) propagateLiked() { + reality.likedMutex.Lock() + reality.liked = true + reality.likedMutex.Unlock() + + reality.SetModified() + + for _, cachedSubReality := range reality.GetSubRealities() { + if !cachedSubReality.Exists() { + cachedSubReality.Release() + + // TODO: SWITCH TO ERR INSTEAD OF PANIC + panic("could not load sub reality") + } + + cachedSubReality.Consume(func(object objectstorage.StorableObject) { + subReality := object.(*Reality) + + subReality.parentRealityIdsMutex.RLock() + if len(subReality.parentRealityIds) == 1 && subReality.parentRealityIds.Contains(reality.id) { + subReality.parentRealityIdsMutex.RUnlock() + + subReality.propagateLiked() + } else { + subReality.parentRealityIdsMutex.RUnlock() + + if subReality.areParentsLiked() { + subReality.propagateLiked() + } + } + }) + } +} + +func (reality *Reality) propagateDisliked() { + reality.likedMutex.Lock() + reality.liked = false + reality.likedMutex.Unlock() + + reality.SetModified() + + for _, cachedSubReality := range reality.GetSubRealities() { + if !cachedSubReality.Exists() { + cachedSubReality.Release() + + // TODO: SWITCH TO ERR INSTEAD OF PANIC + panic("could not load sub reality") + } + + cachedSubReality.Consume(func(object objectstorage.StorableObject) { + subReality := object.(*Reality) + + if subReality.IsLiked() { + subReality.propagateDisliked() + } + }) + } +} + +func (reality *Reality) GetSubRealities() (subRealities objectstorage.CachedObjects) { + reality.subRealityIdsMutex.RLock() + subRealities = make(objectstorage.CachedObjects, len(reality.subRealityIds)) + i := 0 + for subRealityId := range reality.subRealityIds { + subRealities[i] = reality.ledgerState.GetReality(subRealityId) + + i++ + } + reality.subRealityIdsMutex.RUnlock() + + return +} + +func (reality *Reality) SetPreferred(preferred ...bool) (updated bool) { + newPreferredValue := len(preferred) == 0 || preferred[0] + + reality.preferredMutex.RLock() + if reality.preferred != newPreferredValue { + reality.preferredMutex.RUnlock() + + reality.preferredMutex.Lock() + if reality.preferred != newPreferredValue { + reality.preferred = newPreferredValue + + if newPreferredValue { + if reality.areParentsLiked() { + reality.propagateLiked() + } + } else { + if reality.IsLiked() { + reality.propagateDisliked() + } + } + + updated = true + + reality.SetModified() + } + reality.preferredMutex.Unlock() + } else { + reality.preferredMutex.RUnlock() + } + + return +} + +func (reality *Reality) IsPreferred() (preferred bool) { + reality.preferredMutex.RLock() + preferred = reality.preferred + reality.preferredMutex.RUnlock() + + return +} + // region DONE REVIEWING /////////////////////////////////////////////////////////////////////////////////////////////// // Creates a new Reality with the given id and parents. It is only used internally and therefore "private". @@ -45,7 +176,7 @@ func newReality(id RealityId, parentRealities ...RealityId) *Reality { return result } -func (reality *Reality) GetLiked() (liked bool) { +func (reality *Reality) IsLiked() (liked bool) { reality.likedMutex.RLock() liked = reality.liked reality.likedMutex.RUnlock() @@ -64,6 +195,8 @@ func (reality *Reality) SetLiked(liked ...bool) (likedStatusChanged bool) { if reality.liked != newLikedStatus { reality.liked = newLikedStatus + likedStatusChanged = true + reality.SetModified() } reality.likedMutex.Unlock() @@ -479,7 +612,7 @@ func (reality *Reality) createRealityForPreviouslyUnconflictingConsumers(consume if cachedElevatedReality, realityErr := reality.ledgerState.realities.ComputeIfAbsent(elevatedRealityId[:], func(key []byte) (object objectstorage.StorableObject, e error) { newReality := newReality(elevatedRealityId, reality.id) newReality.ledgerState = reality.ledgerState - newReality.SetLiked() + newReality.SetPreferred() reality.RegisterSubReality(elevatedRealityId) diff --git a/packages/ledgerstate/reality_id_set.go b/packages/ledgerstate/reality_id_set.go index e202a3064895de39e666cb399e49b1209f96153d..e5c6ce54f93e73e47277342557238018858e2db4 100644 --- a/packages/ledgerstate/reality_id_set.go +++ b/packages/ledgerstate/reality_id_set.go @@ -12,6 +12,12 @@ func NewRealityIdSet(realityIds ...RealityId) (realityIdSet RealityIdSet) { return } +func (realityIdSet RealityIdSet) Contains(realityId RealityId) bool { + _, exists := realityIdSet[realityId] + + return exists +} + func (realityIdSet RealityIdSet) Add(realityId RealityId) RealityIdSet { realityIdSet[realityId] = void