Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: started implementing TransactionConfirmed event #2168

Merged
merged 21 commits into from
Apr 25, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion client/wallet/output.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (
// Output is a wallet specific representation of an output in the IOTA network.
type Output struct {
Address address.Address
Object devnetvm.OutputEssence
Object devnetvm.Output
Metadata OutputMetadata
GradeOfFinalityReached bool
// Spent is a local wallet-only property that gets set once an output is spent from within the same wallet.
Expand Down
4 changes: 2 additions & 2 deletions client/wallet/wallet.go
Original file line number Diff line number Diff line change
Expand Up @@ -2177,7 +2177,7 @@ func (wallet *Wallet) buildOutputs(
}
}
// construct result
var outputsSlice []devnetvm.OutputEssence
var outputsSlice []devnetvm.Output

// add output for remainder
if len(consumedFunds) != 0 {
Expand All @@ -2186,7 +2186,7 @@ func (wallet *Wallet) buildOutputs(

for addr, outputBalanceMap := range outputsByColor {
coloredBalances := devnetvm.NewColoredBalances(outputBalanceMap)
var output devnetvm.OutputEssence
var output devnetvm.Output
if !sendOptions.LockUntil.IsZero() || !sendOptions.FallbackDeadline.IsZero() || sendOptions.FallbackAddress != nil {
extended := devnetvm.NewExtendedLockedOutput(outputBalanceMap, addr.Address())
if !sendOptions.LockUntil.IsZero() {
Expand Down
2 changes: 1 addition & 1 deletion client/wallet/webconnector.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ func (webConnector WebConnector) GetUnspentAliasOutput(addr *devnetvm.AliasAddre
if o.Type != devnetvm.AliasOutputType.String() {
continue
}
var uncastedOutput devnetvm.OutputEssence
var uncastedOutput devnetvm.Output
uncastedOutput, err = o.ToLedgerstateOutput()
if err != nil {
return
Expand Down
147 changes: 20 additions & 127 deletions packages/consensus/finality/finality_gadget.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package finality

import (
"fmt"
"sync"

"github.com/iotaledger/hive.go/generics/set"
Expand All @@ -11,6 +10,7 @@ import (
"github.com/iotaledger/goshimmer/packages/ledger"
"github.com/iotaledger/goshimmer/packages/ledger/branchdag"
"github.com/iotaledger/goshimmer/packages/ledger/utxo"
"github.com/iotaledger/goshimmer/packages/ledger/vm/devnetvm"
"github.com/iotaledger/goshimmer/packages/markers"
"github.com/iotaledger/goshimmer/packages/tangle"
)
Expand Down Expand Up @@ -277,11 +277,15 @@ func (s *SimpleFinalityGadget) propagateGoFToMessagePastCone(messageID tangle.Me
}

s.tangle.Storage.MessageMetadata(strongParentMessageID).Consume(func(messageMetadata *tangle.MessageMetadata) {
if messageMetadata.GradeOfFinality() >= gradeOfFinality || !s.setMessageGoF(messageMetadata, gradeOfFinality) {
if messageMetadata.GradeOfFinality() >= gradeOfFinality {
return
}

s.tangle.Storage.Message(strongParentMessageID).Consume(func(message *tangle.Message) {
if !s.setMessageGoF(message, messageMetadata, gradeOfFinality) {
return
}

message.ForEachParent(func(parent tangle.Parent) {
if parent.Type == tangle.StrongParentType {
strongParentWalker.Push(parent.ID)
Expand All @@ -301,155 +305,44 @@ func (s *SimpleFinalityGadget) propagateGoFToMessagePastCone(messageID tangle.Me
if messageMetadata.GradeOfFinality() >= gradeOfFinality {
return
}
s.setMessageGoF(messageMetadata, gradeOfFinality)

s.tangle.Storage.Message(weakParent).Consume(func(message *tangle.Message) {
s.setMessageGoF(message, messageMetadata, gradeOfFinality)
})
})
})
}

// HandleBranch receives a branchID and its approval weight. It propagates the GoF according to AW to transactions
// in the branch (UTXO future cone) and their outputs.
func (s *SimpleFinalityGadget) HandleBranch(branchID branchdag.BranchID, aw float64) (err error) {
newGradeOfFinality := s.opts.BranchTransFunc(branchID, aw)

// update GoF of txs within the same branch
txGoFPropWalker := walker.New[utxo.TransactionID]()
s.tangle.Ledger.Storage.CachedTransactionMetadata(branchID.TransactionID()).Consume(func(transactionMetadata *ledger.TransactionMetadata) {
s.updateTransactionGoF(transactionMetadata, newGradeOfFinality, txGoFPropWalker)
})
for txGoFPropWalker.HasNext() {
s.forwardPropagateBranchGoFToTxs(txGoFPropWalker.Next(), branchID, newGradeOfFinality, txGoFPropWalker)
}

if newGradeOfFinality >= s.opts.BranchGoFReachedLevel {
s.events.BranchConfirmed.Trigger(&tangle.BranchConfirmedEvent{
BranchID: branchID,
})
if s.opts.BranchTransFunc(branchID, aw) >= s.opts.BranchGoFReachedLevel {
s.tangle.Ledger.BranchDAG.SetBranchConfirmed(branchID)
}

return err
return nil
}

func (s *SimpleFinalityGadget) forwardPropagateBranchGoFToTxs(candidateTxID utxo.TransactionID, candidateBranchID branchdag.BranchID, newGradeOfFinality gof.GradeOfFinality, txGoFPropWalker *walker.Walker[utxo.TransactionID]) bool {
return s.tangle.Ledger.Storage.CachedTransactionMetadata(candidateTxID).Consume(func(transactionMetadata *ledger.TransactionMetadata) {
// we stop if we walk outside our branch
if !transactionMetadata.BranchIDs().Has(candidateBranchID) {
return
}

var maxAttachmentGoF gof.GradeOfFinality
s.tangle.Storage.Attachments(transactionMetadata.ID()).Consume(func(attachment *tangle.Attachment) {
s.tangle.Storage.MessageMetadata(attachment.MessageID()).Consume(func(messageMetadata *tangle.MessageMetadata) {
if maxAttachmentGoF < messageMetadata.GradeOfFinality() {
maxAttachmentGoF = messageMetadata.GradeOfFinality()
}
})
})

// only adjust tx GoF if attachments have at least GoF derived from UTXO parents
if maxAttachmentGoF < newGradeOfFinality {
return
}

s.updateTransactionGoF(transactionMetadata, newGradeOfFinality, txGoFPropWalker)
})
}

func (s *SimpleFinalityGadget) updateTransactionGoF(transactionMetadata *ledger.TransactionMetadata, newGradeOfFinality gof.GradeOfFinality, txGoFPropWalker *walker.Walker[utxo.TransactionID]) {
// abort if the grade of finality did not change
if !transactionMetadata.SetGradeOfFinality(newGradeOfFinality) {
return
}

s.tangle.Ledger.Storage.CachedTransactionMetadata(transactionMetadata.ID()).Consume(func(txMetadata *ledger.TransactionMetadata) {
// we use a set of consumer txs as our candidate tx can consume multiple outputs from the same txs,
// but we want to add such tx only once to the walker
consumerTxs := utxo.NewTransactionIDs()

// adjust output GoF and add its consumer txs to the walker
for it := txMetadata.OutputIDs().Iterator(); it.HasNext(); {
s.adjustOutputGoF(it.Next(), newGradeOfFinality, consumerTxs, txGoFPropWalker)
}
})
if transactionMetadata.GradeOfFinality() >= s.opts.BranchGoFReachedLevel {
s.events.TransactionConfirmed.Trigger(&tangle.TransactionConfirmedEvent{
TransactionID: transactionMetadata.ID(),
})
}
}

func (s *SimpleFinalityGadget) adjustOutputGoF(outputID utxo.OutputID, newGradeOfFinality gof.GradeOfFinality, consumerTxs utxo.TransactionIDs, txGoFPropWalker *walker.Walker[utxo.TransactionID]) bool {
return s.tangle.Ledger.Storage.CachedOutputMetadata(outputID).Consume(func(outputMetadata *ledger.OutputMetadata) {
outputMetadata.SetGradeOfFinality(newGradeOfFinality)
s.tangle.Ledger.Storage.CachedConsumers(outputID).Consume(func(consumer *ledger.Consumer) {
if !consumerTxs.Has(consumer.TransactionID()) {
consumerTxs.Add(consumer.TransactionID())
txGoFPropWalker.Push(consumer.TransactionID())
}
})
})
}

func (s *SimpleFinalityGadget) setMessageGoF(messageMetadata *tangle.MessageMetadata, gradeOfFinality gof.GradeOfFinality) (modified bool) {
func (s *SimpleFinalityGadget) setMessageGoF(message *tangle.Message, messageMetadata *tangle.MessageMetadata, gradeOfFinality gof.GradeOfFinality) (modified bool) {
// abort if message has GoF already set
if modified = messageMetadata.SetGradeOfFinality(gradeOfFinality); !modified {
return
}

// set GoF of payload (applicable only to transactions)
s.setPayloadGoF(messageMetadata.ID(), gradeOfFinality)

if gradeOfFinality >= s.opts.MessageGoFReachedLevel {
s.Events().MessageConfirmed.Trigger(&tangle.MessageConfirmedEvent{
messageMetadata.ID(),
MessageID: messageMetadata.ID(),
})

// set GoF of payload (applicable only to transactions)
if tx, ok := message.Payload().(*devnetvm.Transaction); ok {
s.tangle.Ledger.SetTransactionInclusionTime(tx.ID(), message.IssuingTime())
}
}

return modified
}

func (s *SimpleFinalityGadget) setPayloadGoF(messageID tangle.MessageID, gradeOfFinality gof.GradeOfFinality) {
s.tangle.Utils.ComputeIfTransaction(messageID, func(transactionID utxo.TransactionID) {
s.tangle.Ledger.Storage.CachedTransactionMetadata(transactionID).Consume(func(transactionMetadata *ledger.TransactionMetadata) {
// A transaction can't have a higher GoF than its branch, thus we need to evaluate based on min(branchGoF,max(messageGoF,transactionGoF)).
// This also works for transactions in MasterBranch since it has gof.High and we then use max(messageGoF,transactionGoF).
// max(messageGoF,transactionGoF) gets the max GoF of any possible reattachment (which has set the transaction's GoF before).
transactionGoF := transactionMetadata.GradeOfFinality()
if transactionGoF > gradeOfFinality {
gradeOfFinality = transactionMetadata.GradeOfFinality()
}

lowestBranchGoF := s.getTransactionBranchesGoF(transactionMetadata)

// This is an invalid invariant and should never happen.
if transactionGoF > lowestBranchGoF {
panic(fmt.Sprintf("%s GoF (%s) is bigger than its branches %s GoF (%s)", transactionID, transactionGoF, transactionMetadata.BranchIDs(), lowestBranchGoF))
}

if lowestBranchGoF < gradeOfFinality {
gradeOfFinality = lowestBranchGoF
}

// abort if transaction has GoF already set
if !transactionMetadata.SetGradeOfFinality(gradeOfFinality) {
return
}

// set GoF in outputs
for it := transactionMetadata.OutputIDs().Iterator(); it.HasNext(); {
s.tangle.Ledger.Storage.CachedOutputMetadata(it.Next()).Consume(func(outputMetadata *ledger.OutputMetadata) {
outputMetadata.SetGradeOfFinality(gradeOfFinality)
})
}

if gradeOfFinality >= s.opts.BranchGoFReachedLevel {
s.Events().TransactionConfirmed.Trigger(&tangle.TransactionConfirmedEvent{
TransactionID: transactionID,
})
}
})
})
}

func (s *SimpleFinalityGadget) getTransactionBranchesGoF(transactionMetadata *ledger.TransactionMetadata) (lowestBranchGoF gof.GradeOfFinality) {
lowestBranchGoF = gof.High
for it := transactionMetadata.BranchIDs().Iterator(); it.HasNext(); {
Expand Down
Loading