Skip to content

Commit

Permalink
Feat: started working on executor and booker
Browse files Browse the repository at this point in the history
  • Loading branch information
hmoog committed Mar 20, 2022
1 parent 174b0cc commit a3bded3
Show file tree
Hide file tree
Showing 9 changed files with 118 additions and 87 deletions.
11 changes: 11 additions & 0 deletions packages/refactored/generics/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,3 +66,14 @@ func Keys[K comparable, V any](in map[K]V) []K {

return result
}

// Values creates an array of the map values.
func Values[K comparable, V any](in map[K]V) []V {
result := make([]V, 0, len(in))

for _, v := range in {
result = append(result, v)
}

return result
}
74 changes: 74 additions & 0 deletions packages/refactored/ledger/booker.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package ledger

import (
"fmt"

"github.com/cockroachdb/errors"
"github.com/iotaledger/hive.go/generics/dataflow"
"github.com/iotaledger/hive.go/generics/walker"
"github.com/iotaledger/hive.go/types"

"github.com/iotaledger/goshimmer/packages/refactored/generics"
"github.com/iotaledger/goshimmer/packages/refactored/ledger/branchdag"
"github.com/iotaledger/goshimmer/packages/refactored/utxo"
)

type Booker struct {
*Ledger
}

func (b *Booker) bookTransactionCommand(params *params, next dataflow.Next[*params]) (err error) {
// check the double spends first because at this step we also create the consumers
conflictingInputIDs := generics.Keys(generics.FilterByValue(params.InputsMetadata, b.doubleSpendRegistered(params.Transaction.ID())))

inheritedBranchIDs, err := b.ResolvePendingBranchIDs(generics.Reduce(generics.Values(params.InputsMetadata), b.accumulateBranchIDs, branchdag.NewBranchIDs()))
if err != nil {
return errors.Errorf("failed to resolve pending branches: %w", err)
}

if len(conflictingInputIDs) != 0 {
b.WalkConsumingTransactionID(conflictingInputIDs, func(txID utxo.TransactionID, _ *walker.Walker[utxo.OutputID]) {
b.forkConsumer(txID, conflictingInputIDs)
return
})

newBranchID := branchdag.NewBranchID(params.Transaction.ID())
inheritedBranchIDs = branchdag.NewBranchIDs(newBranchID)
cachedBranch, _, err := b.CreateBranch(newBranchID, inheritedBranchIDs, conflictingInputIDs)
if err != nil {
panic(fmt.Errorf("failed to create Branch when booking Transaction with %s: %w", params.Transaction.ID(), err))
}
cachedBranch.Release()
}

b.bookTransaction(params.Transaction, params.TransactionMetadata, params.InputsMetadata, inheritedBranchIDs)

return next(params)
}

func (b *Booker) accumulateBranchIDs(accumulator branchdag.BranchIDs, inputMetadata *OutputMetadata) (result branchdag.BranchIDs) {
return accumulator.AddAll(inputMetadata.BranchIDs())
}

func (b *Booker) doubleSpendRegistered(txID utxo.TransactionID) func(*OutputMetadata) bool {
return func(outputMetadata *OutputMetadata) (conflicting bool) {
outputMetadata.RegisterConsumer(txID)

b.bookConsumers(inputsMetadata, transaction.ID(), types.True)

return false
}
}

// bookNonConflictingTransaction is an internal utility function that books the Transaction into the Branch that is
// determined by aggregating the Branches of the consumed Inputs.
func (u *Booker) bookTransaction(transaction utxo.Transaction, transactionMetadata *TransactionMetadata, inputsMetadata map[utxo.OutputID]*OutputMetadata, branchIDs branchdag.BranchIDs) (targetBranchIDs branchdag.BranchIDs) {
u.bookOutputs(transaction, branchIDs)

transactionMetadata.SetBranchIDs(branchIDs)
transactionMetadata.SetSolid(true)

return branchIDs
}

//
18 changes: 18 additions & 0 deletions packages/refactored/ledger/executor.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package ledger

import (
"github.com/cockroachdb/errors"
"github.com/iotaledger/hive.go/generics/dataflow"
)

type Executor struct {
*Ledger
}

func (e *Executor) executeTransactionCommand(params *params, next dataflow.Next[*params]) (err error) {
if params.Outputs, err = e.vm.ExecuteTransaction(params.Transaction, params.Inputs); err != nil {
return errors.Errorf("failed to execute transaction with %s: %w", params.Transaction.ID(), ErrTransactionInvalid)
}

return next(params)
}
8 changes: 6 additions & 2 deletions packages/refactored/ledger/ledger.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"github.com/iotaledger/hive.go/generics/event"
"github.com/iotaledger/hive.go/kvstore"

"github.com/iotaledger/goshimmer/packages/refactored/ledger/branchdag"
"github.com/iotaledger/goshimmer/packages/refactored/syncutils"
"github.com/iotaledger/goshimmer/packages/refactored/utxo"
)
Expand All @@ -21,7 +22,9 @@ type Ledger struct {
*Storage
*Solidifier
*Validator
*Executor
*Utils
*branchdag.BranchDAG

syncutils.DAGMutex[[32]byte]

Expand Down Expand Up @@ -85,8 +88,8 @@ func (l *Ledger) processTransaction(tx utxo.Transaction, txMeta *TransactionMeta
err := dataflow.New[*params](
l.checkSolidityCommand,
l.checkOutputsCausallyRelatedCommand,
l.executeTransactionCommand,
/*
l.ExecuteTransaction,
l.BookTransaction,
*/
l.notifyConsumersCommand,
Expand Down Expand Up @@ -125,6 +128,7 @@ func (l *Ledger) notifyConsumersCommand(params *params, next dataflow.Next[*para
type params struct {
Transaction utxo.Transaction
TransactionMetadata *TransactionMetadata
Inputs map[utxo.OutputID]utxo.Output
Inputs []utxo.Output
InputsMetadata map[utxo.OutputID]*OutputMetadata
Outputs []utxo.Output
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,15 @@ func NewSolidifier(ledger *Ledger) (newAvailabilityManager *Solidifier) {
}
}

func (s *Solidifier) checkSolidity(transaction utxo.Transaction, metadata *TransactionMetadata) (success bool, inputs map[utxo.OutputID]utxo.Output) {
func (s *Solidifier) checkSolidity(transaction utxo.Transaction, metadata *TransactionMetadata) (success bool, inputs []utxo.Output) {
if metadata.Solid() {
return false, nil
}

cachedInputs := objectstorage.CachedObjects[utxo.Output](generics.Map(generics.Map(transaction.Inputs(), s.vm.ResolveInput), s.CachedOutput))
defer cachedInputs.Release()

inputs = generics.KeyBy[utxo.OutputID, utxo.Output](cachedInputs.Unwrap(true), utxo.Output.ID)
inputs = cachedInputs.Unwrap(true)
if len(inputs) != len(cachedInputs) {
return false, nil
}
Expand Down
8 changes: 4 additions & 4 deletions packages/refactored/ledger/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ type Utils struct {
*Ledger
}

func (u *Utils) WalkConsumingTransactionID(callback func(consumingTxID utxo.TransactionID, walker *walker.Walker[utxo.OutputID]), entryPoints []utxo.OutputID) {
func (u *Utils) WalkConsumingTransactionID(entryPoints []utxo.OutputID, callback func(consumingTxID utxo.TransactionID, walker *walker.Walker[utxo.OutputID])) {
if len(entryPoints) == 0 {
return
}
Expand All @@ -29,10 +29,10 @@ func (u *Utils) WalkConsumingTransactionID(callback func(consumingTxID utxo.Tran
}
}

func (u *Utils) WalkConsumingTransactionMetadata(callback func(txMetadata *TransactionMetadata, walker *walker.Walker[utxo.OutputID]), entryPoints []utxo.OutputID) {
u.WalkConsumingTransactionID(func(consumingTxID utxo.TransactionID, walker *walker.Walker[utxo.OutputID]) {
func (u *Utils) WalkConsumingTransactionMetadata(entryPoints []utxo.OutputID, callback func(txMetadata *TransactionMetadata, walker *walker.Walker[utxo.OutputID])) {
u.WalkConsumingTransactionID(entryPoints, func(consumingTxID utxo.TransactionID, walker *walker.Walker[utxo.OutputID]) {
u.CachedTransactionMetadata(consumingTxID).Consume(func(txMetadata *TransactionMetadata) {
callback(txMetadata, walker)
})
}, entryPoints)
})
}
6 changes: 3 additions & 3 deletions packages/refactored/ledger/validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ type Validator struct {
}

func (v *Validator) checkOutputsCausallyRelatedCommand(params *params, next dataflow.Next[*params]) (err error) {
cachedOutputsMetadata := objectstorage.CachedObjects[*OutputMetadata](generics.Map(generics.Keys(params.Inputs), v.CachedOutputMetadata))
cachedOutputsMetadata := objectstorage.CachedObjects[*OutputMetadata](generics.Map(generics.Map(params.Inputs, utxo.Output.ID), v.CachedOutputMetadata))
defer cachedOutputsMetadata.Release()

params.InputsMetadata = generics.KeyBy[utxo.OutputID, *OutputMetadata](cachedOutputsMetadata.Unwrap(), (*OutputMetadata).ID)
Expand All @@ -33,7 +33,7 @@ func (v *Validator) outputsCausallyRelated(outputsMetadata map[utxo.OutputID]*Ou
return false
}

v.WalkConsumingTransactionMetadata(func(txMetadata *TransactionMetadata, walker *walker.Walker[utxo.OutputID]) {
v.WalkConsumingTransactionMetadata(spentOutputIDs, func(txMetadata *TransactionMetadata, walker *walker.Walker[utxo.OutputID]) {
for _, outputID := range txMetadata.OutputIDs() {
if _, related = outputsMetadata[outputID]; related {
walker.StopWalk()
Expand All @@ -42,7 +42,7 @@ func (v *Validator) outputsCausallyRelated(outputsMetadata map[utxo.OutputID]*Ou

walker.Push(outputID)
}
}, spentOutputIDs)
})

return related
}
76 changes: 0 additions & 76 deletions packages/refactored/old/utxo_dag.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,52 +100,6 @@ func (u *UTXODAG) CheckTransaction(transaction utxo.Transaction) (err error) {
return nil
}

// BookTransaction books a Transaction into the ledger state.
func (u *UTXODAG) BookTransaction(transaction *Transaction) (targetBranchIDs branchdag.BranchIDs, err error) {
// store TransactionMetadata
transactionMetadata := NewTransactionMetadata(transaction.ID())
transactionMetadata.SetSolid(true)
newTransaction := false
cachedTransactionMetadata := u.transactionMetadataStorage.ComputeIfAbsent(transaction.ID().Bytes(), func(key []byte) *TransactionMetadata {
newTransaction = true

transactionMetadata.Persist()
transactionMetadata.SetModified()

return transactionMetadata
})
if !newTransaction {
if !cachedTransactionMetadata.Consume(func(transactionMetadata *TransactionMetadata) {
targetBranchIDs = transactionMetadata.BranchIDs()
}) {
err = errors.Errorf("failed to load TransactionMetadata with %s: %w", transaction.ID(), cerrors.ErrFatal)
}
return
}
defer cachedTransactionMetadata.Release()

// store Transaction
u.transactionStorage.Store(transaction).Release()

// retrieve the metadata of the Inputs
cachedInputsMetadata := u.outputsMetadata(transaction)
defer cachedInputsMetadata.Release()
inputsMetadata := cachedInputsMetadata.Unwrap()

// determine the booking details before we book
parentBranchIDs, conflictingInputs, err := u.determineBookingDetails(inputsMetadata)
if err != nil {
err = errors.Errorf("failed to determine book details of Transaction with %s: %w", transaction.ID(), err)
return
}

if len(conflictingInputs) != 0 {
return u.bookConflictingTransaction(transaction, transactionMetadata, inputsMetadata, parentBranchIDs, conflictingInputs.ByID()), nil
}

return u.bookNonConflictingTransaction(transaction, transactionMetadata, inputsMetadata, parentBranchIDs), nil
}

// TransactionBranchIDs returns the BranchIDs of the given Transaction.
func (u *UTXODAG) TransactionBranchIDs(transactionID TransactionID) (branchIDs branchdag.BranchIDs, err error) {
if !u.CachedTransactionMetadata(transactionID).Consume(func(transactionMetadata *TransactionMetadata) {
Expand Down Expand Up @@ -275,17 +229,6 @@ func (u *UTXODAG) CachedAddressOutputMapping(address Address) (cachedAddressOutp

// region booking functions ////////////////////////////////////////////////////////////////////////////////////////////

// bookNonConflictingTransaction is an internal utility function that books the Transaction into the Branch that is
// determined by aggregating the Branches of the consumed Inputs.
func (u *UTXODAG) bookNonConflictingTransaction(transaction *Transaction, transactionMetadata *TransactionMetadata, inputsMetadata OutputsMetadata, branchIDs branchdag.BranchIDs) (targetBranchIDs branchdag.BranchIDs) {
transactionMetadata.SetBranchIDs(branchIDs)
transactionMetadata.SetSolid(true)
u.bookConsumers(inputsMetadata, transaction.ID(), types.True)
u.bookOutputs(transaction, branchIDs)

return branchIDs
}

// bookConflictingTransaction is an internal utility function that books a Transaction that uses Inputs that have
// already been spent by another Transaction. It creates a new Branch for the new Transaction and "forks" the
// existing consumers of the conflicting Inputs.
Expand Down Expand Up @@ -435,25 +378,6 @@ func (u *UTXODAG) bookOutputs(transaction *Transaction, targetBranchIDs branchda
}
}

// determineBookingDetails is an internal utility function that determines the information that are required to fully
// book a newly arrived Transaction into the UTXODAG using the metadata of its referenced Inputs.
func (u *UTXODAG) determineBookingDetails(inputsMetadata OutputsMetadata) (inheritedBranchIDs branchdag.BranchIDs, conflictingInputs OutputsMetadata, err error) {
conflictingInputs = inputsMetadata.SpentOutputsMetadata()
inheritedBranchIDs = branchdag.NewBranchIDs()
for _, inputMetadata := range inputsMetadata {
inheritedBranchIDs.AddAll(inputMetadata.BranchIDs())
}

inheritedBranchIDs, err = u.ledgerstate.ResolvePendingBranchIDs(inheritedBranchIDs)

if err != nil {
err = errors.Errorf("failed to resolve pending branches: %w", cerrors.ErrFatal)
return
}

return
}

// endregion ///////////////////////////////////////////////////////////////////////////////////////////////////////////

// region private utility functions ////////////////////////////////////////////////////////////////////////////////////
Expand Down

0 comments on commit a3bded3

Please sign in to comment.