diff --git a/packages/ledger/booker.go b/packages/ledger/booker.go index 6aa4c962ff..97959a0983 100644 --- a/packages/ledger/booker.go +++ b/packages/ledger/booker.go @@ -67,11 +67,11 @@ func (b *booker) bookTransaction(txMetadata *TransactionMetadata, inputsMetadata func (b *booker) inheritBranchIDs(txID utxo.TransactionID, inputsMetadata OutputsMetadata) (inheritedBranchIDs branchdag.BranchIDs) { conflictingInputIDs, consumersToFork := b.determineConflictDetails(txID, inputsMetadata) if conflictingInputIDs.Size() == 0 { - return b.BranchDAG.RemoveConfirmedBranches(inputsMetadata.BranchIDs()) + return b.BranchDAG.FilterPendingBranches(inputsMetadata.BranchIDs()) } branchID := branchdag.NewBranchID(txID) - b.BranchDAG.CreateBranch(branchID, b.BranchDAG.RemoveConfirmedBranches(inputsMetadata.BranchIDs()), branchdag.NewConflictIDs(lo.Map(conflictingInputIDs.Slice(), branchdag.NewConflictID)...)) + b.BranchDAG.CreateBranch(branchID, b.BranchDAG.FilterPendingBranches(inputsMetadata.BranchIDs()), branchdag.NewConflictIDs(lo.Map(conflictingInputIDs.Slice(), branchdag.NewConflictID)...)) _ = consumersToFork.ForEach(func(transactionID utxo.TransactionID) (err error) { b.utils.WithTransactionAndMetadata(transactionID, func(tx *Transaction, txMetadata *TransactionMetadata) { @@ -123,7 +123,9 @@ func (b *booker) forkTransaction(tx *Transaction, txMetadata *TransactionMetadat previousParentBranches := txMetadata.BranchIDs() forkedBranchID := branchdag.NewBranchID(txMetadata.ID()) - if !b.BranchDAG.CreateBranch(forkedBranchID, previousParentBranches, branchdag.NewConflictIDs(lo.Map(conflictingInputs.Slice(), branchdag.NewConflictID)...)) { + conflictIDs := branchdag.NewConflictIDs(lo.Map(conflictingInputs.Slice(), branchdag.NewConflictID)...) + if !b.BranchDAG.CreateBranch(forkedBranchID, previousParentBranches, conflictIDs) { + b.BranchDAG.AddBranchToConflicts(forkedBranchID, conflictIDs) b.mutex.Unlock(txMetadata.ID()) return } @@ -159,7 +161,7 @@ func (b *booker) propagateForkedBranchToFutureCone(outputIDs utxo.OutputIDs, for func (b *booker) updateBranchesAfterFork(txMetadata *TransactionMetadata, forkedBranchID branchdag.BranchID, previousParents branchdag.BranchIDs) bool { if txMetadata.IsConflicting() { - b.BranchDAG.UpdateParentsAfterFork(branchdag.NewBranchID(txMetadata.ID()), forkedBranchID, previousParents) + b.BranchDAG.UpdateBranchParents(branchdag.NewBranchID(txMetadata.ID()), forkedBranchID, previousParents) return false } @@ -170,7 +172,7 @@ func (b *booker) updateBranchesAfterFork(txMetadata *TransactionMetadata, forked newBranchIDs := txMetadata.BranchIDs().Clone() newBranchIDs.DeleteAll(previousParents) newBranchIDs.Add(forkedBranchID) - newBranches := b.BranchDAG.RemoveConfirmedBranches(newBranchIDs) + newBranches := b.BranchDAG.FilterPendingBranches(newBranchIDs) b.Storage.CachedOutputsMetadata(txMetadata.OutputIDs()).Consume(func(outputMetadata *OutputMetadata) { outputMetadata.SetBranchIDs(newBranches) diff --git a/packages/ledger/branchdag/branchdag.go b/packages/ledger/branchdag/branchdag.go index 3302e8375f..b3ec5de587 100644 --- a/packages/ledger/branchdag/branchdag.go +++ b/packages/ledger/branchdag/branchdag.go @@ -4,128 +4,125 @@ import ( "fmt" "sync" - "github.com/cockroachdb/errors" + "github.com/iotaledger/hive.go/byteutils" "github.com/iotaledger/hive.go/generics/walker" - "github.com/iotaledger/hive.go/kvstore" - "github.com/iotaledger/hive.go/kvstore/mapdb" - - "github.com/iotaledger/goshimmer/packages/database" ) -// region BranchDAG //////////////////////////////////////////////////////////////////////////////////////////////////// - -// BranchDAG represents the DAG of Branches which contains the business logic to manage the creation and maintenance of -// the Branches which represents containers for the different perceptions of the ledger state that exist in the tangle. +// BranchDAG is an entity that manages conflicting versions of a quadruple-entry-accounting ledger and their causal +// relationships. type BranchDAG struct { - Events *Events + // Events is a dictionary for BranchDAG related events. + Events *Events + + // Storage is a dictionary for storage related API endpoints. Storage *Storage - options *Options - shutdownOnce sync.Once + // Utils is a dictionary for utility methods that simplify the interaction with the BranchDAG. + Utils *Utils + + options *options inclusionStateMutex sync.RWMutex } -// NewBranchDAG returns a new BranchDAG instance that stores its state in the given KVStore. -func NewBranchDAG(options ...Option) (newBranchDAG *BranchDAG) { - newBranchDAG = &BranchDAG{ - Events: NewEvents(), - options: NewOptions(options...), +// New returns a new BranchDAG from the given options. +func New(options ...Option) (new *BranchDAG) { + new = &BranchDAG{ + Events: newEvents(), + options: newOptions(options...), } - newBranchDAG.Storage = NewStorage(newBranchDAG) + new.Storage = newStorage(new) + new.Utils = newUtils(new) return } -// CreateBranch retrieves the Branch that corresponds to the given details. It automatically creates and -// updates the Branch according to the new details if necessary. +// CreateBranch tries to create a Branch with the given details. It returns true if the Branch could be created or false +// if it already existed. It triggers a BranchCreated event if the branch was successfully created. func (b *BranchDAG) CreateBranch(branchID BranchID, parentBranchIDs BranchIDs, conflictIDs ConflictIDs) (created bool) { b.inclusionStateMutex.RLock() - - // create or load the branch - b.Storage.Branch(branchID, func() *Branch { - branch := NewBranch(branchID, parentBranchIDs, conflictIDs) - + b.Storage.CachedBranch(branchID, func() *Branch { created = true - - return branch + return NewBranch(branchID, parentBranchIDs, NewConflictIDs()) }).Consume(func(branch *Branch) { - // If the branch existed already we simply update its conflict members. - // - // An existing Branch can only become a new member of a conflict set if that conflict set was newly created in which - // case none of the members of that set can either be Confirmed or Rejected. This means that our InclusionState does - // not change, and we don't need to update and propagate it. if !created { - _ = conflictIDs.ForEach(func(conflictID ConflictID) (err error) { - if branch.AddConflict(conflictID) { - b.registerConflictMember(conflictID, branchID) - } - - return nil - }) return } - // store child references - _ = parentBranchIDs.ForEach(func(parentBranchID BranchID) (err error) { - if cachedChildBranch, stored := b.Storage.childBranchStorage.StoreIfAbsent(NewChildBranch(parentBranchID, branchID)); stored { - cachedChildBranch.Release() - } - return nil - }) - - // store ConflictMember references - _ = conflictIDs.ForEach(func(conflictID ConflictID) (err error) { - b.registerConflictMember(conflictID, branchID) - return nil - }) + b.addConflictMembers(branch, conflictIDs) + b.createChildBranchReferences(branchID, parentBranchIDs) if b.anyParentRejected(branch) || b.anyConflictMemberConfirmed(branch) { branch.setInclusionState(Rejected) } }) - b.inclusionStateMutex.RUnlock() if created { - b.Events.BranchCreated.Trigger(branchID) + b.Events.BranchCreated.Trigger(&BranchCreatedEvent{ + BranchID: branchID, + ParentBranchIDs: parentBranchIDs, + ConflictIDs: conflictIDs, + }) } return created } -// UpdateParentsAfterFork changes the parents of a Branch (also updating the references of the ChildBranches). -func (b *BranchDAG) UpdateParentsAfterFork(branchID, newParentBranchID BranchID, previousParents BranchIDs) { +// AddBranchToConflicts adds the given Branch to the named conflicts. +func (b *BranchDAG) AddBranchToConflicts(branchID BranchID, newConflictIDs ConflictIDs) (updated bool) { b.inclusionStateMutex.RLock() - defer b.inclusionStateMutex.RUnlock() + b.Storage.CachedBranch(branchID).Consume(func(branch *Branch) { + updated = b.addConflictMembers(branch, newConflictIDs) + }) + b.inclusionStateMutex.RUnlock() + + if updated { + b.Events.BranchConflictsUpdated.Trigger(&BranchConflictsUpdatedEvent{ + BranchID: branchID, + NewConflictIDs: newConflictIDs, + }) + } + + return updated +} - b.Storage.Branch(branchID).Consume(func(branch *Branch) { +// UpdateBranchParents changes the parents of a Branch (also updating the references of the CachedChildBranches). +func (b *BranchDAG) UpdateBranchParents(branchID, addedBranchID BranchID, removedBranchIDs BranchIDs) (updated bool) { + b.inclusionStateMutex.RLock() + b.Storage.CachedBranch(branchID).Consume(func(branch *Branch) { parentBranchIDs := branch.Parents() - if !parentBranchIDs.Add(newParentBranchID) { + if !parentBranchIDs.Add(addedBranchID) { return } - parentBranchIDs.DeleteAll(previousParents) - - if cachedChildBranch, stored := b.Storage.childBranchStorage.StoreIfAbsent(NewChildBranch(newParentBranchID, branchID)); stored { - cachedChildBranch.Release() - } + b.removeChildBranchReferences(parentBranchIDs.DeleteAll(removedBranchIDs), branchID) + b.createChildBranchReferences(branchID, NewBranchIDs(addedBranchID)) - if branch.SetParents(parentBranchIDs) { - b.Events.BranchParentsUpdated.Trigger(&BranchParentUpdate{branchID, parentBranchIDs}) - } + updated = branch.SetParents(parentBranchIDs) }) + b.inclusionStateMutex.RUnlock() + + if updated { + b.Events.BranchParentsUpdated.Trigger(&BranchParentsUpdatedEvent{ + BranchID: branchID, + AddedBranch: addedBranchID, + RemovedBranches: removedBranchIDs, + }) + } + + return updated } -// RemoveConfirmedBranches returns the BranchIDs of the pending and rejected Branches that are +// FilterPendingBranches returns the BranchIDs of the pending and rejected Branches that are // addressed by the given BranchIDs. -func (b *BranchDAG) RemoveConfirmedBranches(branchIDs BranchIDs) (pendingBranchIDs BranchIDs) { +func (b *BranchDAG) FilterPendingBranches(branchIDs BranchIDs) (pendingBranchIDs BranchIDs) { pendingBranchIDs = NewBranchIDs() branchWalker := walker.New[BranchID]().PushAll(branchIDs.Slice()...) for branchWalker.HasNext() { currentBranchID := branchWalker.Next() - b.Storage.Branch(currentBranchID).Consume(func(branch *Branch) { + b.Storage.CachedBranch(currentBranchID).Consume(func(branch *Branch) { if branch.InclusionState() == Confirmed { return } @@ -151,35 +148,32 @@ func (b *BranchDAG) SetBranchConfirmed(branchID BranchID) (modified bool) { for confirmationWalker.HasNext() { currentBranchID := confirmationWalker.Next() - b.Storage.Branch(currentBranchID).Consume(func(branch *Branch) { + b.Storage.CachedBranch(currentBranchID).Consume(func(branch *Branch) { if modified = branch.setInclusionState(Confirmed); !modified { return } - _ = branch.Parents().ForEach(func(branchID BranchID) (err error) { - confirmationWalker.Push(branchID) - return nil - }) + for it := branch.Parents().Iterator(); it.HasNext(); { + confirmationWalker.Push(it.Next()) + } - _ = branch.Conflicts().ForEach(func(conflictID ConflictID) (err error) { - b.Storage.ConflictMembers(conflictID).Consume(func(conflictMember *ConflictMember) { + for it := branch.Conflicts().Iterator(); it.HasNext(); { + b.Storage.CachedConflictMembers(it.Next()).Consume(func(conflictMember *ConflictMember) { if conflictMember.BranchID() != currentBranchID { rejectedWalker.Push(conflictMember.BranchID()) } }) - - return nil - }) + } }) } for rejectedWalker.HasNext() { - b.Storage.Branch(rejectedWalker.Next()).Consume(func(branch *Branch) { + b.Storage.CachedBranch(rejectedWalker.Next()).Consume(func(branch *Branch) { if modified = branch.setInclusionState(Rejected); !modified { return } - b.Storage.ChildBranches(branch.ID()).Consume(func(childBranch *ChildBranch) { + b.Storage.CachedChildBranches(branch.ID()).Consume(func(childBranch *ChildBranch) { rejectedWalker.Push(childBranch.ChildBranchID()) }) }) @@ -194,17 +188,14 @@ func (b *BranchDAG) InclusionState(branchIDs BranchIDs) (inclusionState Inclusio defer b.inclusionStateMutex.RUnlock() inclusionState = Confirmed - _ = branchIDs.ForEach(func(branchID BranchID) (err error) { - switch b.inclusionState(branchID) { + for it := branchIDs.Iterator(); it.HasNext(); { + switch b.inclusionState(it.Next()) { case Rejected: - inclusionState = Rejected - return errors.New("abort") + return Rejected case Pending: inclusionState = Pending } - - return nil - }) + } return inclusionState } @@ -213,57 +204,51 @@ func (b *BranchDAG) Shutdown() { b.Storage.Shutdown() } -// inclusionState returns the InclusionState of the given BranchID. -func (b *BranchDAG) inclusionState(branchID BranchID) (inclusionState InclusionState) { - if !b.Storage.Branch(branchID).Consume(func(branch *Branch) { - inclusionState = branch.InclusionState() - }) { - panic(fmt.Sprintf("failed to load %s", branchID)) +func (b *BranchDAG) addConflictMembers(branch *Branch, conflictIDs ConflictIDs) (added bool) { + for it := conflictIDs.Iterator(); it.HasNext(); { + conflictID := it.Next() + + if added = branch.AddConflict(conflictID); added { + b.registerConflictMember(conflictID, branch.ID()) + } } - return inclusionState + return added } -func (b *BranchDAG) anyParentRejected(conflictBranch *Branch) (parentRejected bool) { - _ = conflictBranch.Parents().ForEach(func(parentBranchID BranchID) (err error) { - b.Storage.Branch(parentBranchID).Consume(func(parentBranch *Branch) { - if parentRejected = parentBranch.InclusionState() == Rejected; parentRejected { - return - } - }) - - if parentRejected { - return errors.New("abort") +func (b *BranchDAG) createChildBranchReferences(branchID BranchID, parentBranchIDs BranchIDs) { + for it := parentBranchIDs.Iterator(); it.HasNext(); { + if cachedChildBranch, stored := b.Storage.childBranchStorage.StoreIfAbsent(NewChildBranch(it.Next(), branchID)); stored { + cachedChildBranch.Release() } - - return nil - }) - - return + } } -// anyConflictMemberConfirmed makes a Branch rejected if any of its conflicting Branches is -// Confirmed. -func (b *BranchDAG) anyConflictMemberConfirmed(branch *Branch) (conflictMemberConfirmed bool) { - _ = branch.Conflicts().ForEach(func(conflictID ConflictID) (err error) { - b.Storage.ConflictMembers(conflictID).Consume(func(conflictMember *ConflictMember) { - if conflictMemberConfirmed || conflictMember.BranchID() == branch.ID() { - return - } - - b.Storage.Branch(conflictMember.BranchID()).Consume(func(conflictingBranch *Branch) { - conflictMemberConfirmed = conflictingBranch.InclusionState() == Confirmed - }) - }) +func (b *BranchDAG) removeChildBranchReferences(parentBranches BranchIDs, childBranchID BranchID) { + for it := parentBranches.Iterator(); it.HasNext(); { + b.Storage.childBranchStorage.Delete(byteutils.ConcatBytes(it.Next().Bytes(), childBranchID.Bytes())) + } +} - if conflictMemberConfirmed { - return errors.New("abort") +// anyParentRejected checks if any of a Branches parents is rejected. +func (b *BranchDAG) anyParentRejected(branch *Branch) (rejected bool) { + for it := branch.Parents().Iterator(); it.HasNext(); { + if b.inclusionState(it.Next()) == Rejected { + return true } + } - return nil + return false +} + +// anyConflictMemberConfirmed checks if any of the conflicting Branches of a Branch is Confirmed. +func (b *BranchDAG) anyConflictMemberConfirmed(branch *Branch) (confirmed bool) { + b.Utils.forEachConflictingBranchID(branch, func(conflictingBranchID BranchID) bool { + confirmed = b.inclusionState(conflictingBranchID) == Confirmed + return !confirmed }) - return + return confirmed } // registerConflictMember is an internal utility function that creates the ConflictMember references of a Branch @@ -284,48 +269,13 @@ func (b *BranchDAG) registerConflictMember(conflictID ConflictID, branchID Branc }) } -// endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////// - -// region Options ////////////////////////////////////////////////////////////////////////////////////////////////////// - -// Options is a container for all configurable parameters of the BranchDAG. -type Options struct { - Store kvstore.KVStore - CacheTimeProvider *database.CacheTimeProvider -} - -func NewOptions(options ...Option) (new *Options) { - return (&Options{ - Store: mapdb.NewMapDB(), - CacheTimeProvider: database.NewCacheTimeProvider(0), - }).Apply(options...) -} - -func (o *Options) Apply(options ...Option) (self *Options) { - for _, option := range options { - option(o) - } - - return o -} - -// Option represents the return type of optional parameters that can be handed into the constructor of the Ledger -// to configure its behavior. -type Option func(*Options) - -// WithStore is an Option for the Ledger that allows to specify which storage layer is supposed to be used to persist -// data. -func WithStore(store kvstore.KVStore) Option { - return func(options *Options) { - options.Store = store +// inclusionState returns the InclusionState of the given BranchID. +func (b *BranchDAG) inclusionState(branchID BranchID) (inclusionState InclusionState) { + if !b.Storage.CachedBranch(branchID).Consume(func(branch *Branch) { + inclusionState = branch.InclusionState() + }) { + panic(fmt.Sprintf("failed to load %s", branchID)) } -} -// WithCacheTimeProvider is an Option for the Tangle that allows to override hard coded cache time. -func WithCacheTimeProvider(cacheTimeProvider *database.CacheTimeProvider) Option { - return func(options *Options) { - options.CacheTimeProvider = cacheTimeProvider - } + return inclusionState } - -// endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/packages/ledger/branchdag/branchdag_test.go b/packages/ledger/branchdag/branchdag_test.go index 5f8112b62c..cb92236640 100644 --- a/packages/ledger/branchdag/branchdag_test.go +++ b/packages/ledger/branchdag/branchdag_test.go @@ -9,7 +9,7 @@ import ( ) func TestBranchDAG_RetrieveBranch(t *testing.T) { - branchDAG := NewBranchDAG() + branchDAG := New() defer branchDAG.Shutdown() var ( @@ -30,7 +30,7 @@ func TestBranchDAG_RetrieveBranch(t *testing.T) { require.NoError(t, conflictID3.FromRandomness()) assert.True(t, branchDAG.CreateBranch(branchID2, NewBranchIDs(MasterBranchID), NewConflictIDs(conflictID0, conflictID1))) - cachedBranch2 := branchDAG.Storage.Branch(branchID2) + cachedBranch2 := branchDAG.Storage.CachedBranch(branchID2) defer cachedBranch2.Release() Branch2, exists := cachedBranch2.Unwrap() require.True(t, exists) @@ -38,7 +38,7 @@ func TestBranchDAG_RetrieveBranch(t *testing.T) { assert.True(t, NewConflictIDs(conflictID0, conflictID1).Equal(Branch2.Conflicts())) assert.True(t, branchDAG.CreateBranch(branchID3, NewBranchIDs(Branch2.ID()), NewConflictIDs(conflictID0, conflictID1, conflictID2))) - cachedBranch3 := branchDAG.Storage.Branch(branchID3) + cachedBranch3 := branchDAG.Storage.CachedBranch(branchID3) defer cachedBranch3.Release() Branch3, exists := cachedBranch3.Unwrap() require.True(t, exists) @@ -47,7 +47,8 @@ func TestBranchDAG_RetrieveBranch(t *testing.T) { assert.Equal(t, NewConflictIDs(conflictID0, conflictID1, conflictID2), Branch3.Conflicts()) assert.False(t, branchDAG.CreateBranch(branchID2, NewBranchIDs(MasterBranchID), NewConflictIDs(conflictID0, conflictID1, conflictID2))) - cachedBranch2 = branchDAG.Storage.Branch(branchID2) + assert.True(t, branchDAG.AddBranchToConflicts(branchID2, NewConflictIDs(conflictID0, conflictID1, conflictID2))) + cachedBranch2 = branchDAG.Storage.CachedBranch(branchID2) defer cachedBranch2.Release() Branch2, exists = cachedBranch2.Unwrap() require.True(t, exists) @@ -55,7 +56,7 @@ func TestBranchDAG_RetrieveBranch(t *testing.T) { assert.Equal(t, NewConflictIDs(conflictID0, conflictID1, conflictID2), Branch2.Conflicts()) assert.True(t, branchDAG.CreateBranch(branchID4, NewBranchIDs(Branch3.ID(), Branch3.ID()), NewConflictIDs(conflictID3))) - cachedBranch4 := branchDAG.Storage.Branch(branchID4) + cachedBranch4 := branchDAG.Storage.CachedBranch(branchID4) defer cachedBranch4.Release() Branch4, exists := cachedBranch4.Unwrap() require.True(t, exists) @@ -63,7 +64,7 @@ func TestBranchDAG_RetrieveBranch(t *testing.T) { } func TestBranchDAG_ConflictMembers(t *testing.T) { - branchDAG := NewBranchDAG() + branchDAG := New() defer branchDAG.Shutdown() var ( @@ -79,13 +80,13 @@ func TestBranchDAG_ConflictMembers(t *testing.T) { // create initial branches assert.True(t, branchDAG.CreateBranch(branchID2, NewBranchIDs(MasterBranchID), NewConflictIDs(conflictID0))) - cachedBranch2 := branchDAG.Storage.Branch(branchID2) + cachedBranch2 := branchDAG.Storage.CachedBranch(branchID2) defer cachedBranch2.Release() branch2, exists := cachedBranch2.Unwrap() assert.True(t, exists) assert.True(t, branchDAG.CreateBranch(branchID3, NewBranchIDs(MasterBranchID), NewConflictIDs(conflictID0))) - cachedBranch3 := branchDAG.Storage.Branch(branchID3) + cachedBranch3 := branchDAG.Storage.CachedBranch(branchID3) defer cachedBranch3.Release() branch3, exists := cachedBranch3.Unwrap() assert.True(t, exists) @@ -95,14 +96,14 @@ func TestBranchDAG_ConflictMembers(t *testing.T) { branch2.ID(): {}, branch3.ID(): {}, } actualConflictMembers := map[BranchID]struct{}{} - branchDAG.Storage.ConflictMembers(conflictID0).Consume(func(conflictMember *ConflictMember) { + branchDAG.Storage.CachedConflictMembers(conflictID0).Consume(func(conflictMember *ConflictMember) { actualConflictMembers[conflictMember.BranchID()] = struct{}{} }) assert.Equal(t, expectedConflictMembers, actualConflictMembers) // add branch 4 assert.True(t, branchDAG.CreateBranch(branchID4, NewBranchIDs(MasterBranchID), NewConflictIDs(conflictID0))) - cachedBranch4 := branchDAG.Storage.Branch(branchID4) + cachedBranch4 := branchDAG.Storage.CachedBranch(branchID4) defer cachedBranch4.Release() branch4, exists := cachedBranch4.Unwrap() assert.True(t, exists) @@ -112,14 +113,14 @@ func TestBranchDAG_ConflictMembers(t *testing.T) { branch2.ID(): {}, branch3.ID(): {}, branch4.ID(): {}, } actualConflictMembers = map[BranchID]struct{}{} - branchDAG.Storage.ConflictMembers(conflictID0).Consume(func(conflictMember *ConflictMember) { + branchDAG.Storage.CachedConflictMembers(conflictID0).Consume(func(conflictMember *ConflictMember) { actualConflictMembers[conflictMember.BranchID()] = struct{}{} }) assert.Equal(t, expectedConflictMembers, actualConflictMembers) } func TestBranchDAG_SetBranchConfirmed(t *testing.T) { - branchDAG := NewBranchDAG() + branchDAG := New() defer branchDAG.Shutdown() var ( @@ -244,7 +245,7 @@ func createBranch(t *testing.T, branchDAG *BranchDAG, branchAlias string, parent } assert.True(t, branchDAG.CreateBranch(randomBranchID, parents, conflictIDs)) - cachedBranch := branchDAG.Storage.Branch(randomBranchID) + cachedBranch := branchDAG.Storage.CachedBranch(randomBranchID) cachedBranch.Release() randomBranchID.RegisterAlias(branchAlias) diff --git a/packages/ledger/branchdag/events.go b/packages/ledger/branchdag/events.go index a903ce7dbe..a552ac7b7e 100644 --- a/packages/ledger/branchdag/events.go +++ b/packages/ledger/branchdag/events.go @@ -1,36 +1,64 @@ package branchdag import ( - "github.com/iotaledger/hive.go/events" + "github.com/iotaledger/hive.go/generics/event" ) // region Events /////////////////////////////////////////////////////////////////////////////////////////////////////// +// Events is a container that acts as a dictionary for the existing events of a BranchDAG. type Events struct { - // BranchCreated gets triggered when a new Branch is created. - BranchCreated *events.Event + // BranchCreated is an event that gets triggered whenever a new Branch is created. + BranchCreated *event.Event[*BranchCreatedEvent] - // BranchParentsUpdated gets triggered whenever a Branch's parents are updated. - BranchParentsUpdated *events.Event + // BranchConflictsUpdated is an event that gets triggered whenever the ConflictIDs of a Branch are updated. + BranchConflictsUpdated *event.Event[*BranchConflictsUpdatedEvent] + + // BranchParentsUpdated is an event that gets triggered whenever the parent BranchIDs of a Branch are updated. + BranchParentsUpdated *event.Event[*BranchParentsUpdatedEvent] } -func NewEvents() *Events { +// newEvents returns a new Events object. +func newEvents() *Events { return &Events{ - BranchCreated: events.NewEvent(func(handler interface{}, params ...interface{}) { - handler.(func(BranchID))(params[0].(BranchID)) - }), - BranchParentsUpdated: events.NewEvent(branchParentUpdateEventCaller), + BranchCreated: event.New[*BranchCreatedEvent](), + BranchConflictsUpdated: event.New[*BranchConflictsUpdatedEvent](), + BranchParentsUpdated: event.New[*BranchParentsUpdatedEvent](), } } -// BranchParentUpdate contains the new branch parents of a branch. -type BranchParentUpdate struct { - ID BranchID - NewParents BranchIDs +// endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// region BranchCreatedEvent /////////////////////////////////////////////////////////////////////////////////////////// + +// BranchCreatedEvent is a container that acts as a dictionary for the BranchCreated event related parameters. +type BranchCreatedEvent struct { + BranchID BranchID + ParentBranchIDs BranchIDs + ConflictIDs ConflictIDs +} + +// endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// region BranchConflictsUpdatedEvent ////////////////////////////////////////////////////////////////////////////////// + +// BranchConflictsUpdatedEvent is a container that acts as a dictionary for the BranchConflictsUpdated event related +// parameters. +type BranchConflictsUpdatedEvent struct { + BranchID BranchID + NewConflictIDs ConflictIDs } -func branchParentUpdateEventCaller(handler interface{}, params ...interface{}) { - handler.(func(branchParents *BranchParentUpdate))(params[0].(*BranchParentUpdate)) +// endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// region BranchParentsUpdatedEvent //////////////////////////////////////////////////////////////////////////////////// + +// BranchParentsUpdatedEvent is a container that acts as a dictionary for the BranchParentsUpdated event related +// parameters. +type BranchParentsUpdatedEvent struct { + BranchID BranchID + AddedBranch BranchID + RemovedBranches BranchIDs } // endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/packages/ledger/branchdag/options.go b/packages/ledger/branchdag/options.go new file mode 100644 index 0000000000..bada99deac --- /dev/null +++ b/packages/ledger/branchdag/options.go @@ -0,0 +1,68 @@ +package branchdag + +import ( + "github.com/iotaledger/hive.go/kvstore" + "github.com/iotaledger/hive.go/kvstore/mapdb" + + "github.com/iotaledger/goshimmer/packages/database" +) + +// region WithStore //////////////////////////////////////////////////////////////////////////////////////////////////// + +// WithStore is an Option for the BranchDAG that allows to configure which KVStore is supposed to be used to persist +// data (the default option is to use a MapDB). +func WithStore(store kvstore.KVStore) Option { + return func(options *options) { + options.store = store + } +} + +// endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// region WithCacheTimeProvider //////////////////////////////////////////////////////////////////////////////////////// + +// WithCacheTimeProvider is an Option for the BranchDAG that allows to configure which CacheTimeProvider is supposed to +// be used (the default option is to use a forced CacheTime of 0). +func WithCacheTimeProvider(cacheTimeProvider *database.CacheTimeProvider) Option { + return func(options *options) { + options.cacheTimeProvider = cacheTimeProvider + } +} + +// endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// region Option /////////////////////////////////////////////////////////////////////////////////////////////////////// + +// Option represents the return type of optional parameters that can be handed into the constructor of the BranchDAG to +// configure its behavior. +type Option func(*options) + +// endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// region options ////////////////////////////////////////////////////////////////////////////////////////////////////// + +// options is a container for all configurable parameters of a BranchDAG. +type options struct { + store kvstore.KVStore + cacheTimeProvider *database.CacheTimeProvider +} + +// newOptions returns a new options object that corresponds to the hand in options and is derived from the default +// options. +func newOptions(option ...Option) (new *options) { + return (&options{ + store: mapdb.NewMapDB(), + cacheTimeProvider: database.NewCacheTimeProvider(0), + }).apply(option...) +} + +// apply modifies the options object by overriding the given options. +func (o *options) apply(options ...Option) (self *options) { + for _, option := range options { + option(o) + } + + return o +} + +// endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/packages/ledger/branchdag/storage.go b/packages/ledger/branchdag/storage.go index c85463ebec..6b99973055 100644 --- a/packages/ledger/branchdag/storage.go +++ b/packages/ledger/branchdag/storage.go @@ -1,6 +1,7 @@ package branchdag import ( + "sync" "time" "github.com/cockroachdb/errors" @@ -13,42 +14,41 @@ import ( // region Storage ////////////////////////////////////////////////////////////////////////////////////////////////////// type Storage struct { - *BranchDAG - branchStorage *objectstorage.ObjectStorage[*Branch] childBranchStorage *objectstorage.ObjectStorage[*ChildBranch] conflictStorage *objectstorage.ObjectStorage[*Conflict] conflictMemberStorage *objectstorage.ObjectStorage[*ConflictMember] + branchDAG *BranchDAG + shutdownOnce sync.Once } -func NewStorage(branchDAG *BranchDAG) (new *Storage) { +func newStorage(branchDAG *BranchDAG) (new *Storage) { new = &Storage{ - BranchDAG: branchDAG, - branchStorage: objectstorage.New[*Branch]( - branchDAG.options.Store.WithRealm([]byte{database.PrefixLedger, PrefixBranchStorage}), - branchDAG.options.CacheTimeProvider.CacheTime(branchCacheTime), + branchDAG.options.store.WithRealm([]byte{database.PrefixLedger, PrefixBranchStorage}), + branchDAG.options.cacheTimeProvider.CacheTime(branchCacheTime), objectstorage.LeakDetectionEnabled(false), ), childBranchStorage: objectstorage.New[*ChildBranch]( - branchDAG.options.Store.WithRealm([]byte{database.PrefixLedger, PrefixChildBranchStorage}), + branchDAG.options.store.WithRealm([]byte{database.PrefixLedger, PrefixChildBranchStorage}), ChildBranchKeyPartition, - branchDAG.options.CacheTimeProvider.CacheTime(branchCacheTime), + branchDAG.options.cacheTimeProvider.CacheTime(branchCacheTime), objectstorage.LeakDetectionEnabled(false), objectstorage.StoreOnCreation(true), ), conflictStorage: objectstorage.New[*Conflict]( - branchDAG.options.Store.WithRealm([]byte{database.PrefixLedger, PrefixConflictStorage}), - branchDAG.options.CacheTimeProvider.CacheTime(consumerCacheTime), + branchDAG.options.store.WithRealm([]byte{database.PrefixLedger, PrefixConflictStorage}), + branchDAG.options.cacheTimeProvider.CacheTime(consumerCacheTime), objectstorage.LeakDetectionEnabled(false), ), conflictMemberStorage: objectstorage.New[*ConflictMember]( - branchDAG.options.Store.WithRealm([]byte{database.PrefixLedger, PrefixConflictMemberStorage}), + branchDAG.options.store.WithRealm([]byte{database.PrefixLedger, PrefixConflictMemberStorage}), ConflictMemberKeyPartition, - branchDAG.options.CacheTimeProvider.CacheTime(conflictCacheTime), + branchDAG.options.cacheTimeProvider.CacheTime(conflictCacheTime), objectstorage.LeakDetectionEnabled(false), objectstorage.StoreOnCreation(true), ), + branchDAG: branchDAG, } new.init() @@ -56,8 +56,8 @@ func NewStorage(branchDAG *BranchDAG) (new *Storage) { return new } -// Branch retrieves the Branch with the given BranchID from the object storage. -func (s *Storage) Branch(branchID BranchID, computeIfAbsentCallback ...func() *Branch) (cachedBranch *objectstorage.CachedObject[*Branch]) { +// CachedBranch retrieves the Branch with the given BranchID from the object storage. +func (s *Storage) CachedBranch(branchID BranchID, computeIfAbsentCallback ...func() *Branch) (cachedBranch *objectstorage.CachedObject[*Branch]) { if len(computeIfAbsentCallback) >= 1 { return s.branchStorage.ComputeIfAbsent(branchID.Bytes(), func(key []byte) *Branch { return computeIfAbsentCallback[0]() @@ -67,8 +67,8 @@ func (s *Storage) Branch(branchID BranchID, computeIfAbsentCallback ...func() *B return s.branchStorage.Load(branchID.Bytes()) } -// ChildBranches loads the references to the ChildBranches of the given Branch from the object storage. -func (s *Storage) ChildBranches(branchID BranchID) (cachedChildBranches objectstorage.CachedObjects[*ChildBranch]) { +// CachedChildBranches loads the references to the CachedChildBranches of the given Branch from the object storage. +func (s *Storage) CachedChildBranches(branchID BranchID) (cachedChildBranches objectstorage.CachedObjects[*ChildBranch]) { cachedChildBranches = make(objectstorage.CachedObjects[*ChildBranch], 0) s.childBranchStorage.ForEach(func(key []byte, cachedObject *objectstorage.CachedObject[*ChildBranch]) bool { cachedChildBranches = append(cachedChildBranches, cachedObject) @@ -79,24 +79,13 @@ func (s *Storage) ChildBranches(branchID BranchID) (cachedChildBranches objectst return } -// ForEachBranch iterates over all the branches and executes consumer. -func (s *Storage) ForEachBranch(consumer func(branch *Branch)) { - s.branchStorage.ForEach(func(key []byte, cachedObject *objectstorage.CachedObject[*Branch]) bool { - cachedObject.Consume(func(branch *Branch) { - consumer(branch) - }) - - return true - }) -} - -// Conflict loads a Conflict from the object storage. -func (s *Storage) Conflict(conflictID ConflictID) *objectstorage.CachedObject[*Conflict] { +// CachedConflict loads a Conflict from the object storage. +func (s *Storage) CachedConflict(conflictID ConflictID) *objectstorage.CachedObject[*Conflict] { return s.conflictStorage.Load(conflictID.Bytes()) } -// ConflictMembers loads the referenced ConflictMembers of a Conflict from the object storage. -func (s *Storage) ConflictMembers(conflictID ConflictID) (cachedConflictMembers objectstorage.CachedObjects[*ConflictMember]) { +// CachedConflictMembers loads the referenced ConflictMembers of a Conflict from the object storage. +func (s *Storage) CachedConflictMembers(conflictID ConflictID) (cachedConflictMembers objectstorage.CachedObjects[*ConflictMember]) { cachedConflictMembers = make(objectstorage.CachedObjects[*ConflictMember], 0) s.conflictMemberStorage.ForEach(func(key []byte, cachedObject *objectstorage.CachedObject[*ConflictMember]) bool { cachedConflictMembers = append(cachedConflictMembers, cachedObject) diff --git a/packages/ledger/branchdag/utils.go b/packages/ledger/branchdag/utils.go index 0756acaa84..6e578ecebb 100644 --- a/packages/ledger/branchdag/utils.go +++ b/packages/ledger/branchdag/utils.go @@ -1,47 +1,60 @@ package branchdag import ( - "github.com/cockroachdb/errors" + "github.com/iotaledger/hive.go/generics/objectstorage" "github.com/iotaledger/hive.go/generics/set" "github.com/iotaledger/hive.go/generics/walker" ) -type utils struct { +type Utils struct { branchDAG *BranchDAG } -func newUtil(branchDAG *BranchDAG) (new *utils) { - return &utils{ +func newUtils(branchDAG *BranchDAG) (new *Utils) { + return &Utils{ branchDAG: branchDAG, } } +// ForEachBranch iterates over all the branches and executes consumer. +func (u *Utils) ForEachBranch(consumer func(branch *Branch)) { + u.branchDAG.Storage.branchStorage.ForEach(func(key []byte, cachedObject *objectstorage.CachedObject[*Branch]) bool { + cachedObject.Consume(func(branch *Branch) { + consumer(branch) + }) + + return true + }) +} + // ForEachConflictingBranchID executes the callback for each Branch that is conflicting with the Branch // identified by the given BranchID. -func (u *utils) ForEachConflictingBranchID(branchID BranchID, callback func(conflictingBranchID BranchID) bool) { - abort := false - u.branchDAG.Storage.Branch(branchID).Consume(func(branch *Branch) { - _ = branch.Conflicts().ForEach(func(conflictID ConflictID) (err error) { - u.branchDAG.Storage.ConflictMembers(conflictID).Consume(func(conflictMember *ConflictMember) { - if abort || conflictMember.BranchID() == branchID { - return - } - - abort = !callback(conflictMember.BranchID()) - }) +func (u *Utils) ForEachConflictingBranchID(branchID BranchID, callback func(conflictingBranchID BranchID) bool) { + u.branchDAG.Storage.CachedBranch(branchID).Consume(func(branch *Branch) { + u.forEachConflictingBranchID(branch, callback) + }) +} - if abort { - return errors.New("abort") +// ForEachConflictingBranchID executes the callback for each Branch that is conflicting with the Branch +// identified by the given BranchID. +func (u *Utils) forEachConflictingBranchID(branch *Branch, callback func(conflictingBranchID BranchID) bool) { + for it := branch.Conflicts().Iterator(); it.HasNext(); { + abort := false + u.branchDAG.Storage.CachedConflictMembers(it.Next()).Consume(func(conflictMember *ConflictMember) { + if abort || conflictMember.BranchID() == branch.ID() { + return } - return nil + if abort = !callback(conflictMember.BranchID()); abort { + it.StopWalk() + } }) - }) + } } // ForEachConnectedConflictingBranchID executes the callback for each Branch that is connected through a chain // of intersecting ConflictSets. -func (u *utils) ForEachConnectedConflictingBranchID(branchID BranchID, callback func(conflictingBranchID BranchID)) { +func (u *Utils) ForEachConnectedConflictingBranchID(branchID BranchID, callback func(conflictingBranchID BranchID)) { traversedBranches := set.New[BranchID]() conflictSetsWalker := walker.New[ConflictID]() @@ -50,7 +63,7 @@ func (u *utils) ForEachConnectedConflictingBranchID(branchID BranchID, callback return } - u.branchDAG.Storage.Branch(branchID).Consume(func(branch *Branch) { + u.branchDAG.Storage.CachedBranch(branchID).Consume(func(branch *Branch) { _ = branch.Conflicts().ForEach(func(conflictID ConflictID) (err error) { conflictSetsWalker.Push(conflictID) return nil @@ -61,7 +74,7 @@ func (u *utils) ForEachConnectedConflictingBranchID(branchID BranchID, callback processBranchAndQueueConflictSets(branchID) for conflictSetsWalker.HasNext() { - u.branchDAG.Storage.ConflictMembers(conflictSetsWalker.Next()).Consume(func(conflictMember *ConflictMember) { + u.branchDAG.Storage.CachedConflictMembers(conflictSetsWalker.Next()).Consume(func(conflictMember *ConflictMember) { processBranchAndQueueConflictSets(conflictMember.BranchID()) }) } diff --git a/packages/ledger/dataflow.go b/packages/ledger/dataflow.go index b51ee938b8..72a768c498 100644 --- a/packages/ledger/dataflow.go +++ b/packages/ledger/dataflow.go @@ -8,6 +8,7 @@ import ( // region dataFlow ///////////////////////////////////////////////////////////////////////////////////////////////////// +// dataFlow is a component of the realities-ledger that connects the different commands into data flows that can be executed as chain-of-command pattern. type dataFlow struct { *Ledger } diff --git a/packages/ledger/events.go b/packages/ledger/events.go index 06311fd318..a7320705c8 100644 --- a/packages/ledger/events.go +++ b/packages/ledger/events.go @@ -15,7 +15,7 @@ type Events struct { Error *event.Event[error] } -func NewEvents() (new *Events) { +func newEvents() (new *Events) { return &Events{ TransactionStored: event.New[*TransactionStoredEvent](), TransactionBooked: event.New[*TransactionBookedEvent](), diff --git a/packages/ledger/ledger.go b/packages/ledger/ledger.go index e4574c21bc..bd72f44500 100644 --- a/packages/ledger/ledger.go +++ b/packages/ledger/ledger.go @@ -18,35 +18,31 @@ import ( // quadruple-entry accounting. It acts as a wrapper for the underlying components and exposes the public facing API. type Ledger struct { Events *Events - Options *Options BranchDAG *branchdag.BranchDAG Storage *storage - dataFlow *dataFlow validator *validator booker *booker + dataFlow *dataFlow utils *utils + options *options mutex *syncutils.DAGMutex[utxo.TransactionID] } func New(options ...Option) (ledger *Ledger) { ledger = &Ledger{ - Events: NewEvents(), - Options: NewOptions(options...), + Events: newEvents(), + options: newOptions(options...), mutex: syncutils.NewDAGMutex[utxo.TransactionID](), } + ledger.BranchDAG = branchdag.New(branchdag.WithStore(ledger.options.Store), branchdag.WithCacheTimeProvider(ledger.options.CacheTimeProvider)) ledger.Storage = newStorage(ledger) - ledger.dataFlow = newDataFlow(ledger) ledger.validator = newValidator(ledger) ledger.booker = newBooker(ledger) + ledger.dataFlow = newDataFlow(ledger) ledger.utils = newUtils(ledger) - ledger.BranchDAG = branchdag.NewBranchDAG( - branchdag.WithStore(ledger.Options.Store), - branchdag.WithCacheTimeProvider(ledger.Options.CacheTimeProvider), - ) - ledger.Events.TransactionBooked.Attach(event.NewClosure[*TransactionBookedEvent](func(event *TransactionBookedEvent) { ledger.processConsumingTransactions(event.Outputs.IDs()) })) @@ -83,24 +79,24 @@ func (l *Ledger) processConsumingTransactions(outputIDs utxo.OutputIDs) { // endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////// -// region Options ////////////////////////////////////////////////////////////////////////////////////////////////////// +// region options ////////////////////////////////////////////////////////////////////////////////////////////////////// -// Options is a container for all configurable parameters of the Ledger. -type Options struct { +// options is a container for all configurable parameters of the Ledger. +type options struct { Store kvstore.KVStore CacheTimeProvider *database.CacheTimeProvider VM vm.VM } -func NewOptions(options ...Option) (new *Options) { - return (&Options{ +func newOptions(option ...Option) (new *options) { + return (&options{ Store: mapdb.NewMapDB(), CacheTimeProvider: database.NewCacheTimeProvider(0), VM: NewMockedVM(), - }).Apply(options...) + }).Apply(option...) } -func (o *Options) Apply(options ...Option) (self *Options) { +func (o *options) Apply(options ...Option) (self *options) { for _, option := range options { option(o) } @@ -110,25 +106,25 @@ func (o *Options) Apply(options ...Option) (self *Options) { // Option represents the return type of optional parameters that can be handed into the constructor of the Ledger // to configure its behavior. -type Option func(*Options) +type Option func(*options) // WithStore is an Option for the Ledger that allows to specify which storage layer is supposed to be used to persist // data. func WithStore(store kvstore.KVStore) Option { - return func(options *Options) { + return func(options *options) { options.Store = store } } // WithCacheTimeProvider is an Option for the Tangle that allows to override hard coded cache time. func WithCacheTimeProvider(cacheTimeProvider *database.CacheTimeProvider) Option { - return func(options *Options) { + return func(options *options) { options.CacheTimeProvider = cacheTimeProvider } } func WithVM(vm vm.VM) Option { - return func(options *Options) { + return func(options *options) { options.VM = vm } } diff --git a/packages/ledger/storage.go b/packages/ledger/storage.go index 6ffbba3782..5e32855e9d 100644 --- a/packages/ledger/storage.go +++ b/packages/ledger/storage.go @@ -65,32 +65,32 @@ func outputFactory(vm vm.VM) func(key []byte, data []byte) (output objectstorage func newStorage(ledger *Ledger) (newStorage *storage) { return &storage{ transactionStorage: objectstorage.New[*Transaction]( - ledger.Options.Store.WithRealm([]byte{database.PrefixLedger, PrefixTransactionStorage}), - ledger.Options.CacheTimeProvider.CacheTime(transactionCacheTime), + ledger.options.Store.WithRealm([]byte{database.PrefixLedger, PrefixTransactionStorage}), + ledger.options.CacheTimeProvider.CacheTime(transactionCacheTime), objectstorage.LeakDetectionEnabled(false), objectstorage.StoreOnCreation(true), - objectstorage.WithObjectFactory(transactionFactory(ledger.Options.VM)), + objectstorage.WithObjectFactory(transactionFactory(ledger.options.VM)), ), transactionMetadataStorage: objectstorage.New[*TransactionMetadata]( - ledger.Options.Store.WithRealm([]byte{database.PrefixLedger, PrefixTransactionMetadataStorage}), - ledger.Options.CacheTimeProvider.CacheTime(transactionCacheTime), + ledger.options.Store.WithRealm([]byte{database.PrefixLedger, PrefixTransactionMetadataStorage}), + ledger.options.CacheTimeProvider.CacheTime(transactionCacheTime), objectstorage.LeakDetectionEnabled(false), ), outputStorage: objectstorage.New[*Output]( - ledger.Options.Store.WithRealm([]byte{database.PrefixLedger, PrefixOutputStorage}), - ledger.Options.CacheTimeProvider.CacheTime(outputCacheTime), + ledger.options.Store.WithRealm([]byte{database.PrefixLedger, PrefixOutputStorage}), + ledger.options.CacheTimeProvider.CacheTime(outputCacheTime), objectstorage.LeakDetectionEnabled(false), objectstorage.StoreOnCreation(true), - objectstorage.WithObjectFactory(outputFactory(ledger.Options.VM)), + objectstorage.WithObjectFactory(outputFactory(ledger.options.VM)), ), outputMetadataStorage: objectstorage.New[*OutputMetadata]( - ledger.Options.Store.WithRealm([]byte{database.PrefixLedger, PrefixOutputMetadataStorage}), - ledger.Options.CacheTimeProvider.CacheTime(outputCacheTime), + ledger.options.Store.WithRealm([]byte{database.PrefixLedger, PrefixOutputMetadataStorage}), + ledger.options.CacheTimeProvider.CacheTime(outputCacheTime), objectstorage.LeakDetectionEnabled(false), ), consumerStorage: objectstorage.New[*Consumer]( - ledger.Options.Store.WithRealm([]byte{database.PrefixLedger, PrefixConsumerStorage}), - ledger.Options.CacheTimeProvider.CacheTime(consumerCacheTime), + ledger.options.Store.WithRealm([]byte{database.PrefixLedger, PrefixConsumerStorage}), + ledger.options.CacheTimeProvider.CacheTime(consumerCacheTime), objectstorage.LeakDetectionEnabled(false), ConsumerPartitionKeys, ), diff --git a/packages/ledger/testframework.go b/packages/ledger/testframework.go index e4c8e1cdde..5734836bb0 100644 --- a/packages/ledger/testframework.go +++ b/packages/ledger/testframework.go @@ -89,7 +89,7 @@ func (t *TestFramework) AssertBranchDAG(testing *testing.T, expectedParents map[ expectedBranchIDs := t.BranchIDs(expectedParentAliases...) - assert.True(testing, t.Ledger.BranchDAG.Storage.Branch(currentBranchID).Consume(func(branch *branchdag.Branch) { + assert.True(testing, t.Ledger.BranchDAG.Storage.CachedBranch(currentBranchID).Consume(func(branch *branchdag.Branch) { assert.Truef(testing, expectedBranchIDs.Equal(branch.Parents()), "Branch(%s): expected parents %s are not equal to actual parents %s", currentBranchID, expectedBranchIDs, branch.Parents()) })) } diff --git a/packages/ledger/utils.go b/packages/ledger/utils.go index 08b85e8eaa..4c7b07f5d1 100644 --- a/packages/ledger/utils.go +++ b/packages/ledger/utils.go @@ -19,7 +19,7 @@ func newUtils(ledger *Ledger) (new *utils) { } func (u *utils) resolveInputs(inputs []utxo.Input) (outputIDs utxo.OutputIDs) { - return utxo.NewOutputIDs(lo.Map(inputs, u.Options.VM.ResolveInput)...) + return utxo.NewOutputIDs(lo.Map(inputs, u.options.VM.ResolveInput)...) } func (u *utils) UnprocessedConsumingTransactions(outputIDs utxo.OutputIDs) (consumingTransactions utxo.TransactionIDs) { diff --git a/packages/ledger/validator.go b/packages/ledger/validator.go index 4fd7c6cf1a..59136a9ed4 100644 --- a/packages/ledger/validator.go +++ b/packages/ledger/validator.go @@ -51,7 +51,7 @@ func (v *validator) checkOutputsCausallyRelatedCommand(params *dataFlowParams, n } func (v *validator) checkTransactionExecutionCommand(params *dataFlowParams, next dataflow.Next[*dataFlowParams]) (err error) { - utxoOutputs, err := v.Options.VM.ExecuteTransaction(params.Transaction.Transaction, params.Inputs.UTXOOutputs()) + utxoOutputs, err := v.options.VM.ExecuteTransaction(params.Transaction.Transaction, params.Inputs.UTXOOutputs()) if err != nil { return errors.Errorf("failed to execute transaction with %s: %w", params.Transaction.ID(), ErrTransactionInvalid) }