diff --git a/components/dashboard/jsonresponse.go b/components/dashboard/jsonresponse.go index 4819642b6..c5dd4906f 100644 --- a/components/dashboard/jsonresponse.go +++ b/components/dashboard/jsonresponse.go @@ -130,13 +130,13 @@ type Transaction struct { // NewTransaction returns a Transaction from the given iotago.SignedTransaction. func NewTransaction(signedTx *iotago.SignedTransaction) *Transaction { - txID, err := signedTx.ID() + txID, err := signedTx.Transaction.ID() if err != nil { return nil } - inputs := make([]*Input, len(signedTx.Transaction.Inputs)) - for i, input := range signedTx.Transaction.Inputs { + inputs := make([]*Input, len(signedTx.Transaction.TransactionEssence.Inputs)) + for i, input := range signedTx.Transaction.TransactionEssence.Inputs { inputs[i] = NewInput(input) } diff --git a/components/dashboard/visualizer.go b/components/dashboard/visualizer.go index d9daffca2..1475721af 100644 --- a/components/dashboard/visualizer.go +++ b/components/dashboard/visualizer.go @@ -43,7 +43,7 @@ type tipinfo struct { func sendVertex(blk *blocks.Block, confirmed bool) { modelBlk, _ := model.BlockFromBlock(blk.ProtocolBlock()) - tx, isTx := modelBlk.SignedTransaction() + signedTransaction, isTx := modelBlk.SignedTransaction() broadcastWsBlock(&wsblk{MsgTypeVertex, &vertex{ ID: blk.ID().ToHex(), @@ -54,7 +54,7 @@ func sendVertex(blk *blocks.Block, confirmed bool) { IsTx: isTx, IsTxAccepted: func() bool { if isTx { - txMetadata, exists := deps.Protocol.MainEngineInstance().Ledger.MemPool().TransactionMetadata(lo.PanicOnErr(tx.ID())) + txMetadata, exists := deps.Protocol.MainEngineInstance().Ledger.MemPool().TransactionMetadata(lo.PanicOnErr(signedTransaction.Transaction.ID())) if exists { return txMetadata.IsAccepted() } @@ -85,9 +85,9 @@ func runVisualizer(component *app.Component) { deps.Protocol.Events.Engine.BlockDAG.BlockAttached.Hook(func(block *blocks.Block) { sendVertex(block, false) - tx, hasTx := block.SignedTransaction() + signedTransaction, hasTx := block.SignedTransaction() if hasTx { - txMetadata, exists := deps.Protocol.MainEngineInstance().Ledger.MemPool().TransactionMetadata(lo.PanicOnErr(tx.ID())) + txMetadata, exists := deps.Protocol.MainEngineInstance().Ledger.MemPool().TransactionMetadata(lo.PanicOnErr(signedTransaction.Transaction.ID())) if exists { txMetadata.OnAccepted(func() { sendTxAccepted(block.ID(), true) diff --git a/components/debugapi/transactions.go b/components/debugapi/transactions.go index 1371882b9..890e7660d 100644 --- a/components/debugapi/transactions.go +++ b/components/debugapi/transactions.go @@ -18,7 +18,7 @@ func init() { func storeTransactionsPerSlot(scd *notarization.SlotCommittedDetails) error { slot := scd.Commitment.Slot() stateDiff := deps.Protocol.MainEngineInstance().Ledger.MemPool().StateDiff(slot) - mutationsTree := ads.NewSet(mapdb.NewMapDB(), iotago.TransactionID.Bytes, iotago.SlotIdentifierFromBytes) + mutationsTree := ads.NewSet(mapdb.NewMapDB(), iotago.TransactionID.Bytes, iotago.TransactionIDFromBytes) tcs := &TransactionsChangesResponse{ Index: slot, IncludedTransactions: make([]string, 0), diff --git a/components/metrics/metrics_slots.go b/components/metrics/metrics_slots.go index c5957d385..02f49c8df 100644 --- a/components/metrics/metrics_slots.go +++ b/components/metrics/metrics_slots.go @@ -71,7 +71,7 @@ var SlotMetrics = collector.NewCollection(slotNamespace, collector.WithInitFunc(func() { deps.Protocol.MainEngineInstance().Ledger.OnTransactionAttached(func(transactionMetadata mempool.TransactionMetadata) { transactionMetadata.OnAccepted(func() { - for _, attachmentBlockID := range transactionMetadata.Attachments() { + for _, attachmentBlockID := range transactionMetadata.ValidAttachments() { if block, exists := deps.Protocol.MainEngineInstance().BlockCache.Block(attachmentBlockID); exists && block.IsAccepted() { deps.Collector.Increment(slotNamespace, acceptedAttachments, strconv.Itoa(int(attachmentBlockID.Slot()))) } @@ -95,7 +95,7 @@ var SlotMetrics = collector.NewCollection(slotNamespace, deps.Protocol.Events.Engine.ConflictDAG.ConflictCreated.Hook(func(conflictID iotago.TransactionID) { if txMetadata, exists := deps.Protocol.MainEngineInstance().Ledger.TransactionMetadata(conflictID); exists { - for _, attachment := range txMetadata.Attachments() { + for _, attachment := range txMetadata.ValidAttachments() { deps.Collector.Increment(slotNamespace, createdConflicts, strconv.Itoa(int(attachment.Slot()))) } } @@ -117,7 +117,7 @@ var SlotMetrics = collector.NewCollection(slotNamespace, deps.Protocol.Events.Engine.ConflictDAG.ConflictAccepted.Hook(func(conflictID iotago.TransactionID) { if txMetadata, exists := deps.Protocol.MainEngineInstance().Ledger.TransactionMetadata(conflictID); exists { - for _, attachmentBlockID := range txMetadata.Attachments() { + for _, attachmentBlockID := range txMetadata.ValidAttachments() { if attachment, exists := deps.Protocol.MainEngineInstance().BlockCache.Block(attachmentBlockID); exists && attachment.IsAccepted() { deps.Collector.Increment(slotNamespace, acceptedConflicts, strconv.Itoa(int(attachment.ID().Slot()))) } @@ -141,7 +141,7 @@ var SlotMetrics = collector.NewCollection(slotNamespace, deps.Protocol.Events.Engine.ConflictDAG.ConflictRejected.Hook(func(conflictID iotago.TransactionID) { if txMetadata, exists := deps.Protocol.MainEngineInstance().Ledger.TransactionMetadata(conflictID); exists { - for _, attachmentBlockID := range txMetadata.Attachments() { + for _, attachmentBlockID := range txMetadata.ValidAttachments() { if attachment, exists := deps.Protocol.MainEngineInstance().BlockCache.Block(attachmentBlockID); exists && attachment.IsAccepted() { deps.Collector.Increment(slotNamespace, rejectedConflicts, strconv.Itoa(int(attachment.ID().Slot()))) } diff --git a/go.mod b/go.mod index 760bdc365..265e877aa 100644 --- a/go.mod +++ b/go.mod @@ -26,7 +26,7 @@ require ( github.com/iotaledger/hive.go/stringify v0.0.0-20230929122509-67f34bfed40d github.com/iotaledger/inx-app v1.0.0-rc.3.0.20230927140518-622f63be6182 github.com/iotaledger/inx/go v1.0.0-rc.2.0.20230927140257-bfa0bb0af2bd - github.com/iotaledger/iota.go/v4 v4.0.0-20231002120511-9ab88bf44daf + github.com/iotaledger/iota.go/v4 v4.0.0-20231003101444-5687809cd68a github.com/labstack/echo/v4 v4.11.1 github.com/labstack/gommon v0.4.0 github.com/libp2p/go-libp2p v0.30.0 @@ -61,7 +61,7 @@ require ( github.com/dustin/go-humanize v1.0.1 // indirect github.com/eclipse/paho.mqtt.golang v1.4.3 // indirect github.com/elastic/gosigar v0.14.2 // indirect - github.com/ethereum/go-ethereum v1.13.1 // indirect + github.com/ethereum/go-ethereum v1.13.2 // indirect github.com/fatih/structs v1.1.0 // indirect github.com/felixge/fgprof v0.9.3 // indirect github.com/fjl/memsize v0.0.2 // indirect diff --git a/go.sum b/go.sum index 61beb8884..1d30c914e 100644 --- a/go.sum +++ b/go.sum @@ -96,8 +96,8 @@ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.m github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/ethereum/go-ethereum v1.13.1 h1:UF2FaUKPIy5jeZk3X06ait3y2Q4wI+vJ1l7+UARp+60= -github.com/ethereum/go-ethereum v1.13.1/go.mod h1:xHQKzwkHSl0gnSjZK1mWa06XEdm9685AHqhRknOzqGQ= +github.com/ethereum/go-ethereum v1.13.2 h1:g9mCpfPWqCA1OL4e6C98PeVttb0HadfBRuKTGvMnOvw= +github.com/ethereum/go-ethereum v1.13.2/go.mod h1:gkQ5Ygi64ZBh9M/4iXY1R8WqoNCx1Ey0CkYn2BD4/fw= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= @@ -305,8 +305,8 @@ github.com/iotaledger/inx-app v1.0.0-rc.3.0.20230927140518-622f63be6182 h1:lQikt github.com/iotaledger/inx-app v1.0.0-rc.3.0.20230927140518-622f63be6182/go.mod h1:q24QEsS887ZWJVX76w2kwSgC84KS7wIKOy1otuqZ2ZM= github.com/iotaledger/inx/go v1.0.0-rc.2.0.20230927140257-bfa0bb0af2bd h1:nFG3Zq/zFA4KhBYFX2IezX1C74zfE0DqCt0LrgTa9Ig= github.com/iotaledger/inx/go v1.0.0-rc.2.0.20230927140257-bfa0bb0af2bd/go.mod h1:c5778OnWpLq108YE+Eb2m8Ri/t/4ydV0TvI/Sy5YivQ= -github.com/iotaledger/iota.go/v4 v4.0.0-20231002120511-9ab88bf44daf h1:TNt6qra1H62HctwYhoxujPml/uN2AtnE1zMkB5kkVfI= -github.com/iotaledger/iota.go/v4 v4.0.0-20231002120511-9ab88bf44daf/go.mod h1:+e3bsJFDr9HxmUMe+eQOLNut5wfcB/ivhJdouOJgOnE= +github.com/iotaledger/iota.go/v4 v4.0.0-20231003101444-5687809cd68a h1:xgh1YQvLN+Y3KwX1G9/znGbCaQsfpDtpSLn8nKvaP8s= +github.com/iotaledger/iota.go/v4 v4.0.0-20231003101444-5687809cd68a/go.mod h1:+e3bsJFDr9HxmUMe+eQOLNut5wfcB/ivhJdouOJgOnE= github.com/ipfs/boxo v0.10.0 h1:tdDAxq8jrsbRkYoF+5Rcqyeb91hgWe2hp7iLu7ORZLY= github.com/ipfs/boxo v0.10.0/go.mod h1:Fg+BnfxZ0RPzR0nOodzdIq3A7KgoWAOWsEIImrIQdBM= github.com/ipfs/go-cid v0.4.1 h1:A/T3qGvxi4kpKWWcPC/PgbvDA2bjVLO7n4UeVwnbs/s= diff --git a/pkg/core/promise/promise.go b/pkg/core/promise/promise.go index 8e2ccb514..316901366 100644 --- a/pkg/core/promise/promise.go +++ b/pkg/core/promise/promise.go @@ -212,6 +212,22 @@ func (p *Promise[T]) WasCompleted() bool { return p.complete } +// Result returns the result of the promise (or the zero value if the promise was not resolved). +func (p *Promise[T]) Result() T { + p.mutex.RLock() + defer p.mutex.RUnlock() + + return p.result +} + +// Err returns the error of the promise (or nil if the promise was not rejected). +func (p *Promise[T]) Err() error { + p.mutex.RLock() + defer p.mutex.RUnlock() + + return p.err +} + // IsEmpty returns true if the promise has no updateCallbacks. func (p *Promise[T]) IsEmpty() bool { p.mutex.RLock() diff --git a/pkg/protocol/engine/booker/inmemorybooker/booker.go b/pkg/protocol/engine/booker/inmemorybooker/booker.go index 2a9e123f5..f84a226f2 100644 --- a/pkg/protocol/engine/booker/inmemorybooker/booker.go +++ b/pkg/protocol/engine/booker/inmemorybooker/booker.go @@ -27,7 +27,7 @@ type Booker struct { blockCache *blocks.Blocks - conflictDAG conflictdag.ConflictDAG[iotago.TransactionID, iotago.OutputID, ledger.BlockVoteRank] + conflictDAG conflictdag.ConflictDAG[iotago.TransactionID, mempool.StateID, ledger.BlockVoteRank] ledger ledger.Ledger @@ -98,23 +98,26 @@ var _ booker.Booker = new(Booker) // Queue checks if payload is solid and then adds the block to a Booker's CausalOrder. func (b *Booker) Queue(block *blocks.Block) error { - transactionMetadata, containsTransaction := b.ledger.AttachTransaction(block) + signedTransactionMetadata, containsTransaction := b.ledger.AttachTransaction(block) if !containsTransaction { b.bookingOrder.Queue(block) return nil } - if transactionMetadata == nil { + if signedTransactionMetadata == nil { b.retainBlockFailure(block.ID(), apimodels.BlockFailurePayloadInvalid) return ierrors.Errorf("transaction in %s was not attached", block.ID()) } // Based on the assumption that we always fork and the UTXO and Tangle past cones are always fully known. - transactionMetadata.OnBooked(func() { - block.SetPayloadConflictIDs(transactionMetadata.ConflictIDs()) - b.bookingOrder.Queue(block) + signedTransactionMetadata.OnSignaturesValid(func() { + transactionMetadata := signedTransactionMetadata.TransactionMetadata() + transactionMetadata.OnBooked(func() { + block.SetPayloadConflictIDs(transactionMetadata.ConflictIDs()) + b.bookingOrder.Queue(block) + }) }) return nil @@ -171,7 +174,7 @@ func (b *Booker) inheritConflicts(block *blocks.Block) (conflictIDs ds.Set[iotag case iotago.ShallowLikeParentType: // Check whether the parent contains a conflicting TX, // otherwise reference is invalid and the block should be marked as invalid as well. - if tx, hasTx := parentBlock.SignedTransaction(); !hasTx || !parentBlock.PayloadConflictIDs().Has(lo.PanicOnErr(tx.ID())) { + if signedTransaction, hasTx := parentBlock.SignedTransaction(); !hasTx || !parentBlock.PayloadConflictIDs().Has(lo.PanicOnErr(signedTransaction.Transaction.ID())) { return nil, ierrors.Wrapf(err, "shallow like parent %s does not contain a conflicting transaction", parent.ID.String()) } diff --git a/pkg/protocol/engine/events.go b/pkg/protocol/engine/events.go index fe56288da..7a07445da 100644 --- a/pkg/protocol/engine/events.go +++ b/pkg/protocol/engine/events.go @@ -14,6 +14,7 @@ import ( "github.com/iotaledger/iota-core/pkg/protocol/engine/eviction" "github.com/iotaledger/iota-core/pkg/protocol/engine/filter" "github.com/iotaledger/iota-core/pkg/protocol/engine/ledger" + "github.com/iotaledger/iota-core/pkg/protocol/engine/mempool" "github.com/iotaledger/iota-core/pkg/protocol/engine/mempool/conflictdag" "github.com/iotaledger/iota-core/pkg/protocol/engine/notarization" "github.com/iotaledger/iota-core/pkg/protocol/engine/syncmanager" @@ -41,7 +42,7 @@ type Events struct { SybilProtection *sybilprotection.Events Ledger *ledger.Events Notarization *notarization.Events - ConflictDAG *conflictdag.Events[iotago.TransactionID, iotago.OutputID] + ConflictDAG *conflictdag.Events[iotago.TransactionID, mempool.StateID] Scheduler *scheduler.Events SeatManager *seatmanager.Events SyncManager *syncmanager.Events @@ -68,7 +69,7 @@ var NewEvents = event.CreateGroupConstructor(func() (newEvents *Events) { SybilProtection: sybilprotection.NewEvents(), Ledger: ledger.NewEvents(), Notarization: notarization.NewEvents(), - ConflictDAG: conflictdag.NewEvents[iotago.TransactionID, iotago.OutputID](), + ConflictDAG: conflictdag.NewEvents[iotago.TransactionID, mempool.StateID](), Scheduler: scheduler.NewEvents(), SeatManager: seatmanager.NewEvents(), SyncManager: syncmanager.NewEvents(), diff --git a/pkg/protocol/engine/ledger/ledger.go b/pkg/protocol/engine/ledger/ledger.go index 81fbfc721..da542bb46 100644 --- a/pkg/protocol/engine/ledger/ledger.go +++ b/pkg/protocol/engine/ledger/ledger.go @@ -16,7 +16,7 @@ import ( ) type Ledger interface { - AttachTransaction(block *blocks.Block) (transactionMetadata mempool.TransactionMetadata, containsTransaction bool) + AttachTransaction(block *blocks.Block) (signedTransactionMetadata mempool.SignedTransactionMetadata, containsTransaction bool) OnTransactionAttached(callback func(transactionMetadata mempool.TransactionMetadata), opts ...event.Option) TransactionMetadata(id iotago.TransactionID) (transactionMetadata mempool.TransactionMetadata, exists bool) TransactionMetadataByAttachment(blockID iotago.BlockID) (transactionMetadata mempool.TransactionMetadata, exists bool) @@ -30,7 +30,7 @@ type Ledger interface { ForEachUnspentOutput(func(output *utxoledger.Output) bool) error AddGenesisUnspentOutput(unspentOutput *utxoledger.Output) error - ConflictDAG() conflictdag.ConflictDAG[iotago.TransactionID, iotago.OutputID, BlockVoteRank] + ConflictDAG() conflictdag.ConflictDAG[iotago.TransactionID, mempool.StateID, BlockVoteRank] MemPool() mempool.MemPool[BlockVoteRank] SlotDiffs(slot iotago.SlotIndex) (*utxoledger.SlotDiff, error) diff --git a/pkg/protocol/engine/ledger/ledger/ledger.go b/pkg/protocol/engine/ledger/ledger/ledger.go index a216055f9..6136c26b2 100644 --- a/pkg/protocol/engine/ledger/ledger/ledger.go +++ b/pkg/protocol/engine/ledger/ledger/ledger.go @@ -23,7 +23,6 @@ import ( "github.com/iotaledger/iota-core/pkg/protocol/engine/mempool/conflictdag" "github.com/iotaledger/iota-core/pkg/protocol/engine/mempool/conflictdag/conflictdagv1" mempoolv1 "github.com/iotaledger/iota-core/pkg/protocol/engine/mempool/v1" - "github.com/iotaledger/iota-core/pkg/protocol/engine/notarization" "github.com/iotaledger/iota-core/pkg/protocol/engine/utxoledger" "github.com/iotaledger/iota-core/pkg/protocol/sybilprotection" "github.com/iotaledger/iota-core/pkg/storage/prunable/slotstore" @@ -42,7 +41,7 @@ type Ledger struct { sybilProtection sybilprotection.SybilProtection commitmentLoader func(iotago.SlotIndex) (*model.Commitment, error) memPool mempool.MemPool[ledger.BlockVoteRank] - conflictDAG conflictdag.ConflictDAG[iotago.TransactionID, iotago.OutputID, ledger.BlockVoteRank] + conflictDAG conflictdag.ConflictDAG[iotago.TransactionID, mempool.StateID, ledger.BlockVoteRank] retainTransactionFailure func(iotago.SlotIdentifier, error) errorHandler func(error) @@ -64,12 +63,12 @@ func NewProvider() module.Provider[*engine.Engine, ledger.Ledger] { e.HookConstructed(func() { e.Events.Ledger.LinkTo(l.events) - l.conflictDAG = conflictdagv1.New[iotago.TransactionID, iotago.OutputID, ledger.BlockVoteRank](l.sybilProtection.SeatManager().OnlineCommittee().Size) + l.conflictDAG = conflictdagv1.New[iotago.TransactionID, mempool.StateID, ledger.BlockVoteRank](l.sybilProtection.SeatManager().OnlineCommittee().Size) e.Events.ConflictDAG.LinkTo(l.conflictDAG.Events()) l.setRetainTransactionFailureFunc(e.Retainer.RetainTransactionFailure) - l.memPool = mempoolv1.New(l.executeStardustVM, l.resolveState, e.Workers.CreateGroup("MemPool"), l.conflictDAG, e, l.errorHandler, mempoolv1.WithForkAllTransactions[ledger.BlockVoteRank](true)) + l.memPool = mempoolv1.New(l.validateStardustTransaction, l.executeStardustVM, l.extractInputReferences, l.resolveState, e.Workers.CreateGroup("MemPool"), l.conflictDAG, e, l.errorHandler, mempoolv1.WithForkAllTransactions[ledger.BlockVoteRank](true)) e.EvictionState.Events.SlotEvicted.Hook(l.memPool.Evict) l.manaManager = mana.NewManager(l.apiProvider, l.resolveAccountOutput) @@ -79,9 +78,10 @@ func NewProvider() module.Provider[*engine.Engine, ledger.Ledger] { e.Events.BlockGadget.BlockPreAccepted.Hook(l.blockPreAccepted) - e.Events.Notarization.SlotCommitted.Hook(func(scd *notarization.SlotCommittedDetails) { - l.memPool.PublishCommitmentState(scd.Commitment.Commitment()) - }) + // TODO: CHECK IF STILL NECESSARY + //e.Events.Notarization.SlotCommitted.Hook(func(scd *notarization.SlotCommittedDetails) { + // l.memPool.PublishRequestedState(scd.Commitment.Commitment()) + //}) l.TriggerConstructed() l.TriggerInitialized() @@ -110,7 +110,7 @@ func New( commitmentLoader: commitmentLoader, sybilProtection: sybilProtection, errorHandler: errorHandler, - conflictDAG: conflictdagv1.New[iotago.TransactionID, iotago.OutputID, ledger.BlockVoteRank](sybilProtection.SeatManager().OnlineCommittee().Size), + conflictDAG: conflictdagv1.New[iotago.TransactionID, mempool.StateID, ledger.BlockVoteRank](sybilProtection.SeatManager().OnlineCommittee().Size), } } @@ -122,9 +122,9 @@ func (l *Ledger) OnTransactionAttached(handler func(transaction mempool.Transact l.memPool.OnTransactionAttached(handler, opts...) } -func (l *Ledger) AttachTransaction(block *blocks.Block) (transactionMetadata mempool.TransactionMetadata, containsTransaction bool) { - if transaction, hasTransaction := block.SignedTransaction(); hasTransaction { - transactionMetadata, err := l.memPool.AttachTransaction(transaction, block.ID()) +func (l *Ledger) AttachTransaction(block *blocks.Block) (attachedTransaction mempool.SignedTransactionMetadata, containsTransaction bool) { + if signedTransaction, hasTransaction := block.SignedTransaction(); hasTransaction { + signedTransactionMetadata, err := l.memPool.AttachSignedTransaction(signedTransaction, signedTransaction.Transaction, block.ID()) if err != nil { l.retainTransactionFailure(block.ID(), err) l.errorHandler(err) @@ -132,7 +132,7 @@ func (l *Ledger) AttachTransaction(block *blocks.Block) (transactionMetadata mem return nil, true } - return transactionMetadata, true + return signedTransactionMetadata, true } return nil, false @@ -230,37 +230,23 @@ func (l *Ledger) PastAccounts(accountIDs iotago.AccountIDs, targetIndex iotago.S } func (l *Ledger) Output(outputID iotago.OutputID) (*utxoledger.Output, error) { - stateWithMetadata, err := l.memPool.OutputStateMetadata(outputID.UTXOInput()) + stateWithMetadata, err := l.memPool.StateMetadata(outputID.UTXOInput()) if err != nil { return nil, err } switch castState := stateWithMetadata.State().(type) { case *utxoledger.Output: - return castState, nil - case *ExecutionOutput: - txWithMetadata, exists := l.memPool.TransactionMetadata(outputID.TransactionID()) - // If the transaction is not in the mempool, we need to load the output from the ledger - if !exists { - var output *utxoledger.Output - stateRequest := l.resolveState(outputID.UTXOInput()) - stateRequest.OnSuccess(func(loadedState mempool.State) { - concreteOutput, ok := loadedState.(*utxoledger.Output) - if !ok { - err = iotago.ErrUnknownOutputType - return - } - output = concreteOutput - }) - stateRequest.OnError(func(requestErr error) { err = ierrors.Errorf("failed to request state: %w", requestErr) }) - stateRequest.WaitComplete() + if castState.SlotBooked() == 0 { + txWithMetadata, exists := l.memPool.TransactionMetadata(outputID.TransactionID()) + if exists { + earliestAttachment := txWithMetadata.EarliestIncludedAttachment() - return output, nil + return utxoledger.CreateOutput(l.apiProvider, castState.OutputID(), earliestAttachment, earliestAttachment.Slot(), castState.Output()), nil + } } - earliestAttachment := txWithMetadata.EarliestIncludedAttachment() - - return utxoledger.CreateOutput(l.apiProvider, stateWithMetadata.State().OutputID(), earliestAttachment, earliestAttachment.Slot(), stateWithMetadata.State().Output()), nil + return castState, nil default: panic("unexpected State type") } @@ -306,7 +292,7 @@ func (l *Ledger) TransactionMetadataByAttachment(blockID iotago.BlockID) (mempoo return l.memPool.TransactionMetadataByAttachment(blockID) } -func (l *Ledger) ConflictDAG() conflictdag.ConflictDAG[iotago.TransactionID, iotago.OutputID, ledger.BlockVoteRank] { +func (l *Ledger) ConflictDAG() conflictdag.ConflictDAG[iotago.TransactionID, mempool.StateID, ledger.BlockVoteRank] { return l.conflictDAG } @@ -475,10 +461,10 @@ func (l *Ledger) processCreatedAndConsumedAccountOutputs(stateDiff mempool.State newAccountDelegation := make(map[iotago.ChainID]*iotago.DelegationOutput) - stateDiff.CreatedStates().ForEachKey(func(outputID iotago.OutputID) bool { - createdOutput, errOutput := l.Output(outputID) - if errOutput != nil { - err = ierrors.Errorf("failed to retrieve output %s: %w", outputID, errOutput) + stateDiff.CreatedStates().ForEach(func(_ mempool.StateID, output mempool.StateMetadata) bool { + createdOutput, ok := output.State().(*utxoledger.Output) + if !ok { + err = ierrors.Errorf("unexpected state type %T while processing created states", output.State()) return false } @@ -494,7 +480,7 @@ func (l *Ledger) processCreatedAndConsumedAccountOutputs(stateDiff mempool.State accountID := createdAccount.AccountID if accountID.Empty() { - accountID = iotago.AccountIDFromOutputID(outputID) + accountID = iotago.AccountIDFromOutputID(createdOutput.OutputID()) l.events.AccountCreated.Trigger(accountID) } @@ -507,14 +493,14 @@ func (l *Ledger) processCreatedAndConsumedAccountOutputs(stateDiff mempool.State // Zeroed Delegation ID => newly created. // Non-Zero Delegation ID => delayed claiming transition. if delegationID == iotago.EmptyDelegationID() { - delegationID = iotago.DelegationIDFromOutputID(outputID) + delegationID = iotago.DelegationIDFromOutputID(createdOutput.OutputID()) newAccountDelegation[delegationID] = delegationOutput } case iotago.OutputBasic: // if a basic output is sent to an implicit account creation address, we need to create the account if createdOutput.Output().UnlockConditionSet().Address().Address.Type() == iotago.AddressImplicitAccountCreation { - accountID := iotago.AccountIDFromOutputID(outputID) + accountID := iotago.AccountIDFromOutputID(createdOutput.OutputID()) l.events.AccountCreated.Trigger(accountID) createdAccounts[accountID] = createdOutput } @@ -528,10 +514,10 @@ func (l *Ledger) processCreatedAndConsumedAccountOutputs(stateDiff mempool.State } // input side - stateDiff.DestroyedStates().ForEachKey(func(outputID iotago.OutputID) bool { - spentOutput, errOutput := l.Output(outputID) - if errOutput != nil { - err = ierrors.Errorf("failed to retrieve output %s: %w", outputID, errOutput) + stateDiff.DestroyedStates().ForEach(func(_ mempool.StateID, stateMetadata mempool.StateMetadata) bool { + spentOutput, ok := stateMetadata.State().(*utxoledger.Output) + if !ok { + err = ierrors.Errorf("unexpected state type %T while processing destroyed states", stateMetadata.State()) return false } @@ -553,7 +539,7 @@ func (l *Ledger) processCreatedAndConsumedAccountOutputs(stateDiff mempool.State delegationOutput, _ := spentOutput.Output().(*iotago.DelegationOutput) delegationID := delegationOutput.DelegationID if delegationID == iotago.EmptyDelegationID() { - delegationID = iotago.DelegationIDFromOutputID(outputID) + delegationID = iotago.DelegationIDFromOutputID(spentOutput.OutputID()) } // TODO: do we have a testcase that checks transitioning a delegation output twice in the same slot? @@ -571,7 +557,7 @@ func (l *Ledger) processCreatedAndConsumedAccountOutputs(stateDiff mempool.State case iotago.OutputBasic: // if a basic output (implicit account) is consumed, get the accountID as hash of the output ID. if spentOutput.Output().UnlockConditionSet().Address().Address.Type() == iotago.AddressImplicitAccountCreation { - accountID := iotago.AccountIDFromOutputID(outputID) + accountID := iotago.AccountIDFromOutputID(spentOutput.OutputID()) consumedAccounts[accountID] = spentOutput } } @@ -596,7 +582,7 @@ func (l *Ledger) processStateDiffTransactions(stateDiff mempool.StateDiff) (spen accountDiffs = make(map[iotago.AccountID]*model.AccountDiff) stateDiff.ExecutedTransactions().ForEach(func(txID iotago.TransactionID, txWithMeta mempool.TransactionMetadata) bool { - tx, ok := txWithMeta.Transaction().(*iotago.SignedTransaction) + tx, ok := txWithMeta.Transaction().(*iotago.Transaction) if !ok { err = iotago.ErrTxTypeInvalid return false @@ -623,15 +609,25 @@ func (l *Ledger) processStateDiffTransactions(stateDiff mempool.StateDiff) (spen } // output side - txWithMeta.Outputs().Range(func(stateMetadata mempool.OutputStateMetadata) { - output := utxoledger.CreateOutput(l.apiProvider, stateMetadata.State().OutputID(), txWithMeta.EarliestIncludedAttachment(), stateDiff.Slot(), stateMetadata.State().Output()) + if err = txWithMeta.Outputs().ForEach(func(stateMetadata mempool.StateMetadata) error { + typedOutput, ok := stateMetadata.State().(*utxoledger.Output) + if !ok { + err = ierrors.Errorf("unexpected state type %T while processing state diff transactions", stateMetadata.State()) + return err + } + + output := utxoledger.CreateOutput(l.apiProvider, typedOutput.OutputID(), txWithMeta.EarliestIncludedAttachment(), stateDiff.Slot(), typedOutput.Output()) outputs = append(outputs, output) - }) + + return nil + }); err != nil { + return false + } } // process allotments { - for _, allotment := range tx.Transaction.Allotments { + for _, allotment := range tx.Allotments { // in case it didn't exist, allotments won't change the outputID of the Account, // so the diff defaults to empty new and previous outputIDs accountDiff := getAccountDiff(accountDiffs, allotment.AccountID) diff --git a/pkg/protocol/engine/ledger/ledger/state.go b/pkg/protocol/engine/ledger/ledger/state.go deleted file mode 100644 index 8679ddfba..000000000 --- a/pkg/protocol/engine/ledger/ledger/state.go +++ /dev/null @@ -1,32 +0,0 @@ -package ledger - -import ( - "github.com/iotaledger/hive.go/lo" - iotago "github.com/iotaledger/iota.go/v4" -) - -type ExecutionOutput struct { - outputID iotago.OutputID - output iotago.Output - creationSlot iotago.SlotIndex -} - -func (o *ExecutionOutput) StateID() iotago.Identifier { - return iotago.IdentifierFromData(lo.PanicOnErr(o.outputID.Bytes())) -} - -func (o *ExecutionOutput) Type() iotago.StateType { - return iotago.InputUTXO -} - -func (o *ExecutionOutput) OutputID() iotago.OutputID { - return o.outputID -} - -func (o *ExecutionOutput) Output() iotago.Output { - return o.output -} - -func (o *ExecutionOutput) SlotCreated() iotago.SlotIndex { - return o.creationSlot -} diff --git a/pkg/protocol/engine/ledger/ledger/vm.go b/pkg/protocol/engine/ledger/ledger/vm.go index 714c85c01..daf263543 100644 --- a/pkg/protocol/engine/ledger/ledger/vm.go +++ b/pkg/protocol/engine/ledger/ledger/vm.go @@ -5,42 +5,52 @@ import ( "github.com/iotaledger/hive.go/ierrors" "github.com/iotaledger/iota-core/pkg/protocol/engine/mempool" + "github.com/iotaledger/iota-core/pkg/protocol/engine/utxoledger" iotago "github.com/iotaledger/iota.go/v4" iotagovm "github.com/iotaledger/iota.go/v4/vm" "github.com/iotaledger/iota.go/v4/vm/stardust" ) -func (l *Ledger) executeStardustVM(_ context.Context, stateTransition mempool.Transaction, inputStates []mempool.OutputState, timeReference mempool.ContextState) ([]mempool.OutputState, error) { - tx, ok := stateTransition.(*iotago.SignedTransaction) +func (l *Ledger) extractInputReferences(transaction mempool.Transaction) (inputReferences []iotago.Input, err error) { + stardustTransaction, ok := transaction.(*iotago.Transaction) if !ok { return nil, iotago.ErrTxTypeInvalid } - inputSet := iotagovm.InputSet{} - for _, input := range inputStates { - inputSet[input.OutputID()] = input.Output() + for _, input := range stardustTransaction.TransactionEssence.Inputs { + inputReferences = append(inputReferences, input) } - resolvedInputs := iotagovm.ResolvedInputs{ - InputSet: inputSet, + for _, input := range stardustTransaction.TransactionEssence.ContextInputs { + inputReferences = append(inputReferences, input) } - bicInputs, err := tx.BICInputs() - if err != nil { - return nil, ierrors.Join(err, iotago.ErrBICInputInvalid) - } + return inputReferences, nil +} - rewardInputs, err := tx.RewardInputs() - if err != nil { - return nil, ierrors.Join(err, iotago.ErrRewardInputInvalid) +func (l *Ledger) validateStardustTransaction(signedTransaction mempool.SignedTransaction, resolvedInputStates []mempool.State) (executionContext context.Context, err error) { + signedStardustTransaction, ok := signedTransaction.(*iotago.SignedTransaction) + if !ok { + return nil, iotago.ErrTxTypeInvalid } - commitmentInput, ok := timeReference.(*iotago.Commitment) - if commitmentInput != nil && !ok { - return nil, ierrors.Join(iotago.ErrCommitmentInputInvalid, ierrors.New("unsupported type for time reference")) + utxoInputSet := iotagovm.InputSet{} + commitmentInput := (*iotago.Commitment)(nil) + bicInputs := make([]*iotago.BlockIssuanceCreditInput, 0) + rewardInputs := make([]*iotago.RewardInput, 0) + for _, resolvedInput := range resolvedInputStates { + resolvedInput.Type() + switch typedInput := resolvedInput.(type) { + case *iotago.Commitment: + commitmentInput = typedInput + case *iotago.BlockIssuanceCreditInput: + bicInputs = append(bicInputs, typedInput) + case *iotago.RewardInput: + rewardInputs = append(rewardInputs, typedInput) + case *utxoledger.Output: + utxoInputSet[typedInput.OutputID()] = typedInput.Output() + } } - resolvedInputs.CommitmentInput = commitmentInput - if (len(rewardInputs) > 0 || len(bicInputs) > 0) && commitmentInput == nil { return nil, iotago.ErrCommitmentInputMissing } @@ -57,13 +67,16 @@ func (l *Ledger) executeStardustVM(_ context.Context, stateTransition mempool.Tr bicInputSet[inp.AccountID] = accountData.Credits.Value } - resolvedInputs.BlockIssuanceCreditInputSet = bicInputSet rewardInputSet := make(iotagovm.RewardsInputSet) for _, inp := range rewardInputs { - outputID := inputStates[inp.Index].OutputID() + output, ok := resolvedInputStates[inp.Index].(*utxoledger.Output) + if !ok { + return nil, ierrors.Wrapf(iotago.ErrRewardInputInvalid, "input at index %d is not an UTXO output", inp.Index) + } + outputID := output.OutputID() - switch castOutput := inputStates[inp.Index].Output().(type) { + switch castOutput := output.Output().(type) { case *iotago.AccountOutput: stakingFeature := castOutput.FeatureSet().Staking() if stakingFeature == nil { @@ -100,28 +113,72 @@ func (l *Ledger) executeStardustVM(_ context.Context, stateTransition mempool.Tr rewardInputSet[delegationID] = reward } } - resolvedInputs.RewardsInputSet = rewardInputSet - vmParams := &iotagovm.Params{ - API: tx.API, + resolvedInputs := iotagovm.ResolvedInputs{ + InputSet: utxoInputSet, + CommitmentInput: commitmentInput, + BlockIssuanceCreditInputSet: bicInputSet, + RewardsInputSet: rewardInputSet, + } + + unlockedIdentities, err := stardust.NewVirtualMachine().ValidateUnlocks(signedStardustTransaction, resolvedInputs) + if err != nil { + return nil, err + } + + executionContext = context.Background() + executionContext = context.WithValue(executionContext, ExecutionContextKeyUnlockedIdentities, unlockedIdentities) + executionContext = context.WithValue(executionContext, ExecutionContextKeyResolvedInputs, resolvedInputs) + + return executionContext, nil +} + +func (l *Ledger) executeStardustVM(executionContext context.Context, transaction mempool.Transaction) (outputs []mempool.State, err error) { + stardustTransaction, ok := transaction.(*iotago.Transaction) + if !ok { + return nil, iotago.ErrTxTypeInvalid } - if err = stardust.NewVirtualMachine().Execute(tx, vmParams, resolvedInputs); err != nil { + + transactionID, err := stardustTransaction.ID() + if err != nil { return nil, err } - outputSet, err := tx.OutputsSet() + unlockedIdentities, ok := executionContext.Value(ExecutionContextKeyUnlockedIdentities).(iotagovm.UnlockedIdentities) + if !ok { + return nil, ierrors.Errorf("unlockedIdentities not found in execution context") + } + + resolvedInputs, ok := executionContext.Value(ExecutionContextKeyResolvedInputs).(iotagovm.ResolvedInputs) + if !ok { + return nil, ierrors.Errorf("resolvedInputs not found in execution context") + } + + createdOutputs, err := stardust.NewVirtualMachine().Execute(stardustTransaction, resolvedInputs, unlockedIdentities) if err != nil { return nil, err } - created := make([]mempool.OutputState, 0, len(outputSet)) - for outputID, output := range outputSet { - created = append(created, &ExecutionOutput{ - outputID: outputID, - output: output, - creationSlot: tx.Transaction.CreationSlot, - }) + for index, output := range createdOutputs { + outputs = append(outputs, utxoledger.CreateOutput( + l.apiProvider, + iotago.OutputIDFromTransactionIDAndIndex(transactionID, uint16(index)), + iotago.EmptyBlockID(), + 0, + output, + )) } - return created, nil + return outputs, nil } + +// ExecutionContextKey is the type of the keys used in the execution context. +type ExecutionContextKey uint8 + +const ( + // ExecutionContextKeyUnlockedIdentities is the key for the unlocked identities in the execution context. + ExecutionContextKeyUnlockedIdentities ExecutionContextKey = iota + + // ExecutionContextKeyResolvedInputs is the key for the resolved inputs in the execution context. + ExecutionContextKeyResolvedInputs +) diff --git a/pkg/protocol/engine/ledger/tests/state_resolver.go b/pkg/protocol/engine/ledger/tests/state_resolver.go index fcf0729d3..1d56fa0ea 100644 --- a/pkg/protocol/engine/ledger/tests/state_resolver.go +++ b/pkg/protocol/engine/ledger/tests/state_resolver.go @@ -5,33 +5,32 @@ import ( "github.com/iotaledger/hive.go/ierrors" "github.com/iotaledger/iota-core/pkg/core/promise" "github.com/iotaledger/iota-core/pkg/protocol/engine/mempool" - iotago "github.com/iotaledger/iota.go/v4" ) type MockStateResolver struct { - statesByID *shrinkingmap.ShrinkingMap[iotago.OutputID, mempool.OutputState] + statesByID *shrinkingmap.ShrinkingMap[mempool.StateID, mempool.State] } -func New(initialStates ...mempool.OutputState) *MockStateResolver { +func New(initialStates ...mempool.State) *MockStateResolver { stateResolver := &MockStateResolver{ - statesByID: shrinkingmap.New[iotago.OutputID, mempool.OutputState](), + statesByID: shrinkingmap.New[mempool.StateID, mempool.State](), } for _, initialState := range initialStates { - stateResolver.statesByID.Set(initialState.OutputID(), initialState) + stateResolver.statesByID.Set(initialState.StateID(), initialState) } return stateResolver } -func (s *MockStateResolver) AddOutputState(state mempool.OutputState) { - s.statesByID.Set(state.OutputID(), state) +func (s *MockStateResolver) AddOutputState(state mempool.State) { + s.statesByID.Set(state.StateID(), state) } -func (s *MockStateResolver) DestroyOutputState(stateID iotago.OutputID) { +func (s *MockStateResolver) DestroyOutputState(stateID mempool.StateID) { s.statesByID.Delete(stateID) } -func (s *MockStateResolver) ResolveOutputState(outputID iotago.OutputID) *promise.Promise[mempool.State] { +func (s *MockStateResolver) ResolveOutputState(outputID mempool.StateID) *promise.Promise[mempool.State] { output, exists := s.statesByID.Get(outputID) if !exists { return promise.New[mempool.State]().Reject(ierrors.Errorf("output %s not found: %w", outputID.ToHex(), mempool.ErrStateNotFound)) diff --git a/pkg/protocol/engine/mempool/conflictdag/conflictdagv1/conflictdag_test.go b/pkg/protocol/engine/mempool/conflictdag/conflictdagv1/conflictdag_test.go index 71f6d4322..4b41130f7 100644 --- a/pkg/protocol/engine/mempool/conflictdag/conflictdagv1/conflictdag_test.go +++ b/pkg/protocol/engine/mempool/conflictdag/conflictdagv1/conflictdag_test.go @@ -38,7 +38,7 @@ func newTestFramework(t *testing.T) *tests.Framework { // transactionID creates a (made up) TransactionID from the given alias. func transactionID(alias string) iotago.TransactionID { - result := iotago.TransactionIDFromData(TestTransactionCreationSlot, []byte(alias)) + result := iotago.TransactionIDRepresentingData(TestTransactionCreationSlot, []byte(alias)) result.RegisterAlias(alias) return result @@ -46,7 +46,7 @@ func transactionID(alias string) iotago.TransactionID { // outputID creates a (made up) OutputID from the given alias. func outputID(alias string) iotago.OutputID { - return iotago.OutputIDFromTransactionIDAndIndex(iotago.TransactionIDFromData(TestTransactionCreationSlot, []byte(alias)), 1) + return iotago.OutputIDFromTransactionIDAndIndex(iotago.TransactionIDRepresentingData(TestTransactionCreationSlot, []byte(alias)), 1) } func TestMemoryRelease(t *testing.T) { diff --git a/pkg/protocol/engine/mempool/conflictdag/conflictdagv1/sorted_conflicts_test.go b/pkg/protocol/engine/mempool/conflictdag/conflictdagv1/sorted_conflicts_test.go index d6d52dc8f..a1a0b6d96 100644 --- a/pkg/protocol/engine/mempool/conflictdag/conflictdagv1/sorted_conflicts_test.go +++ b/pkg/protocol/engine/mempool/conflictdag/conflictdagv1/sorted_conflicts_test.go @@ -217,7 +217,7 @@ func assertSortedConflictsOrder(t *testing.T, sortedConflicts SortedConflictSet, func id(alias string) iotago.OutputID { bytes := blake2b.Sum256([]byte(alias)) - txIdentifier := iotago.TransactionIDFromData(TestTransactionCreationSlot, bytes[:]) + txIdentifier := iotago.TransactionIDRepresentingData(TestTransactionCreationSlot, bytes[:]) conflictID := iotago.OutputIDFromTransactionIDAndIndex(txIdentifier, 0) txIdentifier.RegisterAlias(alias) diff --git a/pkg/protocol/engine/mempool/mempool.go b/pkg/protocol/engine/mempool/mempool.go index 5ef3891fb..4f4bb8e18 100644 --- a/pkg/protocol/engine/mempool/mempool.go +++ b/pkg/protocol/engine/mempool/mempool.go @@ -7,19 +7,19 @@ import ( ) type MemPool[VoteRank conflictdag.VoteRankType[VoteRank]] interface { - AttachTransaction(transaction Transaction, blockID iotago.BlockID) (storedTransaction TransactionMetadata, err error) + AttachSignedTransaction(signedTransaction SignedTransaction, transaction Transaction, blockID iotago.BlockID) (signedTransactionMetadata SignedTransactionMetadata, err error) - OnTransactionAttached(callback func(metadata TransactionMetadata), opts ...event.Option) + OnSignedTransactionAttached(callback func(signedTransactionMetadata SignedTransactionMetadata), opts ...event.Option) - MarkAttachmentOrphaned(blockID iotago.BlockID) bool + OnTransactionAttached(callback func(metadata TransactionMetadata), opts ...event.Option) MarkAttachmentIncluded(blockID iotago.BlockID) bool - OutputStateMetadata(reference *iotago.UTXOInput) (state OutputStateMetadata, err error) + StateMetadata(reference iotago.Input) (state StateMetadata, err error) TransactionMetadata(id iotago.TransactionID) (transaction TransactionMetadata, exists bool) - PublishCommitmentState(commitment *iotago.Commitment) + InjectRequestedState(state State) TransactionMetadataByAttachment(blockID iotago.BlockID) (transaction TransactionMetadata, exists bool) diff --git a/pkg/protocol/engine/mempool/signed_transaction_metadata.go b/pkg/protocol/engine/mempool/signed_transaction_metadata.go new file mode 100644 index 000000000..0bf7c3be8 --- /dev/null +++ b/pkg/protocol/engine/mempool/signed_transaction_metadata.go @@ -0,0 +1,17 @@ +package mempool + +import iotago "github.com/iotaledger/iota.go/v4" + +type SignedTransactionMetadata interface { + ID() iotago.SignedTransactionID + + SignedTransaction() SignedTransaction + + OnSignaturesValid(func()) (unsubscribe func()) + + OnSignaturesInvalid(func(err error)) (unsubscribe func()) + + TransactionMetadata() TransactionMetadata + + Attachments() []iotago.BlockID +} diff --git a/pkg/protocol/engine/mempool/state.go b/pkg/protocol/engine/mempool/state.go index 5cf2c63b4..0b0c9ed9e 100644 --- a/pkg/protocol/engine/mempool/state.go +++ b/pkg/protocol/engine/mempool/state.go @@ -3,23 +3,7 @@ package mempool import iotago "github.com/iotaledger/iota.go/v4" type State interface { - StateID() iotago.Identifier + StateID() StateID Type() iotago.StateType } - -type OutputState interface { - State - // OutputID returns the identifier of the State. - OutputID() iotago.OutputID - - // Output returns the underlying Output of the State. - Output() iotago.Output - - // SlotCreated returns the slot when the State was created. - SlotCreated() iotago.SlotIndex -} - -type ContextState interface { - State -} diff --git a/pkg/protocol/engine/mempool/state_diff.go b/pkg/protocol/engine/mempool/state_diff.go index 2b7db9b3a..dbf07e728 100644 --- a/pkg/protocol/engine/mempool/state_diff.go +++ b/pkg/protocol/engine/mempool/state_diff.go @@ -13,10 +13,10 @@ type StateDiff interface { Slot() iotago.SlotIndex // DestroyedStates returns a compacted list of all the states that were destroyed in the slot. - DestroyedStates() *shrinkingmap.ShrinkingMap[iotago.OutputID, OutputStateMetadata] + DestroyedStates() *shrinkingmap.ShrinkingMap[StateID, StateMetadata] // CreatedStates returns a compacted list of all the states that were created in the slot. - CreatedStates() *shrinkingmap.ShrinkingMap[iotago.OutputID, OutputStateMetadata] + CreatedStates() *shrinkingmap.ShrinkingMap[StateID, StateMetadata] // ExecutedTransactions returns an un-compacted list of all the transactions that were executed in the slot. ExecutedTransactions() *orderedmap.OrderedMap[iotago.TransactionID, TransactionMetadata] diff --git a/pkg/protocol/engine/mempool/state_id.go b/pkg/protocol/engine/mempool/state_id.go new file mode 100644 index 000000000..4f350e52f --- /dev/null +++ b/pkg/protocol/engine/mempool/state_id.go @@ -0,0 +1,5 @@ +package mempool + +import iotago "github.com/iotaledger/iota.go/v4" + +type StateID = iotago.Identifier diff --git a/pkg/protocol/engine/mempool/state_metadata.go b/pkg/protocol/engine/mempool/state_metadata.go index 55667dc34..6227fd432 100644 --- a/pkg/protocol/engine/mempool/state_metadata.go +++ b/pkg/protocol/engine/mempool/state_metadata.go @@ -5,10 +5,10 @@ import ( iotago "github.com/iotaledger/iota.go/v4" ) -type OutputStateMetadata interface { - OutputID() iotago.OutputID +type StateMetadata interface { + StateID() StateID - State() OutputState + State() State ConflictIDs() reactive.Set[iotago.TransactionID] @@ -20,7 +20,3 @@ type OutputStateMetadata interface { inclusionFlags } - -type ContextStateMetadata interface { - State() ContextState -} diff --git a/pkg/protocol/engine/mempool/tests/testframework.go b/pkg/protocol/engine/mempool/tests/testframework.go index 8e93ee924..57451f283 100644 --- a/pkg/protocol/engine/mempool/tests/testframework.go +++ b/pkg/protocol/engine/mempool/tests/testframework.go @@ -20,11 +20,13 @@ import ( type TestFramework struct { Instance mempool.MemPool[vote.MockedRank] - ConflictDAG conflictdag.ConflictDAG[iotago.TransactionID, iotago.OutputID, vote.MockedRank] + ConflictDAG conflictdag.ConflictDAG[iotago.TransactionID, mempool.StateID, vote.MockedRank] - stateIDByAlias map[string]iotago.OutputID - transactionByAlias map[string]mempool.Transaction - blockIDsByAlias map[string]iotago.BlockID + referencesByAlias map[string]iotago.Input + stateIDByAlias map[string]mempool.StateID + signedTransactionByAlias map[string]mempool.SignedTransaction + transactionByAlias map[string]mempool.Transaction + blockIDsByAlias map[string]iotago.BlockID ledgerState *ledgertests.MockStateResolver workers *workerpool.Group @@ -33,13 +35,15 @@ type TestFramework struct { mutex syncutils.RWMutex } -func NewTestFramework(test *testing.T, instance mempool.MemPool[vote.MockedRank], conflictDAG conflictdag.ConflictDAG[iotago.TransactionID, iotago.OutputID, vote.MockedRank], ledgerState *ledgertests.MockStateResolver, workers *workerpool.Group) *TestFramework { +func NewTestFramework(test *testing.T, instance mempool.MemPool[vote.MockedRank], conflictDAG conflictdag.ConflictDAG[iotago.TransactionID, mempool.StateID, vote.MockedRank], ledgerState *ledgertests.MockStateResolver, workers *workerpool.Group) *TestFramework { t := &TestFramework{ - Instance: instance, - ConflictDAG: conflictDAG, - stateIDByAlias: make(map[string]iotago.OutputID), - transactionByAlias: make(map[string]mempool.Transaction), - blockIDsByAlias: make(map[string]iotago.BlockID), + Instance: instance, + ConflictDAG: conflictDAG, + referencesByAlias: make(map[string]iotago.Input), + stateIDByAlias: make(map[string]mempool.StateID), + signedTransactionByAlias: make(map[string]mempool.SignedTransaction), + transactionByAlias: make(map[string]mempool.Transaction), + blockIDsByAlias: make(map[string]iotago.BlockID), ledgerState: ledgerState, workers: workers, @@ -50,6 +54,24 @@ func NewTestFramework(test *testing.T, instance mempool.MemPool[vote.MockedRank] return t } +func (t *TestFramework) CreateSignedTransaction(transactionAlias string, referencedStates []string, outputCount uint16, invalid ...bool) { + t.CreateTransaction(transactionAlias, referencedStates, outputCount, invalid...) + t.SignedTransactionFromTransaction(transactionAlias+"-signed", transactionAlias) +} +func (t *TestFramework) SignedTransactionFromTransaction(signedTransactionAlias string, transactionAlias string) { + transaction, exists := t.transactionByAlias[transactionAlias] + require.True(t.test, exists, "transaction with alias %s does not exist", transactionAlias) + + // create transaction + signedTransaction := NewSignedTransaction(transaction) + + t.signedTransactionByAlias[signedTransactionAlias] = signedTransaction + + // register the transaction ID alias + signedTransactionID, signedTransactionIDErr := signedTransaction.ID() + require.NoError(t.test, signedTransactionIDErr, "failed to retrieve signed transaction ID of signed transaction with alias '%s'", signedTransactionAlias) + signedTransactionID.RegisterAlias(signedTransactionAlias) +} func (t *TestFramework) CreateTransaction(alias string, referencedStates []string, outputCount uint16, invalid ...bool) { // create transaction @@ -65,7 +87,12 @@ func (t *TestFramework) CreateTransaction(alias string, referencedStates []strin // register the aliases for the generated output IDs for i := uint16(0); i < transaction.outputCount; i++ { - t.stateIDByAlias[alias+":"+strconv.Itoa(int(i))] = iotago.OutputIDFromTransactionIDAndIndex(transactionID, i) + t.referencesByAlias[alias+":"+strconv.Itoa(int(i))] = &iotago.UTXOInput{ + TransactionID: transactionID, + TransactionOutputIndex: i, + } + + t.stateIDByAlias[alias+":"+strconv.Itoa(int(i))] = t.referencesByAlias[alias+":"+strconv.Itoa(int(i))].StateID() } } @@ -73,10 +100,6 @@ func (t *TestFramework) MarkAttachmentIncluded(alias string) bool { return t.Instance.MarkAttachmentIncluded(t.BlockID(alias)) } -func (t *TestFramework) MarkAttachmentOrphaned(alias string) bool { - return t.Instance.MarkAttachmentOrphaned(t.BlockID(alias)) -} - func (t *TestFramework) BlockID(alias string) iotago.BlockID { blockID, exists := t.blockIDsByAlias[alias] require.True(t.test, exists, "block ID with alias '%s' does not exist", alias) @@ -86,7 +109,7 @@ func (t *TestFramework) BlockID(alias string) iotago.BlockID { func (t *TestFramework) AttachTransactions(transactionAlias ...string) error { for _, alias := range transactionAlias { - if err := t.AttachTransaction(alias, alias, 1); err != nil { + if err := t.AttachTransaction(alias, alias, alias, 1); err != nil { return err } } @@ -94,13 +117,17 @@ func (t *TestFramework) AttachTransactions(transactionAlias ...string) error { return nil } -func (t *TestFramework) AttachTransaction(transactionAlias, blockAlias string, slot iotago.SlotIndex) error { +func (t *TestFramework) AttachTransaction(signedTransactionAlias, transactionAlias, blockAlias string, slot iotago.SlotIndex) error { + signedTransaction, signedTransactionExists := t.signedTransactionByAlias[signedTransactionAlias] + require.True(t.test, signedTransactionExists, "signedTransaction with alias '%s' does not exist", signedTransactionAlias) + transaction, transactionExists := t.transactionByAlias[transactionAlias] require.True(t.test, transactionExists, "transaction with alias '%s' does not exist", transactionAlias) t.blockIDsByAlias[blockAlias] = iotago.SlotIdentifierRepresentingData(slot, []byte(blockAlias)) + t.blockIDsByAlias[blockAlias].RegisterAlias(blockAlias) - if _, err := t.Instance.AttachTransaction(transaction, t.blockIDsByAlias[blockAlias]); err != nil { + if _, err := t.Instance.AttachSignedTransaction(signedTransaction, transaction, t.blockIDsByAlias[blockAlias]); err != nil { return err } @@ -110,14 +137,14 @@ func (t *TestFramework) AttachTransaction(transactionAlias, blockAlias string, s func (t *TestFramework) CommitSlot(slot iotago.SlotIndex) { stateDiff := t.Instance.StateDiff(slot) - stateDiff.CreatedStates().ForEach(func(_ iotago.OutputID, state mempool.OutputStateMetadata) bool { + stateDiff.CreatedStates().ForEach(func(_ mempool.StateID, state mempool.StateMetadata) bool { t.ledgerState.AddOutputState(state.State()) return true }) - stateDiff.DestroyedStates().ForEach(func(outputID iotago.OutputID, _ mempool.OutputStateMetadata) bool { - t.ledgerState.DestroyOutputState(outputID) + stateDiff.DestroyedStates().ForEach(func(stateID mempool.StateID, metadata mempool.StateMetadata) bool { + t.ledgerState.DestroyOutputState(stateID) return true }) @@ -137,13 +164,13 @@ func (t *TestFramework) TransactionMetadataByAttachment(alias string) (mempool.T return t.Instance.TransactionMetadataByAttachment(t.BlockID(alias)) } -func (t *TestFramework) OutputStateMetadata(alias string) (mempool.OutputStateMetadata, error) { - return t.Instance.OutputStateMetadata(t.stateReference(alias)) +func (t *TestFramework) OutputStateMetadata(alias string) (mempool.StateMetadata, error) { + return t.Instance.StateMetadata(t.stateReference(alias)) } -func (t *TestFramework) OutputID(alias string) iotago.OutputID { +func (t *TestFramework) StateID(alias string) mempool.StateID { if alias == "genesis" { - return iotago.OutputID{} + return (&iotago.UTXOInput{}).StateID() } stateID, exists := t.stateIDByAlias[alias] @@ -152,6 +179,16 @@ func (t *TestFramework) OutputID(alias string) iotago.OutputID { return stateID } +func (t *TestFramework) SignedTransactionID(alias string) iotago.SignedTransactionID { + signedTransaction, signedTransactionExists := t.signedTransactionByAlias[alias] + require.True(t.test, signedTransactionExists, "transaction with alias '%s' does not exist", alias) + + signedTransactionID, signedTransactionIDErr := signedTransaction.ID() + require.NoError(t.test, signedTransactionIDErr, "failed to retrieve signed transaction ID of signed transaction with alias '%s'", alias) + + return signedTransactionID +} + func (t *TestFramework) TransactionID(alias string) iotago.TransactionID { transaction, transactionExists := t.transactionByAlias[alias] require.True(t.test, transactionExists, "transaction with alias '%s' does not exist", alias) @@ -274,13 +311,15 @@ func (t *TestFramework) setupHookedEvents() { }) } -func (t *TestFramework) stateReference(alias string) *iotago.UTXOInput { - outputID := t.OutputID(alias) - - return &iotago.UTXOInput{ - TransactionID: outputID.TransactionID(), - TransactionOutputIndex: outputID.Index(), +func (t *TestFramework) stateReference(alias string) iotago.Input { + if alias == "genesis" { + return &iotago.UTXOInput{} } + + reference, exists := t.referencesByAlias[alias] + require.True(t.test, exists, "reference with alias '%s' does not exist", alias) + + return reference } func (t *TestFramework) waitBooked(transactionAliases ...string) { @@ -331,16 +370,16 @@ func (t *TestFramework) AssertStateDiff(slot iotago.SlotIndex, spentOutputAliase require.Equal(t.test, len(transactionAliases), stateDiff.Mutations().Size()) for _, transactionAlias := range transactionAliases { - require.True(t.test, stateDiff.ExecutedTransactions().Has(t.TransactionID(transactionAlias))) - require.True(t.test, lo.PanicOnErr(stateDiff.Mutations().Has(t.TransactionID(transactionAlias)))) + require.True(t.test, stateDiff.ExecutedTransactions().Has(t.TransactionID(transactionAlias)), "transaction %s was not executed", transactionAlias) + require.True(t.test, lo.PanicOnErr(stateDiff.Mutations().Has(t.TransactionID(transactionAlias))), "transaction %s was not mutated", transactionAlias) } for _, createdOutputAlias := range createdOutputAliases { - require.True(t.test, stateDiff.CreatedStates().Has(t.OutputID(createdOutputAlias))) + require.Truef(t.test, stateDiff.CreatedStates().Has(t.StateID(createdOutputAlias)), "state %s was not created", createdOutputAlias) } for _, spentOutputAlias := range spentOutputAliases { - require.True(t.test, stateDiff.DestroyedStates().Has(t.OutputID(spentOutputAlias))) + require.Truef(t.test, stateDiff.DestroyedStates().Has(t.StateID(spentOutputAlias)), "state %s was not destroyed", spentOutputAlias) } } @@ -354,7 +393,9 @@ func (t *TestFramework) Cleanup() { iotago.UnregisterIdentifierAliases() - t.stateIDByAlias = make(map[string]iotago.OutputID) + t.referencesByAlias = make(map[string]iotago.Input) + t.stateIDByAlias = make(map[string]mempool.StateID) t.transactionByAlias = make(map[string]mempool.Transaction) + t.signedTransactionByAlias = make(map[string]mempool.SignedTransaction) t.blockIDsByAlias = make(map[string]iotago.BlockID) } diff --git a/pkg/protocol/engine/mempool/tests/tests.go b/pkg/protocol/engine/mempool/tests/tests.go index 7b34fa2bd..bfd396627 100644 --- a/pkg/protocol/engine/mempool/tests/tests.go +++ b/pkg/protocol/engine/mempool/tests/tests.go @@ -19,18 +19,15 @@ const ( func TestAllWithoutForkingEverything(t *testing.T, frameworkProvider func(*testing.T) *TestFramework) { for testName, testCase := range map[string]func(*testing.T, *TestFramework){ - "TestProcessTransaction": TestProcessTransaction, - "TestProcessTransactionsOutOfOrder": TestProcessTransactionsOutOfOrder, - "TestSetInclusionSlot": TestSetInclusionSlot, - "TestSetTransactionOrphanage": TestSetTransactionOrphanage, - "TestSetAllAttachmentsOrphaned": TestSetAllAttachmentsOrphaned, - "TestSetNotAllAttachmentsOrphaned": TestSetNotAllAttachmentsOrphaned, - "TestSetNotAllAttachmentsOrphanedFutureCone": TestSetNotAllAttachmentsOrphanedFutureCone, - "TestStateDiff": TestStateDiff, - "TestMemoryRelease": TestMemoryRelease, - "TestInvalidTransaction": TestInvalidTransaction, - "TestStoreAttachmentInEvictedSlot": TestStoreAttachmentInEvictedSlot, - "TestConflictPropagationForkOnDoubleSpend": TestConflictPropagationForkOnDoubleSpend, + "TestProcessTransaction": TestProcessTransaction, + "TestProcessTransactionsOutOfOrder": TestProcessTransactionsOutOfOrder, + "TestSetInclusionSlot": TestSetInclusionSlot, + "TestSetTransactionOrphanage": TestSetTransactionOrphanage, + "TestStateDiff": TestStateDiff, + "TestMemoryRelease": TestMemoryRelease, + "TestInvalidTransaction": TestInvalidTransaction, + "TestStoreAttachmentInEvictedSlot": TestStoreAttachmentInEvictedSlot, + "TestConflictPropagationForkOnDoubleSpend": TestConflictPropagationForkOnDoubleSpend, } { t.Run(testName, func(t *testing.T) { testCase(t, frameworkProvider(t)) }) } @@ -54,6 +51,9 @@ func TestProcessTransaction(t *testing.T, tf *TestFramework) { tf.CreateTransaction("tx1", []string{"genesis"}, 1) tf.CreateTransaction("tx2", []string{"tx1:0"}, 1) + tf.SignedTransactionFromTransaction("tx2", "tx2") + tf.SignedTransactionFromTransaction("tx1", "tx1") + require.NoError(t, tf.AttachTransactions("tx1", "tx2")) tf.RequireBooked("tx1", "tx2") @@ -61,7 +61,7 @@ func TestProcessTransaction(t *testing.T, tf *TestFramework) { tx1Metadata, exists := tf.TransactionMetadata("tx1") require.True(t, exists) - _ = tx1Metadata.Outputs().ForEach(func(state mempool.OutputStateMetadata) error { + _ = tx1Metadata.Outputs().ForEach(func(state mempool.StateMetadata) error { require.False(t, state.IsAccepted()) require.Equal(t, 1, state.PendingSpenderCount()) @@ -71,7 +71,7 @@ func TestProcessTransaction(t *testing.T, tf *TestFramework) { tx2Metadata, exists := tf.TransactionMetadata("tx2") require.True(t, exists) - _ = tx2Metadata.Outputs().ForEach(func(state mempool.OutputStateMetadata) error { + _ = tx2Metadata.Outputs().ForEach(func(state mempool.StateMetadata) error { require.False(t, state.IsAccepted()) require.Equal(t, 0, state.PendingSpenderCount()) @@ -80,20 +80,20 @@ func TestProcessTransaction(t *testing.T, tf *TestFramework) { } func TestProcessTransactionsOutOfOrder(t *testing.T, tf *TestFramework) { - tf.CreateTransaction("tx1", []string{"genesis"}, 1) - tf.CreateTransaction("tx2", []string{"tx1:0"}, 1) - tf.CreateTransaction("tx3", []string{"tx2:0"}, 1) + tf.CreateSignedTransaction("tx1", []string{"genesis"}, 1) + tf.CreateSignedTransaction("tx2", []string{"tx1:0"}, 1) + tf.CreateSignedTransaction("tx3", []string{"tx2:0"}, 1) - require.NoError(t, tf.AttachTransactions("tx3")) - require.NoError(t, tf.AttachTransactions("tx2")) - require.NoError(t, tf.AttachTransactions("tx1")) + require.NoError(t, tf.AttachTransaction("tx3-signed", "tx3", "tx3", 1)) + require.NoError(t, tf.AttachTransaction("tx2-signed", "tx2", "tx2", 1)) + require.NoError(t, tf.AttachTransaction("tx1-signed", "tx1", "tx1", 1)) tf.RequireBooked("tx1", "tx2", "tx3") tx1Metadata, exists := tf.TransactionMetadata("tx1") require.True(t, exists) - _ = tx1Metadata.Outputs().ForEach(func(state mempool.OutputStateMetadata) error { + _ = tx1Metadata.Outputs().ForEach(func(state mempool.StateMetadata) error { require.False(t, state.IsAccepted()) require.Equal(t, 1, state.PendingSpenderCount()) @@ -103,7 +103,7 @@ func TestProcessTransactionsOutOfOrder(t *testing.T, tf *TestFramework) { tx2Metadata, exists := tf.TransactionMetadata("tx2") require.True(t, exists) - _ = tx2Metadata.Outputs().ForEach(func(state mempool.OutputStateMetadata) error { + _ = tx2Metadata.Outputs().ForEach(func(state mempool.StateMetadata) error { require.False(t, state.IsAccepted()) require.Equal(t, 1, state.PendingSpenderCount()) @@ -113,7 +113,7 @@ func TestProcessTransactionsOutOfOrder(t *testing.T, tf *TestFramework) { tx3Metadata, exists := tf.TransactionMetadata("tx3") require.True(t, exists) - _ = tx3Metadata.Outputs().ForEach(func(state mempool.OutputStateMetadata) error { + _ = tx3Metadata.Outputs().ForEach(func(state mempool.StateMetadata) error { require.False(t, state.IsAccepted()) require.Equal(t, 0, state.PendingSpenderCount()) @@ -124,13 +124,13 @@ func TestProcessTransactionsOutOfOrder(t *testing.T, tf *TestFramework) { func TestSetInclusionSlot(t *testing.T, tf *TestFramework) { debug.SetEnabled(true) defer debug.SetEnabled(false) - tf.CreateTransaction("tx1", []string{"genesis"}, 1) - tf.CreateTransaction("tx2", []string{"tx1:0"}, 1) - tf.CreateTransaction("tx3", []string{"tx2:0"}, 1) + tf.CreateSignedTransaction("tx1", []string{"genesis"}, 1) + tf.CreateSignedTransaction("tx2", []string{"tx1:0"}, 1) + tf.CreateSignedTransaction("tx3", []string{"tx2:0"}, 1) - require.NoError(t, tf.AttachTransaction("tx3", "block3", 3)) - require.NoError(t, tf.AttachTransaction("tx2", "block2", 2)) - require.NoError(t, tf.AttachTransaction("tx1", "block1", 1)) + require.NoError(t, tf.AttachTransaction("tx3-signed", "tx3", "block3", 3)) + require.NoError(t, tf.AttachTransaction("tx2-signed", "tx2", "block2", 2)) + require.NoError(t, tf.AttachTransaction("tx1-signed", "tx1", "block1", 1)) tf.RequireBooked("tx1", "tx2", "tx3") @@ -184,187 +184,16 @@ func TestSetInclusionSlot(t *testing.T, tf *TestFramework) { tf.RequireAttachmentsEvicted(lo.MergeMaps(attachmentDeletionState, map[string]bool{"block3": true})) } -func TestSetAllAttachmentsOrphaned(t *testing.T, tf *TestFramework) { - debug.SetEnabled(true) - defer debug.SetEnabled(false) - tf.CreateTransaction("tx1", []string{"genesis"}, 1) - - require.NoError(t, tf.AttachTransaction("tx1", "block1.2", 2)) - require.NoError(t, tf.AttachTransaction("tx1", "block1.1", 1)) - - tf.RequireBooked("tx1") - - tx1Metadata, exists := tf.TransactionMetadata("tx1") - require.True(t, exists) - - require.EqualValues(t, 0, tx1Metadata.EarliestIncludedAttachment().Slot()) - - require.True(t, tf.MarkAttachmentIncluded("block1.2")) - require.True(t, tx1Metadata.IsAccepted()) - require.EqualValues(t, 2, tx1Metadata.EarliestIncludedAttachment().Slot()) - tf.AssertStateDiff(1, []string{}, []string{}, []string{}) - tf.AssertStateDiff(2, []string{"genesis"}, []string{"tx1:0"}, []string{"tx1"}) - - require.True(t, tf.MarkAttachmentIncluded("block1.1")) - - require.True(t, tx1Metadata.IsAccepted()) - require.EqualValues(t, 1, tx1Metadata.EarliestIncludedAttachment().Slot()) - tf.AssertStateDiff(1, []string{"genesis"}, []string{"tx1:0"}, []string{"tx1"}) - tf.AssertStateDiff(2, []string{}, []string{}, []string{}) - - require.True(t, tf.MarkAttachmentOrphaned("block1.1")) - - require.True(t, tx1Metadata.IsAccepted()) - require.False(t, tx1Metadata.IsOrphaned()) - require.EqualValues(t, 2, tx1Metadata.EarliestIncludedAttachment().Slot()) - tf.AssertStateDiff(1, []string{}, []string{}, []string{}) - tf.AssertStateDiff(2, []string{"genesis"}, []string{"tx1:0"}, []string{"tx1"}) - - require.True(t, tf.MarkAttachmentOrphaned("block1.2")) - - require.True(t, tx1Metadata.IsOrphaned()) - require.False(t, tx1Metadata.IsAccepted()) - require.EqualValues(t, 0, tx1Metadata.EarliestIncludedAttachment().Slot()) - - tf.AssertStateDiff(1, []string{}, []string{}, []string{}) - tf.AssertStateDiff(2, []string{}, []string{}, []string{}) -} - -func TestSetNotAllAttachmentsOrphanedFutureCone(t *testing.T, tf *TestFramework) { - debug.SetEnabled(true) - defer debug.SetEnabled(false) - tf.CreateTransaction("tx1", []string{"genesis"}, 1) - tf.CreateTransaction("tx2", []string{"tx1:0"}, 1) - tf.CreateTransaction("tx3", []string{"tx2:0"}, 1) - tf.CreateTransaction("tx4", []string{"tx3:0"}, 1) - tf.CreateTransaction("tx5", []string{"tx4:0"}, 1) - - require.NoError(t, tf.AttachTransaction("tx5", "block5", 1)) - require.NoError(t, tf.AttachTransaction("tx4", "block4.3", 1)) - require.NoError(t, tf.AttachTransaction("tx4", "block4.2", 1)) - require.NoError(t, tf.AttachTransaction("tx4", "block4.1", 1)) - require.NoError(t, tf.AttachTransaction("tx3", "block3", 1)) - require.NoError(t, tf.AttachTransaction("tx2", "block2", 1)) - - require.NoError(t, tf.AttachTransaction("tx1", "block1.2", 1)) - require.NoError(t, tf.AttachTransaction("tx1", "block1.1", 1)) - - tf.RequireBooked("tx1", "tx2", "tx3", "tx4", "tx5") - - require.True(t, tf.MarkAttachmentIncluded("block5")) - require.True(t, tf.MarkAttachmentIncluded("block3")) - require.True(t, tf.MarkAttachmentIncluded("block2")) - - acceptanceState := map[string]bool{"tx1": false, "tx2": false, "tx3": false, "tx4": false, "tx5": false} - tf.RequireAccepted(acceptanceState) - - require.True(t, tf.MarkAttachmentIncluded("block1.1")) - - tf.RequireAccepted(lo.MergeMaps(acceptanceState, map[string]bool{"tx1": true, "tx2": true, "tx3": true})) - tf.AssertStateDiff(1, []string{"genesis"}, []string{"tx3:0"}, []string{"tx1", "tx2", "tx3"}) - - require.True(t, tf.MarkAttachmentIncluded("block4.1")) - - tf.RequireAccepted(lo.MergeMaps(acceptanceState, map[string]bool{"tx4": true, "tx5": true})) - tf.AssertStateDiff(1, []string{"genesis"}, []string{"tx5:0"}, []string{"tx1", "tx2", "tx3", "tx4", "tx5"}) - - require.True(t, tf.MarkAttachmentOrphaned("block4.1")) - - tf.RequireAccepted(lo.MergeMaps(acceptanceState, map[string]bool{"tx4": false, "tx5": false})) - tf.AssertStateDiff(1, []string{"genesis"}, []string{"tx3:0"}, []string{"tx1", "tx2", "tx3"}) - - require.True(t, tf.MarkAttachmentOrphaned("block1.1")) - - tf.RequireAccepted(lo.MergeMaps(acceptanceState, map[string]bool{"tx1": false, "tx2": false, "tx3": false})) - tf.AssertStateDiff(1, []string{}, []string{}, []string{}) - - require.True(t, tf.MarkAttachmentIncluded("block1.2")) - - tf.RequireAccepted(lo.MergeMaps(acceptanceState, map[string]bool{"tx1": true, "tx2": true, "tx3": true})) - tf.AssertStateDiff(1, []string{"genesis"}, []string{"tx3:0"}, []string{"tx1", "tx2", "tx3"}) - - require.True(t, tf.MarkAttachmentIncluded("block4.2")) - - tf.RequireAccepted(lo.MergeMaps(acceptanceState, map[string]bool{"tx4": true, "tx5": true})) - tf.AssertStateDiff(1, []string{"genesis"}, []string{"tx5:0"}, []string{"tx1", "tx2", "tx3", "tx4", "tx5"}) - - require.True(t, tf.MarkAttachmentOrphaned("block4.2")) - - tf.RequireAccepted(lo.MergeMaps(acceptanceState, map[string]bool{"tx4": false, "tx5": false})) - tf.AssertStateDiff(1, []string{"genesis"}, []string{"tx3:0"}, []string{"tx1", "tx2", "tx3"}) -} - -func TestSetNotAllAttachmentsOrphaned(t *testing.T, tf *TestFramework) { - debug.SetEnabled(true) - defer debug.SetEnabled(false) - tf.CreateTransaction("tx1", []string{"genesis"}, 1) - - require.NoError(t, tf.AttachTransaction("tx1", "block1.6", 6)) - require.NoError(t, tf.AttachTransaction("tx1", "block1.5", 5)) - require.NoError(t, tf.AttachTransaction("tx1", "block1.4", 4)) - require.NoError(t, tf.AttachTransaction("tx1", "block1.3", 3)) - require.NoError(t, tf.AttachTransaction("tx1", "block1.2", 2)) - require.NoError(t, tf.AttachTransaction("tx1", "block1.1", 1)) - - tf.RequireBooked("tx1") - - tx1Metadata, exists := tf.TransactionMetadata("tx1") - require.True(t, exists) - - require.EqualValues(t, 0, tx1Metadata.EarliestIncludedAttachment().Slot()) - - require.True(t, tf.MarkAttachmentIncluded("block1.2")) - - tf.Instance.Evict(1) - - require.True(t, tx1Metadata.IsAccepted()) - require.False(t, tx1Metadata.IsOrphaned()) - require.EqualValues(t, 2, tx1Metadata.EarliestIncludedAttachment().Slot()) - tf.AssertStateDiff(2, []string{"genesis"}, []string{"tx1:0"}, []string{"tx1"}) - - require.True(t, tf.MarkAttachmentOrphaned("block1.2")) - - require.False(t, tx1Metadata.IsAccepted()) - require.False(t, tx1Metadata.IsOrphaned()) - require.EqualValues(t, 0, tx1Metadata.EarliestIncludedAttachment().Slot()) - tf.AssertStateDiff(2, []string{}, []string{}, []string{}) - - require.True(t, tf.MarkAttachmentIncluded("block1.4")) - - require.True(t, tx1Metadata.IsAccepted()) - require.False(t, tx1Metadata.IsOrphaned()) - require.EqualValues(t, 4, tx1Metadata.EarliestIncludedAttachment().Slot()) - tf.AssertStateDiff(4, []string{"genesis"}, []string{"tx1:0"}, []string{"tx1"}) - - tf.Instance.Evict(2) - tf.Instance.Evict(3) - - require.True(t, tx1Metadata.IsAccepted()) - require.False(t, tx1Metadata.IsOrphaned()) - require.EqualValues(t, 4, tx1Metadata.EarliestIncludedAttachment().Slot()) - tf.AssertStateDiff(4, []string{"genesis"}, []string{"tx1:0"}, []string{"tx1"}) - - tf.Instance.Evict(4) - - require.True(t, tf.MarkAttachmentIncluded("block1.5")) - - require.True(t, tx1Metadata.IsAccepted()) - require.False(t, tx1Metadata.IsOrphaned()) - require.EqualValues(t, 4, tx1Metadata.EarliestIncludedAttachment().Slot()) - tf.AssertStateDiff(4, []string{}, []string{}, []string{}) - tf.AssertStateDiff(5, []string{}, []string{}, []string{}) -} - func TestSetTransactionOrphanage(t *testing.T, tf *TestFramework) { debug.SetEnabled(true) defer debug.SetEnabled(false) - tf.CreateTransaction("tx1", []string{"genesis"}, 1) - tf.CreateTransaction("tx2", []string{"tx1:0"}, 1) - tf.CreateTransaction("tx3", []string{"tx2:0"}, 1) + tf.CreateSignedTransaction("tx1", []string{"genesis"}, 1) + tf.CreateSignedTransaction("tx2", []string{"tx1:0"}, 1) + tf.CreateSignedTransaction("tx3", []string{"tx2:0"}, 1) - require.NoError(t, tf.AttachTransaction("tx3", "block3", 3)) - require.NoError(t, tf.AttachTransaction("tx2", "block2", 2)) - require.NoError(t, tf.AttachTransaction("tx1", "block1", 1)) + require.NoError(t, tf.AttachTransaction("tx3-signed", "tx3", "block3", 3)) + require.NoError(t, tf.AttachTransaction("tx2-signed", "tx2", "block2", 2)) + require.NoError(t, tf.AttachTransaction("tx1-signed", "tx1", "block1", 1)) tf.RequireBooked("tx1", "tx2", "tx3") tx1Metadata, exists := tf.TransactionMetadata("tx1") @@ -396,14 +225,14 @@ func TestSetTransactionOrphanage(t *testing.T, tf *TestFramework) { func TestSetTxOrphanageMultipleAttachments(t *testing.T, tf *TestFramework) { debug.SetEnabled(true) defer debug.SetEnabled(false) - tf.CreateTransaction("tx1", []string{"genesis"}, 1) - tf.CreateTransaction("tx2", []string{"tx1:0"}, 1) - tf.CreateTransaction("tx3", []string{"tx2:0"}, 1) + tf.CreateSignedTransaction("tx1", []string{"genesis"}, 1) + tf.CreateSignedTransaction("tx2", []string{"tx1:0"}, 1) + tf.CreateSignedTransaction("tx3", []string{"tx2:0"}, 1) - require.NoError(t, tf.AttachTransaction("tx3", "block3", 4)) - require.NoError(t, tf.AttachTransaction("tx2", "block2", 3)) - require.NoError(t, tf.AttachTransaction("tx1", "block1.1", 1)) - require.NoError(t, tf.AttachTransaction("tx1", "block1.2", 2)) + require.NoError(t, tf.AttachTransaction("tx3-signed", "tx3", "block3", 4)) + require.NoError(t, tf.AttachTransaction("tx2-signed", "tx2", "block2", 3)) + require.NoError(t, tf.AttachTransaction("tx1-signed", "tx1", "block1.1", 1)) + require.NoError(t, tf.AttachTransaction("tx1-signed", "tx1", "block1.2", 2)) tf.RequireBooked("tx1", "tx2", "tx3") @@ -450,13 +279,13 @@ func TestSetTxOrphanageMultipleAttachments(t *testing.T, tf *TestFramework) { func TestStateDiff(t *testing.T, tf *TestFramework) { debug.SetEnabled(true) defer debug.SetEnabled(false) - tf.CreateTransaction("tx1", []string{"genesis"}, 1) - tf.CreateTransaction("tx2", []string{"tx1:0"}, 1) - tf.CreateTransaction("tx3", []string{"tx2:0"}, 1) + tf.CreateSignedTransaction("tx1", []string{"genesis"}, 1) + tf.CreateSignedTransaction("tx2", []string{"tx1:0"}, 1) + tf.CreateSignedTransaction("tx3", []string{"tx2:0"}, 1) - require.NoError(t, tf.AttachTransaction("tx3", "block3", 1)) - require.NoError(t, tf.AttachTransaction("tx2", "block2", 1)) - require.NoError(t, tf.AttachTransaction("tx1", "block1", 1)) + require.NoError(t, tf.AttachTransaction("tx3-signed", "tx3", "block3", 1)) + require.NoError(t, tf.AttachTransaction("tx2-signed", "tx2", "block2", 1)) + require.NoError(t, tf.AttachTransaction("tx1-signed", "tx1", "block1", 1)) tf.RequireBooked("tx1", "tx2", "tx3") @@ -481,30 +310,30 @@ func TestStateDiff(t *testing.T, tf *TestFramework) { func TestConflictPropagationForkAll(t *testing.T, tf *TestFramework) { debug.SetEnabled(true) defer debug.SetEnabled(false) - tf.CreateTransaction("tx1", []string{"genesis"}, 1) - tf.CreateTransaction("tx1*", []string{"genesis"}, 1) + tf.CreateSignedTransaction("tx1", []string{"genesis"}, 1) + tf.CreateSignedTransaction("tx1*", []string{"genesis"}, 1) - tf.CreateTransaction("tx2", []string{"tx1:0"}, 1) - tf.CreateTransaction("tx2*", []string{"tx1*:0"}, 1) - tf.CreateTransaction("tx3", []string{"tx2:0"}, 1) - tf.CreateTransaction("tx3*", []string{"tx2*:0"}, 1) - tf.CreateTransaction("tx4", []string{"tx1:0"}, 1) + tf.CreateSignedTransaction("tx2", []string{"tx1:0"}, 1) + tf.CreateSignedTransaction("tx2*", []string{"tx1*:0"}, 1) + tf.CreateSignedTransaction("tx3", []string{"tx2:0"}, 1) + tf.CreateSignedTransaction("tx3*", []string{"tx2*:0"}, 1) + tf.CreateSignedTransaction("tx4", []string{"tx1:0"}, 1) - require.NoError(t, tf.AttachTransaction("tx3", "block3", 3)) - require.NoError(t, tf.AttachTransaction("tx2", "block2", 2)) - require.NoError(t, tf.AttachTransaction("tx1", "block1", 1)) + require.NoError(t, tf.AttachTransaction("tx3-signed", "tx3", "block3", 3)) + require.NoError(t, tf.AttachTransaction("tx2-signed", "tx2", "block2", 2)) + require.NoError(t, tf.AttachTransaction("tx1-signed", "tx1", "block1", 1)) tf.RequireBooked("tx1", "tx2", "tx3") tf.RequireConflictIDs(map[string][]string{"tx1": {"tx1"}, "tx2": {"tx2"}, "tx3": {"tx3"}}) - require.NoError(t, tf.AttachTransaction("tx3*", "block3*", 3)) - require.NoError(t, tf.AttachTransaction("tx2*", "block2*", 2)) - require.NoError(t, tf.AttachTransaction("tx1*", "block1*", 1)) + require.NoError(t, tf.AttachTransaction("tx3*-signed", "tx3*", "block3*", 3)) + require.NoError(t, tf.AttachTransaction("tx2*-signed", "tx2*", "block2*", 2)) + require.NoError(t, tf.AttachTransaction("tx1*-signed", "tx1*", "block1*", 1)) tf.RequireBooked("tx1*", "tx2*", "tx3*") tf.RequireConflictIDs(map[string][]string{"tx1": {"tx1"}, "tx2": {"tx2"}, "tx3": {"tx3"}, "tx1*": {"tx1*"}, "tx2*": {"tx2*"}, "tx3*": {"tx3*"}}) - require.NoError(t, tf.AttachTransaction("tx4", "block4", 2)) + require.NoError(t, tf.AttachTransaction("tx4-signed", "tx4", "block4", 2)) tf.RequireBooked("tx4") tf.RequireConflictIDs(map[string][]string{"tx1": {"tx1"}, "tx2": {"tx2"}, "tx3": {"tx3"}, "tx4": {"tx4"}, "tx1*": {"tx1*"}, "tx2*": {"tx2*"}, "tx3*": {"tx3*"}}) @@ -513,30 +342,30 @@ func TestConflictPropagationForkAll(t *testing.T, tf *TestFramework) { func TestConflictPropagationForkOnDoubleSpend(t *testing.T, tf *TestFramework) { debug.SetEnabled(true) defer debug.SetEnabled(false) - tf.CreateTransaction("tx1", []string{"genesis"}, 1) - tf.CreateTransaction("tx1*", []string{"genesis"}, 1) + tf.CreateSignedTransaction("tx1", []string{"genesis"}, 1) + tf.CreateSignedTransaction("tx1*", []string{"genesis"}, 1) - tf.CreateTransaction("tx2", []string{"tx1:0"}, 1) - tf.CreateTransaction("tx2*", []string{"tx1*:0"}, 1) - tf.CreateTransaction("tx3", []string{"tx2:0"}, 1) - tf.CreateTransaction("tx3*", []string{"tx2*:0"}, 1) - tf.CreateTransaction("tx4", []string{"tx1:0"}, 1) + tf.CreateSignedTransaction("tx2", []string{"tx1:0"}, 1) + tf.CreateSignedTransaction("tx2*", []string{"tx1*:0"}, 1) + tf.CreateSignedTransaction("tx3", []string{"tx2:0"}, 1) + tf.CreateSignedTransaction("tx3*", []string{"tx2*:0"}, 1) + tf.CreateSignedTransaction("tx4", []string{"tx1:0"}, 1) - require.NoError(t, tf.AttachTransaction("tx3", "block3", 3)) - require.NoError(t, tf.AttachTransaction("tx2", "block2", 2)) - require.NoError(t, tf.AttachTransaction("tx1", "block1", 1)) + require.NoError(t, tf.AttachTransaction("tx3-signed", "tx3", "block3", 3)) + require.NoError(t, tf.AttachTransaction("tx2-signed", "tx2", "block2", 2)) + require.NoError(t, tf.AttachTransaction("tx1-signed", "tx1", "block1", 1)) tf.RequireBooked("tx1", "tx2", "tx3") tf.RequireConflictIDs(map[string][]string{"tx1": {}, "tx2": {}, "tx3": {}}) - require.NoError(t, tf.AttachTransaction("tx3*", "block3*", 3)) - require.NoError(t, tf.AttachTransaction("tx2*", "block2*", 2)) - require.NoError(t, tf.AttachTransaction("tx1*", "block1*", 1)) + require.NoError(t, tf.AttachTransaction("tx3*-signed", "tx3*", "block3*", 3)) + require.NoError(t, tf.AttachTransaction("tx2*-signed", "tx2*", "block2*", 2)) + require.NoError(t, tf.AttachTransaction("tx1*-signed", "tx1*", "block1*", 1)) tf.RequireBooked("tx1*", "tx2*", "tx3*") tf.RequireConflictIDs(map[string][]string{"tx1": {"tx1"}, "tx2": {"tx1"}, "tx3": {"tx1"}, "tx1*": {"tx1*"}, "tx2*": {"tx1*"}, "tx3*": {"tx1*"}}) - require.NoError(t, tf.AttachTransaction("tx4", "block4", 2)) + require.NoError(t, tf.AttachTransaction("tx4-signed", "tx4", "block4", 2)) tf.RequireBooked("tx4") tf.RequireConflictIDs(map[string][]string{"tx1": {"tx1"}, "tx2": {"tx2"}, "tx3": {"tx2"}, "tx4": {"tx4"}, "tx1*": {"tx1*"}, "tx2*": {"tx1*"}, "tx3*": {"tx1*"}}) @@ -546,8 +375,8 @@ func TestInvalidTransaction(t *testing.T, tf *TestFramework) { debug.SetEnabled(true) defer debug.SetEnabled(false) - tf.CreateTransaction("tx1", []string{"genesis"}, 1, true) - require.NoError(t, tf.AttachTransaction("tx1", "block2", 1)) + tf.CreateSignedTransaction("tx1", []string{"genesis"}, 1, true) + require.NoError(t, tf.AttachTransaction("tx1-signed", "tx1", "block2", 1)) tf.RequireInvalid("tx1") } @@ -558,8 +387,8 @@ func TestStoreAttachmentInEvictedSlot(t *testing.T, tf *TestFramework) { tf.Instance.Evict(iotago.SlotIndex(5)) - tf.CreateTransaction("tx1", []string{"genesis"}, 1, true) - require.Error(t, tf.AttachTransaction("tx1", "block2", 1)) + tf.CreateSignedTransaction("tx1", []string{"genesis"}, 1, true) + require.Error(t, tf.AttachTransaction("tx1-signed", "tx1", "block2", 1)) require.False(t, lo.Return2(tf.TransactionMetadata("tx1"))) } @@ -568,11 +397,12 @@ func TestMemoryRelease(t *testing.T, tf *TestFramework) { issueTransactions := func(startIndex, transactionCount int, prevStateAlias string) (int, string) { index := startIndex for ; index < startIndex+transactionCount; index++ { + signedTxAlias := fmt.Sprintf("tx%d-signed", index) txAlias := fmt.Sprintf("tx%d", index) blockAlias := fmt.Sprintf("block%d", index) - tf.CreateTransaction(txAlias, []string{prevStateAlias}, 2) + tf.CreateSignedTransaction(txAlias, []string{prevStateAlias}, 2) - require.NoError(t, tf.AttachTransaction(txAlias, blockAlias, iotago.SlotIndex(index))) + require.NoError(t, tf.AttachTransaction(signedTxAlias, txAlias, blockAlias, iotago.SlotIndex(index))) tf.RequireBooked(txAlias) tf.MarkAttachmentIncluded(blockAlias) @@ -586,7 +416,7 @@ func TestMemoryRelease(t *testing.T, tf *TestFramework) { } fmt.Println("Memory report before:") - fmt.Println(memanalyzer.MemoryReport(tf)) + fmt.Println(memanalyzer.MemoryReport(tf.Instance)) memStatsStart := memanalyzer.MemSize(tf) txIndex, prevStateAlias := issueTransactions(1, 20000, "genesis") @@ -599,7 +429,7 @@ func TestMemoryRelease(t *testing.T, tf *TestFramework) { memStatsEnd := memanalyzer.MemSize(tf) fmt.Println("Memory report after:") - fmt.Println(memanalyzer.MemoryReport(tf)) + fmt.Println(memanalyzer.MemoryReport(tf.Instance)) fmt.Println(memStatsEnd, memStatsStart) require.Less(t, float64(memStatsEnd), TestMemoryReleaseMaxMemoryIncreaseFactor*float64(memStatsStart), "the objects in the heap should not grow by more than 15%") diff --git a/pkg/protocol/engine/mempool/tests/transaction.go b/pkg/protocol/engine/mempool/tests/transaction.go index 43e91913c..a21fb2523 100644 --- a/pkg/protocol/engine/mempool/tests/transaction.go +++ b/pkg/protocol/engine/mempool/tests/transaction.go @@ -6,14 +6,34 @@ import ( "github.com/iotaledger/iota.go/v4/tpkg" ) +type SignedTransaction struct { + id iotago.SignedTransactionID + transaction mempool.Transaction +} + +func (s *SignedTransaction) ID() (iotago.SignedTransactionID, error) { + return s.id, nil +} + +func (s *SignedTransaction) String() string { + return "SignedTransaction(" + s.id.String() + ")" +} + type Transaction struct { id iotago.TransactionID - inputs []*iotago.UTXOInput + inputs []iotago.Input outputCount uint16 invalidTransaction bool } -func NewTransaction(outputCount uint16, inputs ...*iotago.UTXOInput) *Transaction { +func NewSignedTransaction(transaction mempool.Transaction) *SignedTransaction { + return &SignedTransaction{ + id: tpkg.RandSignedTransactionID(), + transaction: transaction, + } +} + +func NewTransaction(outputCount uint16, inputs ...iotago.Input) *Transaction { return &Transaction{ id: tpkg.RandTransactionID(), inputs: inputs, @@ -25,7 +45,7 @@ func (t *Transaction) ID() (iotago.TransactionID, error) { return t.id, nil } -func (t *Transaction) Inputs() ([]*iotago.UTXOInput, error) { +func (t *Transaction) Inputs() ([]iotago.Input, error) { return t.inputs, nil } diff --git a/pkg/protocol/engine/mempool/tests/vm.go b/pkg/protocol/engine/mempool/tests/vm.go index 77c411b2b..4985e835f 100644 --- a/pkg/protocol/engine/mempool/tests/vm.go +++ b/pkg/protocol/engine/mempool/tests/vm.go @@ -8,7 +8,11 @@ import ( "github.com/iotaledger/iota-core/pkg/protocol/engine/mempool" ) -func VM(_ context.Context, inputTransaction mempool.Transaction, _ []mempool.OutputState, _ mempool.ContextState) (outputs []mempool.OutputState, err error) { +func TransactionValidator(_ mempool.SignedTransaction, _ []mempool.State) (executionContext context.Context, err error) { + return context.Background(), nil +} + +func TransactionExecutor(_ context.Context, inputTransaction mempool.Transaction) (outputs []mempool.State, err error) { transaction, ok := inputTransaction.(*Transaction) if !ok { return nil, ierrors.New("invalid transaction type in MockedVM") diff --git a/pkg/protocol/engine/mempool/transaction.go b/pkg/protocol/engine/mempool/transaction.go index ed4ff92a9..fdad023a8 100644 --- a/pkg/protocol/engine/mempool/transaction.go +++ b/pkg/protocol/engine/mempool/transaction.go @@ -4,19 +4,14 @@ import ( iotago "github.com/iotaledger/iota.go/v4" ) +type SignedTransaction interface { + // ID returns the identifier of the Transaction that contains a signature. + ID() (iotago.SignedTransactionID, error) +} + type Transaction interface { // ID returns the identifier of the Transaction. ID() (iotago.TransactionID, error) - - // Inputs returns the inputs of the Transaction. - Inputs() ([]*iotago.UTXOInput, error) - - // CommitmentInput returns the commitment input of the Transaction, if present. - CommitmentInput() *iotago.CommitmentInput - - // ContextInputs returns the context inputs of the Transaction. - ContextInputs() (iotago.TransactionContextInputs, error) - - // String returns a human-readable version of the Transaction. - String() string } + +type TransactionInputReferenceRetriever func(transaction Transaction) ([]iotago.Input, error) diff --git a/pkg/protocol/engine/mempool/transaction_metadata.go b/pkg/protocol/engine/mempool/transaction_metadata.go index 24b42e697..3b1103ad8 100644 --- a/pkg/protocol/engine/mempool/transaction_metadata.go +++ b/pkg/protocol/engine/mempool/transaction_metadata.go @@ -11,9 +11,9 @@ type TransactionMetadata interface { Transaction() Transaction - Inputs() ds.Set[OutputStateMetadata] + Inputs() ds.Set[StateMetadata] - Outputs() ds.Set[OutputStateMetadata] + Outputs() ds.Set[StateMetadata] ConflictIDs() reactive.Set[iotago.TransactionID] @@ -39,7 +39,7 @@ type TransactionMetadata interface { OnConflicting(func()) - Attachments() []iotago.BlockID + ValidAttachments() []iotago.BlockID EarliestIncludedAttachment() iotago.BlockID diff --git a/pkg/protocol/engine/mempool/v1/context_state_metadata.go b/pkg/protocol/engine/mempool/v1/context_state_metadata.go deleted file mode 100644 index 70663d31d..000000000 --- a/pkg/protocol/engine/mempool/v1/context_state_metadata.go +++ /dev/null @@ -1,62 +0,0 @@ -package mempoolv1 - -import ( - "sync/atomic" - - "github.com/iotaledger/hive.go/runtime/event" - "github.com/iotaledger/iota-core/pkg/protocol/engine/mempool" - iotago "github.com/iotaledger/iota.go/v4" -) - -type ContextStateMetadata struct { - state mempool.ContextState - - // lifecycle - spenderCount uint64 - allSpendersRemoved *event.Event -} - -func NewContextStateMetadata(state mempool.ContextState) *ContextStateMetadata { - return &ContextStateMetadata{ - state: state, - allSpendersRemoved: event.New(), - } -} - -func (s *ContextStateMetadata) State() mempool.ContextState { - return s.state -} - -func (s *ContextStateMetadata) StateID() iotago.Identifier { - return s.state.StateID() -} - -func (s *ContextStateMetadata) Type() iotago.StateType { - return s.state.Type() -} - -func (s *ContextStateMetadata) PendingSpenderCount() int { - return int(atomic.LoadUint64(&s.spenderCount)) -} - -func (s *ContextStateMetadata) increaseSpenderCount() { - atomic.AddUint64(&s.spenderCount, 1) -} - -func (s *ContextStateMetadata) decreaseSpenderCount() { - if atomic.AddUint64(&s.spenderCount, ^uint64(0)) == 0 { - s.allSpendersRemoved.Trigger() - } -} - -func (s *ContextStateMetadata) onAllSpendersRemoved(callback func()) (unsubscribe func()) { - return s.allSpendersRemoved.Hook(callback).Unhook -} - -func (s *ContextStateMetadata) setupSpender(spender *TransactionMetadata) { - s.increaseSpenderCount() - - spender.OnCommitted(s.decreaseSpenderCount) - - spender.OnOrphaned(s.decreaseSpenderCount) -} diff --git a/pkg/protocol/engine/mempool/v1/inclusion_flags.go b/pkg/protocol/engine/mempool/v1/inclusion_flags.go index 9aa77e34c..80d6e13d1 100644 --- a/pkg/protocol/engine/mempool/v1/inclusion_flags.go +++ b/pkg/protocol/engine/mempool/v1/inclusion_flags.go @@ -86,28 +86,3 @@ func (s *inclusionFlags) IsOrphaned() bool { func (s *inclusionFlags) OnOrphaned(callback func()) { s.orphaned.OnTrigger(callback) } - -// setAccepted marks the entity as accepted. -func (s *inclusionFlags) setAccepted() { - s.accepted.Set(true) -} - -// setPending marks the entity as pending. -func (s *inclusionFlags) setPending() { - s.accepted.Set(false) -} - -// setRejected marks the entity as rejected. -func (s *inclusionFlags) setRejected() { - s.rejected.Trigger() -} - -// setCommitted marks the entity as committed. -func (s *inclusionFlags) setCommitted() { - s.committed.Trigger() -} - -// setOrphaned marks the entity as orphaned. -func (s *inclusionFlags) setOrphaned() { - s.orphaned.Trigger() -} diff --git a/pkg/protocol/engine/mempool/v1/mempool.go b/pkg/protocol/engine/mempool/v1/mempool.go index caef76ba1..6e93935dc 100644 --- a/pkg/protocol/engine/mempool/v1/mempool.go +++ b/pkg/protocol/engine/mempool/v1/mempool.go @@ -21,28 +21,37 @@ import ( // MemPool is a component that manages the state of transactions that are not yet included in the ledger state. type MemPool[VoteRank conflictdag.VoteRankType[VoteRank]] struct { + signedTransactionAttached *event.Event1[mempool.SignedTransactionMetadata] + transactionAttached *event.Event1[mempool.TransactionMetadata] - // executeStateTransition is the VM that is used to execute the state transition of transactions. - executeStateTransition mempool.VM + // executeStateTransition is the TransactionExecutor that is used to execute the state transition of transactions. + executeStateTransition mempool.TransactionExecutor // resolveState is the function that is used to request state from the ledger. resolveState mempool.StateReferenceResolver + inputsOfTransaction mempool.TransactionInputReferenceRetriever + + validateSignedTransaction mempool.TransactionValidator + // attachments is the storage that is used to keep track of the attachments of transactions. - attachments *memstorage.IndexedStorage[iotago.SlotIndex, iotago.BlockID, *TransactionMetadata] + attachments *memstorage.IndexedStorage[iotago.SlotIndex, iotago.BlockID, *SignedTransactionMetadata] // cachedTransactions holds the transactions that are currently in the MemPool. cachedTransactions *shrinkingmap.ShrinkingMap[iotago.TransactionID, *TransactionMetadata] + // cachedSignedTransactions holds the signed transactions that are currently in the MemPool. + cachedSignedTransactions *shrinkingmap.ShrinkingMap[iotago.SignedTransactionID, *SignedTransactionMetadata] + // cachedStateRequests holds the requests for states that are required to execute transactions. - cachedStateRequests *shrinkingmap.ShrinkingMap[iotago.Identifier, *promise.Promise[mempool.State]] + cachedStateRequests *shrinkingmap.ShrinkingMap[mempool.StateID, *promise.Promise[*StateMetadata]] // stateDiffs holds aggregated state mutations for each slot. stateDiffs *shrinkingmap.ShrinkingMap[iotago.SlotIndex, *StateDiff] // conflictDAG is the DAG that is used to keep track of the conflicts between transactions. - conflictDAG conflictdag.ConflictDAG[iotago.TransactionID, iotago.OutputID, VoteRank] + conflictDAG conflictdag.ConflictDAG[iotago.TransactionID, mempool.StateID, VoteRank] errorHandler func(error) @@ -61,49 +70,63 @@ type MemPool[VoteRank conflictdag.VoteRankType[VoteRank]] struct { } // New is the constructor of the MemPool. -func New[VoteRank conflictdag.VoteRankType[VoteRank]](vm mempool.VM, inputResolver mempool.StateReferenceResolver, workers *workerpool.Group, conflictDAG conflictdag.ConflictDAG[iotago.TransactionID, iotago.OutputID, VoteRank], apiProvider iotago.APIProvider, errorHandler func(error), opts ...options.Option[MemPool[VoteRank]]) *MemPool[VoteRank] { +func New[VoteRank conflictdag.VoteRankType[VoteRank]]( + transactionValidator mempool.TransactionValidator, + transactionExecutor mempool.TransactionExecutor, + transactionInputReferenceRetriever mempool.TransactionInputReferenceRetriever, + stateResolver mempool.StateReferenceResolver, + workers *workerpool.Group, + conflictDAG conflictdag.ConflictDAG[iotago.TransactionID, mempool.StateID, VoteRank], + apiProvider iotago.APIProvider, + errorHandler func(error), + opts ...options.Option[MemPool[VoteRank]], +) *MemPool[VoteRank] { return options.Apply(&MemPool[VoteRank]{ - transactionAttached: event.New1[mempool.TransactionMetadata](), - executeStateTransition: vm, - resolveState: inputResolver, - attachments: memstorage.NewIndexedStorage[iotago.SlotIndex, iotago.BlockID, *TransactionMetadata](), - cachedTransactions: shrinkingmap.New[iotago.TransactionID, *TransactionMetadata](), - cachedStateRequests: shrinkingmap.New[iotago.Identifier, *promise.Promise[mempool.State]](), - stateDiffs: shrinkingmap.New[iotago.SlotIndex, *StateDiff](), - executionWorkers: workers.CreatePool("executionWorkers", 1), - conflictDAG: conflictDAG, - apiProvider: apiProvider, - errorHandler: errorHandler, + signedTransactionAttached: event.New1[mempool.SignedTransactionMetadata](), + transactionAttached: event.New1[mempool.TransactionMetadata](), + validateSignedTransaction: transactionValidator, + executeStateTransition: transactionExecutor, + inputsOfTransaction: transactionInputReferenceRetriever, + resolveState: stateResolver, + attachments: memstorage.NewIndexedStorage[iotago.SlotIndex, iotago.BlockID, *SignedTransactionMetadata](), + cachedTransactions: shrinkingmap.New[iotago.TransactionID, *TransactionMetadata](), + cachedSignedTransactions: shrinkingmap.New[iotago.SignedTransactionID, *SignedTransactionMetadata](), + cachedStateRequests: shrinkingmap.New[mempool.StateID, *promise.Promise[*StateMetadata]](), + stateDiffs: shrinkingmap.New[iotago.SlotIndex, *StateDiff](), + executionWorkers: workers.CreatePool("executionWorkers", 1), + conflictDAG: conflictDAG, + apiProvider: apiProvider, + errorHandler: errorHandler, }, opts, (*MemPool[VoteRank]).setup) } -// AttachTransaction adds a transaction to the MemPool that was attached by the given block. -func (m *MemPool[VoteRank]) AttachTransaction(transaction mempool.Transaction, blockID iotago.BlockID) (metadata mempool.TransactionMetadata, err error) { - storedTransaction, isNew, err := m.storeTransaction(transaction, blockID) +// AttachSignedTransaction adds a transaction to the MemPool that was attached by the given block. +func (m *MemPool[VoteRank]) AttachSignedTransaction(signedTransaction mempool.SignedTransaction, transaction mempool.Transaction, blockID iotago.BlockID) (signedTransactionMetadata mempool.SignedTransactionMetadata, err error) { + storedSignedTransaction, isNewSignedTransaction, isNewTransaction, err := m.storeTransaction(signedTransaction, transaction, blockID) if err != nil { - return nil, ierrors.Wrap(err, "failed to store transaction") + return nil, ierrors.Wrap(err, "failed to store signedTransaction") } - if isNew { - m.transactionAttached.Trigger(storedTransaction) + if isNewSignedTransaction { + m.signedTransactionAttached.Trigger(storedSignedTransaction) + + if isNewTransaction { + m.transactionAttached.Trigger(storedSignedTransaction.transactionMetadata) + + m.solidifyInputs(storedSignedTransaction.transactionMetadata) + } - m.solidifyInputs(storedTransaction) } - return storedTransaction, nil + return storedSignedTransaction, nil } -func (m *MemPool[VoteRank]) OnTransactionAttached(handler func(transaction mempool.TransactionMetadata), opts ...event.Option) { - m.transactionAttached.Hook(handler, opts...) +func (m *MemPool[VoteRank]) OnSignedTransactionAttached(handler func(signedTransactionMetadata mempool.SignedTransactionMetadata), opts ...event.Option) { + m.signedTransactionAttached.Hook(handler, opts...) } -// MarkAttachmentOrphaned marks the attachment of the given block as orphaned. -func (m *MemPool[VoteRank]) MarkAttachmentOrphaned(blockID iotago.BlockID) bool { - if attachmentSlot := m.attachments.Get(blockID.Slot(), false); attachmentSlot != nil { - defer attachmentSlot.Delete(blockID) - } - - return m.updateAttachment(blockID, (*TransactionMetadata).markAttachmentOrphaned) +func (m *MemPool[VoteRank]) OnTransactionAttached(handler func(transaction mempool.TransactionMetadata), opts ...event.Option) { + m.transactionAttached.Hook(handler, opts...) } // MarkAttachmentIncluded marks the attachment of the given block as included. @@ -116,34 +139,24 @@ func (m *MemPool[VoteRank]) TransactionMetadata(id iotago.TransactionID) (transa return m.cachedTransactions.Get(id) } -// OutputStateMetadata returns the metadata of the output state with the given ID. -func (m *MemPool[VoteRank]) OutputStateMetadata(stateReference *iotago.UTXOInput) (state mempool.OutputStateMetadata, err error) { +// StateMetadata returns the metadata of the output state with the given ID. +func (m *MemPool[VoteRank]) StateMetadata(stateReference iotago.Input) (state mempool.StateMetadata, err error) { stateRequest, exists := m.cachedStateRequests.Get(stateReference.StateID()) + // create a new request that does not wait for missing states if !exists || !stateRequest.WasCompleted() { stateRequest = m.requestState(stateReference) + stateRequest.WaitComplete() } - stateRequest.OnSuccess(func(loadedState mempool.State) { - switch loadedState.Type() { - case iotago.InputUTXO: - //nolint:forcetypeassert // we can safely assume that this is an OutputStateMetadata - state = loadedState.(mempool.OutputStateMetadata) - default: - // as we are waiting for this Promise completion, we can assign the outer err - err = ierrors.Errorf("invalid state type, only UTXO states can return metadata") - } - }) - stateRequest.OnError(func(requestErr error) { err = ierrors.Errorf("failed to request state: %w", requestErr) }) - stateRequest.WaitComplete() - - return state, err + return stateRequest.Result(), stateRequest.Err() } -// PublishCommitmentState publishes the given commitment state to the MemPool. -func (m *MemPool[VoteRank]) PublishCommitmentState(commitment *iotago.Commitment) { - if stateRequest, exists := m.cachedStateRequests.Get(commitment.StateID()); exists { - stateRequest.Resolve(commitment) +// InjectRequestedState allows to inject a requested state into the MemPool that is provided on the fly instead of being +// provided by the ledger. +func (m *MemPool[VoteRank]) InjectRequestedState(state mempool.State) { + if stateRequest, exists := m.cachedStateRequests.Get(state.StateID()); exists { + stateRequest.Resolve(NewStateMetadata(state)) } } @@ -163,7 +176,7 @@ func (m *MemPool[VoteRank]) StateDiff(slot iotago.SlotIndex) mempool.StateDiff { // Evict evicts the slot with the given slot from the MemPool. func (m *MemPool[VoteRank]) Evict(slot iotago.SlotIndex) { - if evictedAttachments := func() *shrinkingmap.ShrinkingMap[iotago.BlockID, *TransactionMetadata] { + if evictedAttachments := func() *shrinkingmap.ShrinkingMap[iotago.BlockID, *SignedTransactionMetadata] { m.evictionMutex.Lock() defer m.evictionMutex.Unlock() @@ -173,75 +186,72 @@ func (m *MemPool[VoteRank]) Evict(slot iotago.SlotIndex) { return m.attachments.Evict(slot) }(); evictedAttachments != nil { - evictedAttachments.ForEach(func(blockID iotago.BlockID, transaction *TransactionMetadata) bool { - transaction.evictAttachment(blockID) - + evictedAttachments.ForEach(func(blockID iotago.BlockID, signedTransactionMetadata *SignedTransactionMetadata) bool { + signedTransactionMetadata.evictAttachment(blockID) return true }) } } -func (m *MemPool[VoteRank]) storeTransaction(transaction mempool.Transaction, blockID iotago.BlockID) (storedTransaction *TransactionMetadata, isNew bool, err error) { +func (m *MemPool[VoteRank]) storeTransaction(signedTransaction mempool.SignedTransaction, transaction mempool.Transaction, blockID iotago.BlockID) (storedSignedTransaction *SignedTransactionMetadata, isNewSignedTransaction, isNewTransaction bool, err error) { m.evictionMutex.RLock() defer m.evictionMutex.RUnlock() if m.lastEvictedSlot >= blockID.Slot() { // block will be retained as invalid, we do not store tx failure as it was block's fault - return nil, false, ierrors.Errorf("blockID %d is older than last evicted slot %d", blockID.Slot(), m.lastEvictedSlot) + return nil, false, false, ierrors.Errorf("blockID %d is older than last evicted slot %d", blockID.Slot(), m.lastEvictedSlot) + } + + inputReferences, err := m.inputsOfTransaction(transaction) + if err != nil { + return nil, false, false, ierrors.Wrap(err, "failed to get input references of transaction") } - newTransaction, err := NewTransactionWithMetadata(transaction) + newTransaction, err := NewTransactionMetadata(transaction, inputReferences) if err != nil { - return nil, false, ierrors.Errorf("failed to create transaction metadata: %w", err) + return nil, false, false, ierrors.Errorf("failed to create transaction metadata: %w", err) } - storedTransaction, isNew = m.cachedTransactions.GetOrCreate(newTransaction.ID(), func() *TransactionMetadata { return newTransaction }) - if isNew { + storedTransaction, isNewTransaction := m.cachedTransactions.GetOrCreate(newTransaction.ID(), func() *TransactionMetadata { return newTransaction }) + if isNewTransaction { m.setupTransaction(storedTransaction) } - storedTransaction.addAttachment(blockID) - m.attachments.Get(blockID.Slot(), true).Set(blockID, storedTransaction) + newSignedTransaction, err := NewSignedTransactionMetadata(signedTransaction, storedTransaction) + if err != nil { + return nil, false, false, ierrors.Errorf("failed to create signedTransaction metadata: %w", err) + } + + storedSignedTransaction, isNewSignedTransaction = m.cachedSignedTransactions.GetOrCreate(lo.PanicOnErr(signedTransaction.ID()), func() *SignedTransactionMetadata { return newSignedTransaction }) + if isNewSignedTransaction { + m.setupSignedTransaction(storedSignedTransaction, storedTransaction) + } + + storedSignedTransaction.addAttachment(blockID) + m.attachments.Get(blockID.Slot(), true).Set(blockID, storedSignedTransaction) - return storedTransaction, isNew, nil + return storedSignedTransaction, isNewSignedTransaction, isNewTransaction, nil } func (m *MemPool[VoteRank]) solidifyInputs(transaction *TransactionMetadata) { for i, inputReference := range transaction.inputReferences { - stateReference, outputIndex := inputReference, i + stateReference, index := inputReference, i - request, created := m.cachedStateRequests.GetOrCreate(stateReference.StateID(), func() *promise.Promise[mempool.State] { + request, created := m.cachedStateRequests.GetOrCreate(stateReference.StateID(), func() *promise.Promise[*StateMetadata] { return m.requestState(stateReference, true) }) - request.OnSuccess(func(state mempool.State) { - switch state.Type() { - case iotago.InputUTXO: - //nolint:forcetypeassert // we can safely assume that this is an OutputStateMetadata - outputStateMetadata := state.(*OutputStateMetadata) - - transaction.publishInput(outputIndex, outputStateMetadata) - - if created { - m.setupOutputState(outputStateMetadata) - } - case iotago.InputCommitment: - //nolint:forcetypeassert // we can safely assume that this is an ContextStateMetadata - contextStateMetadata := state.(*ContextStateMetadata) - - transaction.publishCommitmentInput(contextStateMetadata) + request.OnSuccess(func(inputState *StateMetadata) { + transaction.publishInput(index, inputState) - if created { - contextStateMetadata.onAllSpendersRemoved(func() { m.cachedStateRequests.Delete(contextStateMetadata.StateID()) }) - } - case iotago.InputBlockIssuanceCredit, iotago.InputReward: - default: - panic(ierrors.New("invalid state type resolved")) + if created { + m.setupOutputState(inputState) } - // an input has been successfully resolved, decrease the unsolid input counter and check solidity. if transaction.markInputSolid() { - m.executeTransaction(transaction) + transaction.executionContext.OnUpdate(func(_, executionContext context.Context) { + m.executeTransaction(executionContext, transaction) + }) } }) @@ -249,14 +259,9 @@ func (m *MemPool[VoteRank]) solidifyInputs(transaction *TransactionMetadata) { } } -func (m *MemPool[VoteRank]) executeTransaction(transaction *TransactionMetadata) { +func (m *MemPool[VoteRank]) executeTransaction(executionContext context.Context, transaction *TransactionMetadata) { m.executionWorkers.Submit(func() { - var timeReference mempool.ContextState - if commitmentStateMetadata, ok := transaction.CommitmentInput().(*ContextStateMetadata); ok && commitmentStateMetadata != nil { - timeReference = commitmentStateMetadata.State() - } - - if outputStates, err := m.executeStateTransition(context.Background(), transaction.Transaction(), lo.Map(transaction.utxoInputs, (*OutputStateMetadata).State), timeReference); err != nil { + if outputStates, err := m.executeStateTransition(executionContext, transaction.Transaction()); err != nil { transaction.setInvalid(err) } else { transaction.setExecuted(outputStates) @@ -268,11 +273,11 @@ func (m *MemPool[VoteRank]) executeTransaction(transaction *TransactionMetadata) func (m *MemPool[VoteRank]) bookTransaction(transaction *TransactionMetadata) { if m.optForkAllTransactions { - m.forkTransaction(transaction, ds.NewSet(lo.Map(transaction.utxoInputs, (*OutputStateMetadata).OutputID)...)) + m.forkTransaction(transaction, ds.NewSet(lo.Map(transaction.inputs, (*StateMetadata).StateID)...)) } else { - lo.ForEach(transaction.utxoInputs, func(input *OutputStateMetadata) { + lo.ForEach(transaction.inputs, func(input *StateMetadata) { input.OnDoubleSpent(func() { - m.forkTransaction(transaction, ds.NewSet(input.OutputID())) + m.forkTransaction(transaction, ds.NewSet(input.StateID())) }) }) } @@ -282,11 +287,11 @@ func (m *MemPool[VoteRank]) bookTransaction(transaction *TransactionMetadata) { } } -func (m *MemPool[VoteRank]) forkTransaction(transaction *TransactionMetadata, resourceIDs ds.Set[iotago.OutputID]) { - transaction.setConflicting() +func (m *MemPool[VoteRank]) forkTransaction(transactionMetadata *TransactionMetadata, resourceIDs ds.Set[mempool.StateID]) { + transactionMetadata.conflicting.Trigger() - if err := m.conflictDAG.UpdateConflictingResources(transaction.ID(), resourceIDs); err != nil { - transaction.setOrphaned() + if err := m.conflictDAG.UpdateConflictingResources(transactionMetadata.ID(), resourceIDs); err != nil { + transactionMetadata.orphaned.Trigger() m.errorHandler(err) } @@ -294,7 +299,7 @@ func (m *MemPool[VoteRank]) forkTransaction(transaction *TransactionMetadata, re func (m *MemPool[VoteRank]) publishOutputStates(transaction *TransactionMetadata) { for _, output := range transaction.outputs { - stateRequest, isNew := m.cachedStateRequests.GetOrCreate(output.StateID(), lo.NoVariadic(promise.New[mempool.State])) + stateRequest, isNew := m.cachedStateRequests.GetOrCreate(output.StateID(), lo.NoVariadic(promise.New[*StateMetadata])) stateRequest.Resolve(output) if isNew { @@ -303,31 +308,18 @@ func (m *MemPool[VoteRank]) publishOutputStates(transaction *TransactionMetadata } } -func (m *MemPool[VoteRank]) requestState(stateRef iotago.Input, waitIfMissing ...bool) *promise.Promise[mempool.State] { - return promise.New(func(p *promise.Promise[mempool.State]) { +func (m *MemPool[VoteRank]) requestState(stateRef iotago.Input, waitIfMissing ...bool) *promise.Promise[*StateMetadata] { + return promise.New(func(p *promise.Promise[*StateMetadata]) { request := m.resolveState(stateRef) request.OnSuccess(func(state mempool.State) { - switch state.Type() { - case iotago.InputUTXO: - // The output was resolved from the ledger, meaning it was actually persisted as it was accepted and - // committed: otherwise we would have found it in cache or the request would have never resolved. - //nolint:forcetypeassert // we can safely assume that this is an OutputState - outputStateMetadata := NewOutputStateMetadata(state.(mempool.OutputState)) - outputStateMetadata.setAccepted() - outputStateMetadata.setCommitted() - - p.Resolve(outputStateMetadata) - case iotago.InputCommitment: - //nolint:forcetypeassert // we can safely assume that this is an ContextState - commitmentStateMetadata := NewContextStateMetadata(state.(mempool.ContextState)) - - p.Resolve(commitmentStateMetadata) - case iotago.InputBlockIssuanceCredit, iotago.InputReward: - p.Resolve(state) - default: - p.Reject(ierrors.Errorf("unsupported input type %s", stateRef.Type())) - } + // The output was resolved from the ledger, meaning it was actually persisted as it was accepted and + // committed: otherwise we would have found it in cache or the request would have never resolved. + outputStateMetadata := NewStateMetadata(state) + outputStateMetadata.accepted.Set(true) + outputStateMetadata.committed.Trigger() + + p.Resolve(outputStateMetadata) }) request.OnError(func(err error) { @@ -354,7 +346,9 @@ func (m *MemPool[VoteRank]) updateAttachment(blockID iotago.BlockID, updateFunc func (m *MemPool[VoteRank]) transactionByAttachment(blockID iotago.BlockID) (*TransactionMetadata, bool) { if attachmentsInSlot := m.attachments.Get(blockID.Slot()); attachmentsInSlot != nil { - return attachmentsInSlot.Get(blockID) + if signedTransactionMetadata, exists := attachmentsInSlot.Get(blockID); exists { + return signedTransactionMetadata.transactionMetadata, true + } } return nil, false @@ -402,7 +396,7 @@ func (m *MemPool[VoteRank]) stateDiff(slot iotago.SlotIndex) (stateDiff *StateDi func (m *MemPool[VoteRank]) setupTransaction(transaction *TransactionMetadata) { transaction.OnAccepted(func() { - if slot := transaction.EarliestIncludedAttachment().Slot(); slot > 0 { + if slot := transaction.EarliestIncludedAttachment().Slot(); slot != 0 { if stateDiff, evicted := m.stateDiff(slot); !evicted { if err := stateDiff.AddTransaction(transaction, m.errorHandler); err != nil { m.errorHandler(ierrors.Wrapf(err, "failed to add transaction to state diff, txID: %s", transaction.ID())) @@ -435,7 +429,7 @@ func (m *MemPool[VoteRank]) setupTransaction(transaction *TransactionMetadata) { transaction.OnEvicted(func() { if m.cachedTransactions.Delete(transaction.ID()) { - transaction.attachments.ForEach(func(blockID iotago.BlockID, _ bool) bool { + transaction.validAttachments.ForEach(func(blockID iotago.BlockID, _ bool) bool { if slotAttachments := m.attachments.Get(blockID.Slot(), false); slotAttachments != nil { slotAttachments.Delete(blockID) } @@ -443,10 +437,12 @@ func (m *MemPool[VoteRank]) setupTransaction(transaction *TransactionMetadata) { return true }) } + + transaction.signingTransactions.Range((*SignedTransactionMetadata).setEvicted) }) } -func (m *MemPool[VoteRank]) setupOutputState(state *OutputStateMetadata) { +func (m *MemPool[VoteRank]) setupOutputState(state *StateMetadata) { state.OnCommitted(func() { if !m.cachedStateRequests.Delete(state.StateID(), state.HasNoSpenders) && m.cachedStateRequests.Has(state.StateID()) { state.onAllSpendersRemoved(func() { m.cachedStateRequests.Delete(state.StateID(), state.HasNoSpenders) }) @@ -458,6 +454,31 @@ func (m *MemPool[VoteRank]) setupOutputState(state *OutputStateMetadata) { }) } +func (m *MemPool[VoteRank]) setupSignedTransaction(signedTransactionMetadata *SignedTransactionMetadata, transaction *TransactionMetadata) { + transaction.addSigningTransaction(signedTransactionMetadata) + + transaction.OnSolid(func() { + executionContext, err := m.validateSignedTransaction(signedTransactionMetadata.SignedTransaction(), lo.Map(signedTransactionMetadata.transactionMetadata.inputs, (*StateMetadata).State)) + if err != nil { + _ = signedTransactionMetadata.signaturesInvalid.Set(err) + return + } + + signedTransactionMetadata.attachments.OnUpdate(func(mutations ds.SetMutations[iotago.BlockID]) { + mutations.AddedElements().Range(lo.Void(transaction.addValidAttachment)) + mutations.DeletedElements().Range(transaction.evictValidAttachment) + }) + + signedTransactionMetadata.signaturesValid.Trigger() + + transaction.executionContext.Set(executionContext) + }) + + signedTransactionMetadata.evicted.OnTrigger(func() { + m.cachedSignedTransactions.Delete(signedTransactionMetadata.ID()) + }) +} + func WithForkAllTransactions[VoteRank conflictdag.VoteRankType[VoteRank]](forkAllTransactions bool) options.Option[MemPool[VoteRank]] { return func(m *MemPool[VoteRank]) { m.optForkAllTransactions = forkAllTransactions diff --git a/pkg/protocol/engine/mempool/v1/mempool_test.go b/pkg/protocol/engine/mempool/v1/mempool_test.go index f7f71b086..bb955f1a3 100644 --- a/pkg/protocol/engine/mempool/v1/mempool_test.go +++ b/pkg/protocol/engine/mempool/v1/mempool_test.go @@ -35,9 +35,11 @@ func TestMempoolV1_ResourceCleanup(t *testing.T) { workers := workerpool.NewGroup(t.Name()) ledgerState := ledgertests.New(ledgertests.NewMockedState(iotago.TransactionID{}, 0)) - conflictDAG := conflictdagv1.New[iotago.TransactionID, iotago.OutputID, vote.MockedRank](func() int { return 0 }) - mempoolInstance := New[vote.MockedRank](mempooltests.VM, func(reference iotago.Input) *promise.Promise[mempool.State] { - return ledgerState.ResolveOutputState(reference.(*iotago.UTXOInput).OutputID()) + conflictDAG := conflictdagv1.New[iotago.TransactionID, mempool.StateID, vote.MockedRank](func() int { return 0 }) + mempoolInstance := New[vote.MockedRank](mempooltests.TransactionValidator, mempooltests.TransactionExecutor, func(transaction mempool.Transaction) ([]iotago.Input, error) { + return transaction.(*mempooltests.Transaction).Inputs() + }, func(reference iotago.Input) *promise.Promise[mempool.State] { + return ledgerState.ResolveOutputState(reference.StateID()) }, workers, conflictDAG, api.SingleVersionProvider(tpkg.TestAPI), func(error) {}) tf := mempooltests.NewTestFramework(t, mempoolInstance, conflictDAG, ledgerState, workers) @@ -45,11 +47,12 @@ func TestMempoolV1_ResourceCleanup(t *testing.T) { issueTransactions := func(startIndex, transactionCount int, prevStateAlias string) (int, string) { index := startIndex for ; index < startIndex+transactionCount; index++ { + signedTxAlias := fmt.Sprintf("tx%d-signed", index) txAlias := fmt.Sprintf("tx%d", index) blockAlias := fmt.Sprintf("block%d", index) - tf.CreateTransaction(txAlias, []string{prevStateAlias}, 2) + tf.CreateSignedTransaction(txAlias, []string{prevStateAlias}, 2) - require.NoError(t, tf.AttachTransaction(txAlias, blockAlias, iotago.SlotIndex(index))) + require.NoError(t, tf.AttachTransaction(signedTxAlias, txAlias, blockAlias, iotago.SlotIndex(index))) tf.RequireBooked(txAlias) tf.MarkAttachmentIncluded(blockAlias) @@ -83,7 +86,7 @@ func TestMempoolV1_ResourceCleanup(t *testing.T) { require.Equal(t, 0, mempoolInstance.cachedStateRequests.Size()) attachmentsSlotCount := 0 - mempoolInstance.attachments.ForEach(func(index iotago.SlotIndex, storage *shrinkingmap.ShrinkingMap[iotago.BlockID, *TransactionMetadata]) { + mempoolInstance.attachments.ForEach(func(index iotago.SlotIndex, storage *shrinkingmap.ShrinkingMap[iotago.BlockID, *SignedTransactionMetadata]) { attachmentsSlotCount++ }) @@ -102,10 +105,12 @@ func newTestFramework(t *testing.T) *mempooltests.TestFramework { workers := workerpool.NewGroup(t.Name()) ledgerState := ledgertests.New(ledgertests.NewMockedState(iotago.TransactionID{}, 0)) - conflictDAG := conflictdagv1.New[iotago.TransactionID, iotago.OutputID, vote.MockedRank](account.NewAccounts().SelectCommittee().SeatCount) + conflictDAG := conflictdagv1.New[iotago.TransactionID, mempool.StateID, vote.MockedRank](account.NewAccounts().SelectCommittee().SeatCount) - return mempooltests.NewTestFramework(t, New[vote.MockedRank](mempooltests.VM, func(reference iotago.Input) *promise.Promise[mempool.State] { - return ledgerState.ResolveOutputState(reference.(*iotago.UTXOInput).OutputID()) + return mempooltests.NewTestFramework(t, New[vote.MockedRank](mempooltests.TransactionValidator, mempooltests.TransactionExecutor, func(transaction mempool.Transaction) ([]iotago.Input, error) { + return transaction.(*mempooltests.Transaction).Inputs() + }, func(reference iotago.Input) *promise.Promise[mempool.State] { + return ledgerState.ResolveOutputState(reference.StateID()) }, workers, conflictDAG, api.SingleVersionProvider(tpkg.TestAPI), func(error) {}), conflictDAG, ledgerState, workers) } @@ -113,9 +118,11 @@ func newForkingTestFramework(t *testing.T) *mempooltests.TestFramework { workers := workerpool.NewGroup(t.Name()) ledgerState := ledgertests.New(ledgertests.NewMockedState(iotago.TransactionID{}, 0)) - conflictDAG := conflictdagv1.New[iotago.TransactionID, iotago.OutputID, vote.MockedRank](account.NewAccounts().SelectCommittee().SeatCount) + conflictDAG := conflictdagv1.New[iotago.TransactionID, mempool.StateID, vote.MockedRank](account.NewAccounts().SelectCommittee().SeatCount) - return mempooltests.NewTestFramework(t, New[vote.MockedRank](mempooltests.VM, func(reference iotago.Input) *promise.Promise[mempool.State] { - return ledgerState.ResolveOutputState(reference.(*iotago.UTXOInput).OutputID()) + return mempooltests.NewTestFramework(t, New[vote.MockedRank](mempooltests.TransactionValidator, mempooltests.TransactionExecutor, func(transaction mempool.Transaction) ([]iotago.Input, error) { + return transaction.(*mempooltests.Transaction).Inputs() + }, func(reference iotago.Input) *promise.Promise[mempool.State] { + return ledgerState.ResolveOutputState(reference.StateID()) }, workers, conflictDAG, api.SingleVersionProvider(tpkg.TestAPI), func(error) {}, WithForkAllTransactions[vote.MockedRank](true)), conflictDAG, ledgerState, workers) } diff --git a/pkg/protocol/engine/mempool/v1/signed_transaction_metadata.go b/pkg/protocol/engine/mempool/v1/signed_transaction_metadata.go new file mode 100644 index 000000000..cc582843f --- /dev/null +++ b/pkg/protocol/engine/mempool/v1/signed_transaction_metadata.go @@ -0,0 +1,91 @@ +package mempoolv1 + +import ( + "github.com/iotaledger/hive.go/ds/reactive" + "github.com/iotaledger/hive.go/ierrors" + "github.com/iotaledger/hive.go/runtime/syncutils" + "github.com/iotaledger/iota-core/pkg/protocol/engine/mempool" + iotago "github.com/iotaledger/iota.go/v4" +) + +type SignedTransactionMetadata struct { + id iotago.SignedTransactionID + signedTransaction mempool.SignedTransaction + transactionMetadata *TransactionMetadata + attachments reactive.Set[iotago.BlockID] + attachmentsMutex syncutils.RWMutex + signaturesInvalid reactive.Variable[error] + signaturesValid reactive.Event + evicted reactive.Event +} + +func NewSignedTransactionMetadata(signedTransaction mempool.SignedTransaction, transactionMetadata *TransactionMetadata) (*SignedTransactionMetadata, error) { + signedID, signedIDErr := signedTransaction.ID() + if signedIDErr != nil { + return nil, ierrors.Errorf("failed to retrieve signed transaction ID: %w", signedIDErr) + } + + return &SignedTransactionMetadata{ + id: signedID, + signedTransaction: signedTransaction, + transactionMetadata: transactionMetadata, + attachments: reactive.NewSet[iotago.BlockID](), + signaturesInvalid: reactive.NewVariable[error](), + signaturesValid: reactive.NewEvent(), + evicted: reactive.NewEvent(), + }, nil +} + +func (s *SignedTransactionMetadata) ID() iotago.SignedTransactionID { + return s.id +} + +func (s *SignedTransactionMetadata) SignedTransaction() mempool.SignedTransaction { + return s.signedTransaction +} + +func (s *SignedTransactionMetadata) TransactionMetadata() mempool.TransactionMetadata { + return s.transactionMetadata +} + +func (s *SignedTransactionMetadata) OnSignaturesInvalid(callback func(error)) (unsubscribe func()) { + return s.signaturesInvalid.OnUpdate(func(_, err error) { + callback(err) + }) +} + +func (s *SignedTransactionMetadata) OnSignaturesValid(callback func()) (unsubscribe func()) { + return s.signaturesValid.OnTrigger(callback) +} + +func (s *SignedTransactionMetadata) IsEvicted() bool { + return s.evicted.WasTriggered() +} + +func (s *SignedTransactionMetadata) OnEvicted(callback func()) { + s.evicted.OnTrigger(callback) +} + +func (s *SignedTransactionMetadata) setEvicted() { + s.evicted.Trigger() +} + +func (s *SignedTransactionMetadata) Attachments() []iotago.BlockID { + return s.attachments.ToSlice() +} + +func (s *SignedTransactionMetadata) addAttachment(blockID iotago.BlockID) (added bool) { + s.attachmentsMutex.Lock() + defer s.attachmentsMutex.Unlock() + + return s.attachments.Add(blockID) +} + +func (s *SignedTransactionMetadata) evictAttachment(id iotago.BlockID) { + s.attachmentsMutex.Lock() + defer s.attachmentsMutex.Unlock() + + if s.attachments.Delete(id) && s.attachments.IsEmpty() { + s.evicted.Trigger() + } +} diff --git a/pkg/protocol/engine/mempool/v1/state_diff.go b/pkg/protocol/engine/mempool/v1/state_diff.go index ce7544e8e..b152f489d 100644 --- a/pkg/protocol/engine/mempool/v1/state_diff.go +++ b/pkg/protocol/engine/mempool/v1/state_diff.go @@ -13,13 +13,13 @@ import ( type StateDiff struct { slot iotago.SlotIndex - spentOutputs *shrinkingmap.ShrinkingMap[iotago.OutputID, mempool.OutputStateMetadata] + spentOutputs *shrinkingmap.ShrinkingMap[mempool.StateID, mempool.StateMetadata] - createdOutputs *shrinkingmap.ShrinkingMap[iotago.OutputID, mempool.OutputStateMetadata] + createdOutputs *shrinkingmap.ShrinkingMap[mempool.StateID, mempool.StateMetadata] executedTransactions *orderedmap.OrderedMap[iotago.TransactionID, mempool.TransactionMetadata] - stateUsageCounters *shrinkingmap.ShrinkingMap[iotago.OutputID, int] + stateUsageCounters *shrinkingmap.ShrinkingMap[mempool.StateID, int] mutations ads.Set[iotago.TransactionID] } @@ -27,11 +27,11 @@ type StateDiff struct { func NewStateDiff(slot iotago.SlotIndex) *StateDiff { return &StateDiff{ slot: slot, - spentOutputs: shrinkingmap.New[iotago.OutputID, mempool.OutputStateMetadata](), - createdOutputs: shrinkingmap.New[iotago.OutputID, mempool.OutputStateMetadata](), + spentOutputs: shrinkingmap.New[mempool.StateID, mempool.StateMetadata](), + createdOutputs: shrinkingmap.New[mempool.StateID, mempool.StateMetadata](), executedTransactions: orderedmap.New[iotago.TransactionID, mempool.TransactionMetadata](), - stateUsageCounters: shrinkingmap.New[iotago.OutputID, int](), - mutations: ads.NewSet(mapdb.NewMapDB(), iotago.TransactionID.Bytes, iotago.SlotIdentifierFromBytes), + stateUsageCounters: shrinkingmap.New[mempool.StateID, int](), + mutations: ads.NewSet(mapdb.NewMapDB(), iotago.TransactionID.Bytes, iotago.TransactionIDFromBytes), } } @@ -39,11 +39,11 @@ func (s *StateDiff) Slot() iotago.SlotIndex { return s.slot } -func (s *StateDiff) DestroyedStates() *shrinkingmap.ShrinkingMap[iotago.OutputID, mempool.OutputStateMetadata] { +func (s *StateDiff) DestroyedStates() *shrinkingmap.ShrinkingMap[mempool.StateID, mempool.StateMetadata] { return s.spentOutputs } -func (s *StateDiff) CreatedStates() *shrinkingmap.ShrinkingMap[iotago.OutputID, mempool.OutputStateMetadata] { +func (s *StateDiff) CreatedStates() *shrinkingmap.ShrinkingMap[mempool.StateID, mempool.StateMetadata] { return s.createdOutputs } @@ -56,14 +56,14 @@ func (s *StateDiff) Mutations() ads.Set[iotago.TransactionID] { } func (s *StateDiff) updateCompactedStateChanges(transaction *TransactionMetadata, direction int) { - transaction.Inputs().Range(func(input mempool.OutputStateMetadata) { - s.compactStateChanges(input, s.stateUsageCounters.Compute(input.OutputID(), func(currentValue int, _ bool) int { + transaction.Inputs().Range(func(input mempool.StateMetadata) { + s.compactStateChanges(input, s.stateUsageCounters.Compute(input.StateID(), func(currentValue int, _ bool) int { return currentValue - direction })) }) - transaction.Outputs().Range(func(output mempool.OutputStateMetadata) { - s.compactStateChanges(output, s.stateUsageCounters.Compute(output.OutputID(), func(currentValue int, _ bool) int { + transaction.Outputs().Range(func(output mempool.StateMetadata) { + s.compactStateChanges(output, s.stateUsageCounters.Compute(output.StateID(), func(currentValue int, _ bool) int { return currentValue + direction })) }) @@ -97,15 +97,19 @@ func (s *StateDiff) RollbackTransaction(transaction *TransactionMetadata) error return nil } -func (s *StateDiff) compactStateChanges(output mempool.OutputStateMetadata, newValue int) { +func (s *StateDiff) compactStateChanges(output mempool.StateMetadata, newValue int) { + if output.State().Type() != iotago.InputUTXO { + return + } + switch { case newValue > 0: - s.createdOutputs.Set(output.OutputID(), output) + s.createdOutputs.Set(output.StateID(), output) case newValue < 0: - s.spentOutputs.Set(output.OutputID(), output) + s.spentOutputs.Set(output.StateID(), output) default: - s.createdOutputs.Delete(output.OutputID()) - s.spentOutputs.Delete(output.OutputID()) + s.createdOutputs.Delete(output.StateID()) + s.spentOutputs.Delete(output.StateID()) } } diff --git a/pkg/protocol/engine/mempool/v1/output_state_metadata.go b/pkg/protocol/engine/mempool/v1/state_metadata.go similarity index 58% rename from pkg/protocol/engine/mempool/v1/output_state_metadata.go rename to pkg/protocol/engine/mempool/v1/state_metadata.go index 01c2c83d8..67a3f6aca 100644 --- a/pkg/protocol/engine/mempool/v1/output_state_metadata.go +++ b/pkg/protocol/engine/mempool/v1/state_metadata.go @@ -10,9 +10,8 @@ import ( iotago "github.com/iotaledger/iota.go/v4" ) -type OutputStateMetadata struct { - outputID iotago.OutputID - state mempool.OutputState +type StateMetadata struct { + state mempool.State // lifecycle spenderCount uint64 @@ -27,10 +26,9 @@ type OutputStateMetadata struct { *inclusionFlags } -func NewOutputStateMetadata(state mempool.OutputState, optSource ...*TransactionMetadata) *OutputStateMetadata { - return (&OutputStateMetadata{ - outputID: state.OutputID(), - state: state, +func NewStateMetadata(state mempool.State, optSource ...*TransactionMetadata) *StateMetadata { + return (&StateMetadata{ + state: state, spent: promise.NewEvent(), doubleSpent: promise.NewEvent(), @@ -44,7 +42,7 @@ func NewOutputStateMetadata(state mempool.OutputState, optSource ...*Transaction }).setup(optSource...) } -func (s *OutputStateMetadata) setup(optSource ...*TransactionMetadata) *OutputStateMetadata { +func (s *StateMetadata) setup(optSource ...*TransactionMetadata) *StateMetadata { if len(optSource) == 0 { return s } @@ -52,50 +50,46 @@ func (s *OutputStateMetadata) setup(optSource ...*TransactionMetadata) *OutputSt s.conflictIDs.InheritFrom(source.conflictIDs) - source.OnPending(s.setPending) - source.OnAccepted(s.setAccepted) - source.OnRejected(s.setRejected) - source.OnCommitted(s.setCommitted) - source.OnOrphaned(s.setOrphaned) + source.OnPending(func() { s.accepted.Set(false) }) + source.OnAccepted(func() { s.accepted.Set(true) }) + source.OnRejected(func() { s.rejected.Trigger() }) + source.OnCommitted(func() { s.committed.Trigger() }) + source.OnOrphaned(func() { s.orphaned.Trigger() }) return s } -func (s *OutputStateMetadata) StateID() iotago.Identifier { +func (s *StateMetadata) StateID() mempool.StateID { return s.state.StateID() } -func (s *OutputStateMetadata) Type() iotago.StateType { +func (s *StateMetadata) Type() iotago.StateType { return iotago.InputUTXO } -func (s *OutputStateMetadata) OutputID() iotago.OutputID { - return s.outputID -} - -func (s *OutputStateMetadata) State() mempool.OutputState { +func (s *StateMetadata) State() mempool.State { return s.state } -func (s *OutputStateMetadata) ConflictIDs() reactive.Set[iotago.TransactionID] { +func (s *StateMetadata) ConflictIDs() reactive.Set[iotago.TransactionID] { return s.conflictIDs } -func (s *OutputStateMetadata) IsDoubleSpent() bool { +func (s *StateMetadata) IsDoubleSpent() bool { return s.doubleSpent.WasTriggered() } -func (s *OutputStateMetadata) OnDoubleSpent(callback func()) { +func (s *StateMetadata) OnDoubleSpent(callback func()) { s.doubleSpent.OnTrigger(callback) } -func (s *OutputStateMetadata) AcceptedSpender() (mempool.TransactionMetadata, bool) { +func (s *StateMetadata) AcceptedSpender() (mempool.TransactionMetadata, bool) { acceptedSpender := s.spendAccepted.Get() return acceptedSpender, acceptedSpender != nil } -func (s *OutputStateMetadata) OnAcceptedSpenderUpdated(callback func(spender mempool.TransactionMetadata)) { +func (s *StateMetadata) OnAcceptedSpenderUpdated(callback func(spender mempool.TransactionMetadata)) { s.spendAccepted.OnUpdate(func(prevValue, newValue *TransactionMetadata) { if prevValue != newValue { callback(newValue) @@ -103,7 +97,7 @@ func (s *OutputStateMetadata) OnAcceptedSpenderUpdated(callback func(spender mem }) } -func (s *OutputStateMetadata) OnSpendCommitted(callback func(spender mempool.TransactionMetadata)) { +func (s *StateMetadata) OnSpendCommitted(callback func(spender mempool.TransactionMetadata)) { s.spendCommitted.OnUpdate(func(prevValue, newValue *TransactionMetadata) { if prevValue != newValue { callback(newValue) @@ -111,23 +105,23 @@ func (s *OutputStateMetadata) OnSpendCommitted(callback func(spender mempool.Tra }) } -func (s *OutputStateMetadata) AllSpendersRemoved() bool { +func (s *StateMetadata) AllSpendersRemoved() bool { return s.allSpendersRemoved.WasTriggered() } -func (s *OutputStateMetadata) onAllSpendersRemoved(callback func()) (unsubscribe func()) { +func (s *StateMetadata) onAllSpendersRemoved(callback func()) (unsubscribe func()) { return s.allSpendersRemoved.Hook(callback).Unhook } -func (s *OutputStateMetadata) PendingSpenderCount() int { +func (s *StateMetadata) PendingSpenderCount() int { return int(atomic.LoadUint64(&s.spenderCount)) } -func (s *OutputStateMetadata) HasNoSpenders() bool { +func (s *StateMetadata) HasNoSpenders() bool { return atomic.LoadUint64(&s.spenderCount) == 0 } -func (s *OutputStateMetadata) increaseSpenderCount() { +func (s *StateMetadata) increaseSpenderCount() { if spenderCount := atomic.AddUint64(&s.spenderCount, 1); spenderCount == 1 { s.spent.Trigger() } else if spenderCount == 2 { @@ -135,13 +129,13 @@ func (s *OutputStateMetadata) increaseSpenderCount() { } } -func (s *OutputStateMetadata) decreaseSpenderCount() { +func (s *StateMetadata) decreaseSpenderCount() { if atomic.AddUint64(&s.spenderCount, ^uint64(0)) == 0 { s.allSpendersRemoved.Trigger() } } -func (s *OutputStateMetadata) setupSpender(spender *TransactionMetadata) { +func (s *StateMetadata) setupSpender(spender *TransactionMetadata) { s.increaseSpenderCount() spender.OnAccepted(func() { diff --git a/pkg/protocol/engine/mempool/v1/transaction_metadata.go b/pkg/protocol/engine/mempool/v1/transaction_metadata.go index 9c98fdad9..98e968577 100644 --- a/pkg/protocol/engine/mempool/v1/transaction_metadata.go +++ b/pkg/protocol/engine/mempool/v1/transaction_metadata.go @@ -1,6 +1,7 @@ package mempoolv1 import ( + "context" "sync/atomic" "github.com/iotaledger/hive.go/ds" @@ -8,7 +9,6 @@ import ( "github.com/iotaledger/hive.go/ds/shrinkingmap" "github.com/iotaledger/hive.go/ierrors" "github.com/iotaledger/hive.go/lo" - "github.com/iotaledger/hive.go/runtime/promise" "github.com/iotaledger/hive.go/runtime/syncutils" "github.com/iotaledger/iota-core/pkg/protocol/engine/mempool" iotago "github.com/iotaledger/iota.go/v4" @@ -17,87 +17,79 @@ import ( type TransactionMetadata struct { id iotago.TransactionID inputReferences []iotago.Input - utxoInputs []*OutputStateMetadata - commitmentInput *ContextStateMetadata - outputs []*OutputStateMetadata + inputs []*StateMetadata + outputs []*StateMetadata transaction mempool.Transaction parentConflictIDs reactive.DerivedSet[iotago.TransactionID] conflictIDs reactive.DerivedSet[iotago.TransactionID] // lifecycle events unsolidInputsCount uint64 - solid *promise.Event - executed *promise.Event - invalid *promise.Event1[error] - booked *promise.Event - evicted *promise.Event + solid reactive.Event + executionContext reactive.Variable[context.Context] + executed reactive.Event + invalid reactive.Variable[error] + booked reactive.Event + evicted reactive.Event // predecessors for acceptance unacceptedInputsCount uint64 allInputsAccepted reactive.Variable[bool] - conflicting *promise.Event - conflictAccepted *promise.Event + conflicting reactive.Event + conflictAccepted reactive.Event // attachments - attachments *shrinkingmap.ShrinkingMap[iotago.BlockID, bool] - earliestIncludedAttachment reactive.Variable[iotago.BlockID] - allAttachmentsEvicted *promise.Event + signingTransactions reactive.Set[*SignedTransactionMetadata] + allSigningTransactionsEvicted reactive.Event - // mutex needed? - mutex syncutils.RWMutex + validAttachments *shrinkingmap.ShrinkingMap[iotago.BlockID, bool] + earliestIncludedValidAttachment reactive.Variable[iotago.BlockID] + allValidAttachmentsEvicted reactive.Event + // mutex needed? + mutex syncutils.RWMutex attachmentsMutex syncutils.RWMutex *inclusionFlags } -func NewTransactionWithMetadata(transaction mempool.Transaction) (*TransactionMetadata, error) { +func (t *TransactionMetadata) ValidAttachments() []iotago.BlockID { + return t.validAttachments.Keys() +} + +func NewTransactionMetadata(transaction mempool.Transaction, referencedInputs []iotago.Input) (*TransactionMetadata, error) { transactionID, transactionIDErr := transaction.ID() if transactionIDErr != nil { return nil, ierrors.Errorf("failed to retrieve transaction ID: %w", transactionIDErr) } - utxoInputReferences, inputsErr := transaction.Inputs() - if inputsErr != nil { - return nil, ierrors.Join(iotago.ErrUnknownInputType, ierrors.Wrapf(inputsErr, "failed to retrieve inputReferences of transaction %s", transactionID)) - } - - contextInputReferences, contextInputsErr := transaction.ContextInputs() - if contextInputsErr != nil { - return nil, ierrors.Wrapf(contextInputsErr, "failed to retrieve contextInputReferences of transaction %s", transactionID) - } - - inputReferences := make([]iotago.Input, 0, len(utxoInputReferences)+len(contextInputReferences)) - for _, utxoInput := range utxoInputReferences { - inputReferences = append(inputReferences, utxoInput) - } - for _, contextInput := range contextInputReferences { - inputReferences = append(inputReferences, contextInput) - } - return (&TransactionMetadata{ id: transactionID, - inputReferences: inputReferences, - utxoInputs: make([]*OutputStateMetadata, len(utxoInputReferences)), + inputReferences: referencedInputs, + inputs: make([]*StateMetadata, len(referencedInputs)), transaction: transaction, parentConflictIDs: reactive.NewDerivedSet[iotago.TransactionID](), conflictIDs: reactive.NewDerivedSet[iotago.TransactionID](), - unsolidInputsCount: uint64(len(utxoInputReferences) + len(contextInputReferences)), - booked: promise.NewEvent(), - solid: promise.NewEvent(), - executed: promise.NewEvent(), - invalid: promise.NewEvent1[error](), - evicted: promise.NewEvent(), + unsolidInputsCount: uint64(len(referencedInputs)), + booked: reactive.NewEvent(), + solid: reactive.NewEvent(), + executionContext: reactive.NewVariable[context.Context](), + executed: reactive.NewEvent(), + invalid: reactive.NewVariable[error](), + evicted: reactive.NewEvent(), - unacceptedInputsCount: uint64(len(utxoInputReferences)), + unacceptedInputsCount: uint64(len(referencedInputs)), allInputsAccepted: reactive.NewVariable[bool](), - conflicting: promise.NewEvent(), - conflictAccepted: promise.NewEvent(), + conflicting: reactive.NewEvent(), + conflictAccepted: reactive.NewEvent(), - attachments: shrinkingmap.New[iotago.BlockID, bool](), - earliestIncludedAttachment: reactive.NewVariable[iotago.BlockID](), - allAttachmentsEvicted: promise.NewEvent(), + allSigningTransactionsEvicted: reactive.NewEvent(), + signingTransactions: reactive.NewSet[*SignedTransactionMetadata](), + + validAttachments: shrinkingmap.New[iotago.BlockID, bool](), + earliestIncludedValidAttachment: reactive.NewVariable[iotago.BlockID](), + allValidAttachmentsEvicted: reactive.NewEvent(), inclusionFlags: newInclusionFlags(), }).setup(), nil @@ -111,30 +103,23 @@ func (t *TransactionMetadata) Transaction() mempool.Transaction { return t.transaction } -func (t *TransactionMetadata) Inputs() ds.Set[mempool.OutputStateMetadata] { +func (t *TransactionMetadata) Inputs() ds.Set[mempool.StateMetadata] { t.mutex.RLock() defer t.mutex.RUnlock() - inputs := ds.NewSet[mempool.OutputStateMetadata]() - for _, input := range t.utxoInputs { + inputs := ds.NewSet[mempool.StateMetadata]() + for _, input := range t.inputs { inputs.Add(input) } return inputs } -func (t *TransactionMetadata) CommitmentInput() mempool.ContextStateMetadata { +func (t *TransactionMetadata) Outputs() ds.Set[mempool.StateMetadata] { t.mutex.RLock() defer t.mutex.RUnlock() - return t.commitmentInput -} - -func (t *TransactionMetadata) Outputs() ds.Set[mempool.OutputStateMetadata] { - t.mutex.RLock() - defer t.mutex.RUnlock() - - outputs := ds.NewSet[mempool.OutputStateMetadata]() + outputs := ds.NewSet[mempool.StateMetadata]() for _, output := range t.outputs { outputs.Add(output) } @@ -146,26 +131,17 @@ func (t *TransactionMetadata) ConflictIDs() reactive.Set[iotago.TransactionID] { return t.conflictIDs } -func (t *TransactionMetadata) publishInput(index int, input *OutputStateMetadata) { - t.utxoInputs[index] = input +func (t *TransactionMetadata) publishInput(index int, input *StateMetadata) { + t.inputs[index] = input input.setupSpender(t) t.setupInput(input) } -func (t *TransactionMetadata) publishCommitmentInput(commitment *ContextStateMetadata) { - t.mutex.Lock() - defer t.mutex.Unlock() - - t.commitmentInput = commitment - - commitment.setupSpender(t) -} - -func (t *TransactionMetadata) setExecuted(outputStates []mempool.OutputState) { +func (t *TransactionMetadata) setExecuted(outputStates []mempool.State) { t.mutex.Lock() for _, outputState := range outputStates { - t.outputs = append(t.outputs, NewOutputStateMetadata(outputState, t)) + t.outputs = append(t.outputs, NewStateMetadata(outputState, t)) } t.mutex.Unlock() @@ -189,11 +165,13 @@ func (t *TransactionMetadata) OnExecuted(callback func()) { } func (t *TransactionMetadata) IsInvalid() bool { - return t.invalid.WasTriggered() + return t.invalid.Get() != nil } func (t *TransactionMetadata) OnInvalid(callback func(error)) { - t.invalid.OnTrigger(callback) + t.invalid.OnUpdate(func(oldValue, newValue error) { + callback(newValue) + }) } func (t *TransactionMetadata) IsBooked() bool { @@ -225,7 +203,7 @@ func (t *TransactionMetadata) setBooked() bool { } func (t *TransactionMetadata) setInvalid(reason error) { - t.invalid.Trigger(reason) + _ = t.invalid.Set(reason) } func (t *TransactionMetadata) markInputSolid() (allInputsSolid bool) { @@ -237,7 +215,7 @@ func (t *TransactionMetadata) markInputSolid() (allInputsSolid bool) { } func (t *TransactionMetadata) Commit() { - t.setCommitted() + t.committed.Trigger() } func (t *TransactionMetadata) IsConflicting() bool { @@ -260,29 +238,24 @@ func (t *TransactionMetadata) AllInputsAccepted() bool { return t.allInputsAccepted.Get() } -func (t *TransactionMetadata) setConflicting() { - t.conflicting.Trigger() -} - func (t *TransactionMetadata) setConflictAccepted() { if t.conflictAccepted.Trigger() { if t.AllInputsAccepted() && t.EarliestIncludedAttachment().Slot() != 0 { - t.setAccepted() + t.accepted.Set(true) } } } -func (t *TransactionMetadata) setupInput(input *OutputStateMetadata) { +func (t *TransactionMetadata) setupInput(input *StateMetadata) { t.parentConflictIDs.InheritFrom(input.conflictIDs) - input.OnRejected(t.setRejected) - input.OnOrphaned(t.setOrphaned) - + input.OnRejected(func() { t.rejected.Trigger() }) + input.OnOrphaned(func() { t.orphaned.Trigger() }) input.OnAccepted(func() { if atomic.AddUint64(&t.unacceptedInputsCount, ^uint64(0)) == 0 { if wereAllInputsAccepted := t.allInputsAccepted.Set(true); !wereAllInputsAccepted { if t.IsConflictAccepted() && t.EarliestIncludedAttachment().Slot() != 0 { - t.setAccepted() + t.accepted.Set(true) } } } @@ -290,19 +263,19 @@ func (t *TransactionMetadata) setupInput(input *OutputStateMetadata) { input.OnPending(func() { if atomic.AddUint64(&t.unacceptedInputsCount, 1) == 1 && t.allInputsAccepted.Set(false) { - t.setPending() + t.accepted.Set(false) } }) input.OnAcceptedSpenderUpdated(func(spender mempool.TransactionMetadata) { if spender != t { - t.setRejected() + t.rejected.Trigger() } }) input.OnSpendCommitted(func(spender mempool.TransactionMetadata) { if spender != t { - t.setOrphaned() + t.orphaned.Trigger() } }) } @@ -316,19 +289,15 @@ func (t *TransactionMetadata) setup() (self *TransactionMetadata) { t.conflictIDs.Replace(ds.NewSet(t.id)) }) - t.allAttachmentsEvicted.OnTrigger(func() { + t.allSigningTransactionsEvicted.OnTrigger(func() { if !t.IsCommitted() { - t.setOrphaned() + t.orphaned.Trigger() } }) t.OnEarliestIncludedAttachmentUpdated(func(previousIndex, newIndex iotago.BlockID) { if isIncluded, wasIncluded := newIndex.Slot() != 0, previousIndex.Slot() != 0; isIncluded != wasIncluded { - if !isIncluded { - t.setPending() - } else if t.AllInputsAccepted() && t.IsConflictAccepted() { - t.setAccepted() - } + t.accepted.Set(isIncluded && t.AllInputsAccepted() && t.IsConflictAccepted()) } }) @@ -338,72 +307,60 @@ func (t *TransactionMetadata) setup() (self *TransactionMetadata) { return t } -func (t *TransactionMetadata) addAttachment(blockID iotago.BlockID) (added bool) { +func (t *TransactionMetadata) addSigningTransaction(signedTransactionMetadata *SignedTransactionMetadata) (added bool) { t.attachmentsMutex.Lock() defer t.attachmentsMutex.Unlock() - return lo.Return2(t.attachments.GetOrCreate(blockID, func() bool { return false })) -} - -func (t *TransactionMetadata) markAttachmentIncluded(blockID iotago.BlockID) (included bool) { - t.attachmentsMutex.Lock() - defer t.attachmentsMutex.Unlock() - - t.attachments.Set(blockID, true) - - if lowestSlotIndex := t.earliestIncludedAttachment.Get().Slot(); lowestSlotIndex == 0 || blockID.Slot() < lowestSlotIndex { - t.earliestIncludedAttachment.Set(blockID) + if added = t.signingTransactions.Add(signedTransactionMetadata); added { + signedTransactionMetadata.OnEvicted(func() { + t.evictSigningTransaction(signedTransactionMetadata) + }) } - return true + return added } -func (t *TransactionMetadata) markAttachmentOrphaned(blockID iotago.BlockID) (orphaned bool) { +func (t *TransactionMetadata) markAttachmentIncluded(blockID iotago.BlockID) (included bool) { t.attachmentsMutex.Lock() defer t.attachmentsMutex.Unlock() - previousState, exists := t.attachments.Get(blockID) - if !exists { - return false - } + t.validAttachments.Set(blockID, true) - t.evictAttachment(blockID) - - if previousState && blockID == t.earliestIncludedAttachment.Get() { - t.earliestIncludedAttachment.Set(t.findLowestIncludedAttachment()) + if lowestSlotIndex := t.earliestIncludedValidAttachment.Get().Slot(); lowestSlotIndex == 0 || blockID.Slot() < lowestSlotIndex { + t.earliestIncludedValidAttachment.Set(blockID) } return true } -func (t *TransactionMetadata) Attachments() []iotago.BlockID { - return t.attachments.Keys() -} - func (t *TransactionMetadata) EarliestIncludedAttachment() iotago.BlockID { - return t.earliestIncludedAttachment.Get() + return t.earliestIncludedValidAttachment.Get() } func (t *TransactionMetadata) OnEarliestIncludedAttachmentUpdated(callback func(prevBlock, newBlock iotago.BlockID)) { - t.earliestIncludedAttachment.OnUpdate(callback) + t.earliestIncludedValidAttachment.OnUpdate(callback) } -func (t *TransactionMetadata) evictAttachment(id iotago.BlockID) { - if t.attachments.Delete(id) && t.attachments.IsEmpty() { - t.allAttachmentsEvicted.Trigger() - } -} +func (t *TransactionMetadata) addValidAttachment(blockID iotago.BlockID) (added bool) { + t.attachmentsMutex.Lock() + defer t.attachmentsMutex.Unlock() -func (t *TransactionMetadata) findLowestIncludedAttachment() iotago.BlockID { - var lowestIncludedBlock iotago.BlockID + return lo.Return2(t.validAttachments.GetOrCreate(blockID, func() bool { + return false + })) +} - t.attachments.ForEach(func(blockID iotago.BlockID, included bool) bool { - if included && (lowestIncludedBlock.Slot() == 0 || blockID.Slot() < lowestIncludedBlock.Slot()) { - lowestIncludedBlock = blockID - } +func (t *TransactionMetadata) evictValidAttachment(id iotago.BlockID) { + t.attachmentsMutex.Lock() + defer t.attachmentsMutex.Unlock() - return true - }) + if t.validAttachments.Delete(id) && t.validAttachments.IsEmpty() { + t.allValidAttachmentsEvicted.Trigger() + } +} - return lowestIncludedBlock +func (t *TransactionMetadata) evictSigningTransaction(signedTransactionMetadata *SignedTransactionMetadata) { + if t.signingTransactions.Delete(signedTransactionMetadata) && t.signingTransactions.IsEmpty() { + t.allSigningTransactionsEvicted.Trigger() + } } diff --git a/pkg/protocol/engine/mempool/v1/transaction_metadata_test.go b/pkg/protocol/engine/mempool/v1/transaction_metadata_test.go index 4702b9ec5..dae4107c4 100644 --- a/pkg/protocol/engine/mempool/v1/transaction_metadata_test.go +++ b/pkg/protocol/engine/mempool/v1/transaction_metadata_test.go @@ -15,43 +15,25 @@ func TestAttachments(t *testing.T) { "2": iotago.SlotIdentifierRepresentingData(2, []byte("block2")), } - attachments, err := NewTransactionWithMetadata(mempooltests.NewTransaction(2)) + transactionMetadata, err := NewTransactionMetadata(mempooltests.NewTransaction(2), []iotago.Input{}) require.NoError(t, err) - require.True(t, attachments.addAttachment(blockIDs["1"])) - require.True(t, attachments.addAttachment(blockIDs["2"])) - require.False(t, attachments.addAttachment(blockIDs["1"])) + signedTransactionMetadata, err := NewSignedTransactionMetadata(mempooltests.NewSignedTransaction(transactionMetadata.Transaction()), transactionMetadata) - var earliestInclusionIndex, earliestInclusionIndex1, earliestInclusionIndex2 iotago.SlotIndex + require.True(t, signedTransactionMetadata.addAttachment(blockIDs["1"])) + require.True(t, signedTransactionMetadata.addAttachment(blockIDs["2"])) - attachments.OnEarliestIncludedAttachmentUpdated(func(_, includedBlock iotago.BlockID) { + require.False(t, signedTransactionMetadata.addAttachment(blockIDs["1"])) + + var earliestInclusionIndex iotago.SlotIndex + + signedTransactionMetadata.transactionMetadata.OnEarliestIncludedAttachmentUpdated(func(_, includedBlock iotago.BlockID) { earliestInclusionIndex = includedBlock.Slot() }) require.Equal(t, iotago.SlotIndex(0), earliestInclusionIndex) - attachments.markAttachmentIncluded(blockIDs["2"]) + signedTransactionMetadata.transactionMetadata.markAttachmentIncluded(blockIDs["2"]) require.Equal(t, iotago.SlotIndex(2), earliestInclusionIndex) - attachments.markAttachmentIncluded(blockIDs["1"]) + signedTransactionMetadata.transactionMetadata.markAttachmentIncluded(blockIDs["1"]) require.Equal(t, iotago.SlotIndex(1), earliestInclusionIndex) - - attachments.OnEarliestIncludedAttachmentUpdated(func(_, includedBlock iotago.BlockID) { - earliestInclusionIndex1 = includedBlock.Slot() - }) - - require.True(t, attachments.markAttachmentOrphaned(blockIDs["1"])) - require.Equal(t, iotago.SlotIndex(2), earliestInclusionIndex) - require.Equal(t, iotago.SlotIndex(2), earliestInclusionIndex1) - - require.False(t, attachments.markAttachmentOrphaned(blockIDs["1"])) - require.Equal(t, iotago.SlotIndex(2), earliestInclusionIndex) - require.Equal(t, iotago.SlotIndex(2), earliestInclusionIndex1) - - attachments.markAttachmentOrphaned(blockIDs["2"]) - require.Equal(t, iotago.SlotIndex(0), earliestInclusionIndex) - require.Equal(t, iotago.SlotIndex(0), earliestInclusionIndex1) - - attachments.OnEarliestIncludedAttachmentUpdated(func(_, includedBlock iotago.BlockID) { - earliestInclusionIndex2 = includedBlock.Slot() - }) - require.Equal(t, iotago.SlotIndex(0), earliestInclusionIndex2) } diff --git a/pkg/protocol/engine/mempool/vm.go b/pkg/protocol/engine/mempool/vm.go index e2e5e417b..0d05fb49c 100644 --- a/pkg/protocol/engine/mempool/vm.go +++ b/pkg/protocol/engine/mempool/vm.go @@ -4,4 +4,6 @@ import ( "context" ) -type VM func(ctx context.Context, stateTransition Transaction, inputs []OutputState, timeReference ContextState) (outputs []OutputState, err error) +type TransactionValidator func(signedTransaction SignedTransaction, resolvedInputs []State) (executionContext context.Context, err error) + +type TransactionExecutor func(executionContext context.Context, transaction Transaction) (outputs []State, err error) diff --git a/pkg/protocol/engine/tipselection/v1/test_framework_test.go b/pkg/protocol/engine/tipselection/v1/test_framework_test.go index 8eeb61f02..1e42f8cf6 100644 --- a/pkg/protocol/engine/tipselection/v1/test_framework_test.go +++ b/pkg/protocol/engine/tipselection/v1/test_framework_test.go @@ -49,7 +49,7 @@ func NewTestFramework(test *testing.T, opts ...options.Option[TestFramework]) *T t.Instance = tipselectionv1.New().Construct( t.TipManager.Instance, - conflictdagv1.New[iotago.TransactionID, iotago.OutputID, ledger.BlockVoteRank](t.CommitteeSize), + conflictdagv1.New[iotago.TransactionID, mempool.StateID, ledger.BlockVoteRank](t.CommitteeSize), transactionMetadataRetriever, rootBlocksRetriever, t.expectedLivenessDuration, diff --git a/pkg/protocol/engine/tipselection/v1/tip_selection.go b/pkg/protocol/engine/tipselection/v1/tip_selection.go index c1c2977f6..ee7b7fad4 100644 --- a/pkg/protocol/engine/tipselection/v1/tip_selection.go +++ b/pkg/protocol/engine/tipselection/v1/tip_selection.go @@ -25,7 +25,7 @@ type TipSelection struct { tipManager tipmanager.TipManager // conflictDAG is the ConflictDAG that is used to track conflicts. - conflictDAG conflictdag.ConflictDAG[iotago.TransactionID, iotago.OutputID, ledger.BlockVoteRank] + conflictDAG conflictdag.ConflictDAG[iotago.TransactionID, mempool.StateID, ledger.BlockVoteRank] // rootBlocks is a function that returns the current root blocks. rootBlocks func() iotago.BlockIDs @@ -76,7 +76,7 @@ func New(opts ...options.Option[TipSelection]) *TipSelection { // // This method is separated from the constructor so the TipSelection can be initialized lazily after all dependencies // are available. -func (t *TipSelection) Construct(tipManager tipmanager.TipManager, conflictDAG conflictdag.ConflictDAG[iotago.TransactionID, iotago.OutputID, ledger.BlockVoteRank], transactionMetadataRetriever func(iotago.TransactionID) (mempool.TransactionMetadata, bool), rootBlocksRetriever func() iotago.BlockIDs, livenessThresholdFunc func(tipmanager.TipMetadata) time.Duration) *TipSelection { +func (t *TipSelection) Construct(tipManager tipmanager.TipManager, conflictDAG conflictdag.ConflictDAG[iotago.TransactionID, mempool.StateID, ledger.BlockVoteRank], transactionMetadataRetriever func(iotago.TransactionID) (mempool.TransactionMetadata, bool), rootBlocksRetriever func() iotago.BlockIDs, livenessThresholdFunc func(tipmanager.TipMetadata) time.Duration) *TipSelection { t.tipManager = tipManager t.conflictDAG = conflictDAG t.transactionMetadata = transactionMetadataRetriever @@ -101,7 +101,7 @@ func (t *TipSelection) SelectTips(amount int) (references model.ParentReferences references = make(model.ParentReferences) strongParents := ds.NewSet[iotago.BlockID]() shallowLikesParents := ds.NewSet[iotago.BlockID]() - _ = t.conflictDAG.ReadConsistent(func(_ conflictdag.ReadLockedConflictDAG[iotago.TransactionID, iotago.OutputID, ledger.BlockVoteRank]) error { + _ = t.conflictDAG.ReadConsistent(func(_ conflictdag.ReadLockedConflictDAG[iotago.TransactionID, mempool.StateID, ledger.BlockVoteRank]) error { previousLikedInsteadConflicts := ds.NewSet[iotago.TransactionID]() if t.collectReferences(references, iotago.StrongParentType, t.tipManager.StrongTips, func(tip tipmanager.TipMetadata) { @@ -170,7 +170,7 @@ func (t *TipSelection) likedInsteadReferences(likedConflicts ds.Set[iotago.Trans return ierrors.Errorf("transaction required for liked instead reference (%s) not found in mem-pool", likedConflictID) } - necessaryReferences[likedConflictID] = lo.First(transactionMetadata.Attachments()) + necessaryReferences[likedConflictID] = lo.First(transactionMetadata.ValidAttachments()) return nil }); err != nil { diff --git a/pkg/protocol/snapshotcreator/snapshotcreator.go b/pkg/protocol/snapshotcreator/snapshotcreator.go index db8e77948..e470bed35 100644 --- a/pkg/protocol/snapshotcreator/snapshotcreator.go +++ b/pkg/protocol/snapshotcreator/snapshotcreator.go @@ -47,7 +47,7 @@ const ( GenesisTransactionCreationSlot = 0 ) -var GenesisTransactionID = iotago.TransactionIDFromData(GenesisTransactionCreationSlot, []byte("genesis")) +var GenesisTransactionID = iotago.TransactionIDRepresentingData(GenesisTransactionCreationSlot, []byte("genesis")) func CreateSnapshot(opts ...options.Option[Options]) error { opt := NewOptions(opts...) diff --git a/pkg/retainer/retainer/retainer.go b/pkg/retainer/retainer/retainer.go index c5c33c9e7..29d9bc309 100644 --- a/pkg/retainer/retainer/retainer.go +++ b/pkg/retainer/retainer/retainer.go @@ -83,43 +83,51 @@ func NewProvider() module.Provider[*engine.Engine, retainer.Retainer] { }) e.HookInitialized(func() { - e.Ledger.OnTransactionAttached(func(transactionMetadata mempool.TransactionMetadata) { + e.Ledger.MemPool().OnSignedTransactionAttached(func(signedTransactionMetadata mempool.SignedTransactionMetadata) { + attachment := signedTransactionMetadata.Attachments()[0] - // transaction is not included yet, thus EarliestIncludedAttachment is not set. - if err := r.onTransactionAttached(transactionMetadata.Attachments()[0]); err != nil { - r.errorHandler(ierrors.Wrap(err, "failed to store on TransactionAttached in retainer")) - } - - transactionMetadata.OnConflicting(func() { - // transaction is not included yet, thus EarliestIncludedAttachment is not set. - r.RetainTransactionFailure(transactionMetadata.Attachments()[0], iotago.ErrTxConflicting) + signedTransactionMetadata.OnSignaturesInvalid(func(err error) { + r.RetainTransactionFailure(attachment, err) }) - transactionMetadata.OnInvalid(func(err error) { + signedTransactionMetadata.OnSignaturesValid(func() { + transactionMetadata := signedTransactionMetadata.TransactionMetadata() + // transaction is not included yet, thus EarliestIncludedAttachment is not set. - r.RetainTransactionFailure(transactionMetadata.Attachments()[0], err) - }) + if err := r.onTransactionAttached(attachment); err != nil { + r.errorHandler(ierrors.Wrap(err, "failed to store on TransactionAttached in retainer")) + } - transactionMetadata.OnAccepted(func() { - attachmentID := transactionMetadata.EarliestIncludedAttachment() - if slot := attachmentID.Slot(); slot > 0 { - if err := r.onTransactionAccepted(attachmentID); err != nil { - r.errorHandler(ierrors.Wrap(err, "failed to store on TransactionAccepted in retainer")) + transactionMetadata.OnConflicting(func() { + // transaction is not included yet, thus EarliestIncludedAttachment is not set. + r.RetainTransactionFailure(attachment, iotago.ErrTxConflicting) + }) + + transactionMetadata.OnInvalid(func(err error) { + // transaction is not included yet, thus EarliestIncludedAttachment is not set. + r.RetainTransactionFailure(attachment, err) + }) + + transactionMetadata.OnAccepted(func() { + attachmentID := transactionMetadata.EarliestIncludedAttachment() + if slot := attachmentID.Slot(); slot > 0 { + if err := r.onTransactionAccepted(attachmentID); err != nil { + r.errorHandler(ierrors.Wrap(err, "failed to store on TransactionAccepted in retainer")) + } } - } - }) + }) - transactionMetadata.OnEarliestIncludedAttachmentUpdated(func(prevBlock, newBlock iotago.BlockID) { - // if prevBlock is genesis, we do not need to update anything, bc the tx is included in the block we attached to at start. - if prevBlock.Slot() == 0 { - return - } + transactionMetadata.OnEarliestIncludedAttachmentUpdated(func(prevBlock, newBlock iotago.BlockID) { + // if prevBlock is genesis, we do not need to update anything, bc the tx is included in the block we attached to at start. + if prevBlock.Slot() == 0 { + return + } - if err := r.onAttachmentUpdated(prevBlock, newBlock, transactionMetadata.IsAccepted()); err != nil { - r.errorHandler(ierrors.Wrap(err, "failed to delete/store on AttachmentUpdated in retainer")) - } + if err := r.onAttachmentUpdated(prevBlock, newBlock, transactionMetadata.IsAccepted()); err != nil { + r.errorHandler(ierrors.Wrap(err, "failed to delete/store on AttachmentUpdated in retainer")) + } + }) }) - }) }) diff --git a/pkg/tests/accounts_test.go b/pkg/tests/accounts_test.go index 23585ccec..3ed88b77d 100644 --- a/pkg/tests/accounts_test.go +++ b/pkg/tests/accounts_test.go @@ -70,7 +70,7 @@ func Test_TransitionAccount(t *testing.T) { ) consumedInputs, equalOutputs, equalWallets := ts.TransactionFramework.CreateBasicOutputsEqually(4, "Genesis:0") - tx1 := lo.PanicOnErr(ts.TransactionFramework.CreateTransactionWithOptions("TX1", append(accountWallets, equalWallets...), + tx1 := lo.PanicOnErr(ts.TransactionFramework.CreateSignedTransactionWithOptions("TX1", append(accountWallets, equalWallets...), testsuite.WithAccountInput(accountInput, true), testsuite.WithInputs(consumedInputs), testsuite.WithContextInputs(iotago.TxEssenceContextInputs{ @@ -102,7 +102,7 @@ func Test_TransitionAccount(t *testing.T) { PreviousUpdatedTime: 0, PreviousExpirySlot: 1, NewExpirySlot: 1, - NewOutputID: iotago.OutputIDFromTransactionIDAndIndex(lo.PanicOnErr(ts.TransactionFramework.SignedTransaction("TX1").ID()), 0), + NewOutputID: iotago.OutputIDFromTransactionIDAndIndex(ts.TransactionFramework.TransactionID("TX1"), 0), PreviousOutputID: genesisAccount.OutputID(), BlockIssuerKeysRemoved: iotago.NewBlockIssuerKeys(), BlockIssuerKeysAdded: iotago.NewBlockIssuerKeys(newGenesisOutputKey), @@ -111,7 +111,7 @@ func Test_TransitionAccount(t *testing.T) { ts.AssertAccountData(&accounts.AccountData{ ID: genesisAccountOutput.AccountID, Credits: accounts.NewBlockIssuanceCredits(iotago.BlockIssuanceCredits(123), 0), - OutputID: iotago.OutputIDFromTransactionIDAndIndex(lo.PanicOnErr(ts.TransactionFramework.SignedTransaction("TX1").ID()), 0), + OutputID: iotago.OutputIDFromTransactionIDAndIndex(ts.TransactionFramework.TransactionID("TX1"), 0), BlockIssuerKeys: iotago.NewBlockIssuerKeys(oldGenesisOutputKey, newGenesisOutputKey), ExpirySlot: 1, }, ts.Nodes()...) @@ -135,7 +135,7 @@ func Test_TransitionAccount(t *testing.T) { block2Slot := latestParent.ID().Slot() - tx2 := lo.PanicOnErr(ts.TransactionFramework.CreateTransactionWithOptions("TX2", append(newAccountWallets, destroyWallets...), + tx2 := lo.PanicOnErr(ts.TransactionFramework.CreateSignedTransactionWithOptions("TX2", append(newAccountWallets, destroyWallets...), testsuite.WithContextInputs(iotago.TxEssenceContextInputs{ &iotago.BlockIssuanceCreditInput{ AccountID: genesisAccountOutput.AccountID, @@ -161,7 +161,7 @@ func Test_TransitionAccount(t *testing.T) { NewExpirySlot: 0, PreviousExpirySlot: 1, NewOutputID: iotago.EmptyOutputID, - PreviousOutputID: iotago.OutputIDFromTransactionIDAndIndex(lo.PanicOnErr(ts.TransactionFramework.SignedTransaction("TX1").ID()), 0), + PreviousOutputID: iotago.OutputIDFromTransactionIDAndIndex(lo.PanicOnErr(ts.TransactionFramework.Transaction("TX1").ID()), 0), BlockIssuerKeysAdded: iotago.NewBlockIssuerKeys(), BlockIssuerKeysRemoved: iotago.NewBlockIssuerKeys(oldGenesisOutputKey, newGenesisOutputKey), ValidatorStakeChange: 0, @@ -209,7 +209,7 @@ func Test_TransitionAccount(t *testing.T) { block3Slot := latestParent.ID().Slot() - tx3 := lo.PanicOnErr(ts.TransactionFramework.CreateTransactionWithOptions("TX3", newDelegationWallets, + tx3 := lo.PanicOnErr(ts.TransactionFramework.CreateSignedTransactionWithOptions("TX3", newDelegationWallets, testsuite.WithContextInputs(iotago.TxEssenceContextInputs{ &iotago.CommitmentInput{ CommitmentID: node1.Protocol.MainEngineInstance().Storage.Settings().LatestCommitment().Commitment().MustID(), @@ -253,7 +253,7 @@ func Test_TransitionAccount(t *testing.T) { // transition a delegation output to a delayed claiming state inputForDelegationTransition, delegationTransitionOutputs, delegationTransitionWallets := ts.TransactionFramework.DelayedClaimingTransition("TX3:0", 0) - tx4 := lo.PanicOnErr(ts.TransactionFramework.CreateTransactionWithOptions("TX4", delegationTransitionWallets, + tx4 := lo.PanicOnErr(ts.TransactionFramework.CreateSignedTransactionWithOptions("TX4", delegationTransitionWallets, testsuite.WithContextInputs(iotago.TxEssenceContextInputs{ &iotago.CommitmentInput{ CommitmentID: node1.Protocol.MainEngineInstance().Storage.Settings().LatestCommitment().Commitment().MustID(), @@ -299,7 +299,7 @@ func Test_TransitionAccount(t *testing.T) { // CREATE IMPLICIT ACCOUNT FROM BASIC UTXO inputForImplicitAccount, outputsForImplicitAccount, implicitAccountAddress, implicitWallet := ts.TransactionFramework.CreateImplicitAccountFromInput("TX1:3") - tx5 := lo.PanicOnErr(ts.TransactionFramework.CreateTransactionWithOptions("TX5", implicitWallet, + tx5 := lo.PanicOnErr(ts.TransactionFramework.CreateSignedTransactionWithOptions("TX5", implicitWallet, testsuite.WithInputs(inputForImplicitAccount), testsuite.WithOutputs(outputsForImplicitAccount), )) @@ -336,7 +336,7 @@ func Test_TransitionAccount(t *testing.T) { ), ) - tx6 := lo.PanicOnErr(ts.TransactionFramework.CreateTransactionWithOptions("TX6", fullAccountWallet, + tx6 := lo.PanicOnErr(ts.TransactionFramework.CreateSignedTransactionWithOptions("TX6", fullAccountWallet, testsuite.WithContextInputs(iotago.TxEssenceContextInputs{ &iotago.BlockIssuanceCreditInput{ AccountID: implicitAccountID, diff --git a/pkg/tests/booker_test.go b/pkg/tests/booker_test.go index da743259b..15e48560b 100644 --- a/pkg/tests/booker_test.go +++ b/pkg/tests/booker_test.go @@ -26,24 +26,24 @@ func Test_IssuingTransactionsOutOfOrder(t *testing.T) { ts.IssuePayloadWithOptions("block1", node1, tx2) - ts.AssertTransactionsExist(ts.TransactionFramework.SignedTransactions("tx2"), true, node1) - ts.AssertTransactionsExist(ts.TransactionFramework.SignedTransactions("tx1"), false, node1) + ts.AssertTransactionsExist(ts.TransactionFramework.Transactions("tx2"), true, node1) + ts.AssertTransactionsExist(ts.TransactionFramework.Transactions("tx1"), false, node1) - ts.AssertTransactionsInCacheBooked(ts.TransactionFramework.SignedTransactions("tx2"), false, node1) + ts.AssertTransactionsInCacheBooked(ts.TransactionFramework.Transactions("tx2"), false, node1) // make sure that the block is not booked ts.IssuePayloadWithOptions("block2", node1, tx1) - ts.AssertTransactionsExist(ts.TransactionFramework.SignedTransactions("tx1", "tx2"), true, node1) - ts.AssertTransactionsInCacheBooked(ts.TransactionFramework.SignedTransactions("tx1", "tx2"), true, node1) + ts.AssertTransactionsExist(ts.TransactionFramework.Transactions("tx1", "tx2"), true, node1) + ts.AssertTransactionsInCacheBooked(ts.TransactionFramework.Transactions("tx1", "tx2"), true, node1) ts.AssertBlocksInCacheConflicts(map[*blocks.Block][]string{ ts.Block("block1"): {"tx2"}, ts.Block("block2"): {"tx1"}, }, node1) - ts.AssertTransactionInCacheConflicts(map[*iotago.SignedTransaction][]string{ - ts.TransactionFramework.SignedTransaction("tx2"): {"tx2"}, - ts.TransactionFramework.SignedTransaction("tx1"): {"tx1"}, + ts.AssertTransactionInCacheConflicts(map[*iotago.Transaction][]string{ + ts.TransactionFramework.Transaction("tx2"): {"tx2"}, + ts.TransactionFramework.Transaction("tx1"): {"tx1"}, }, node1) } @@ -69,17 +69,17 @@ func Test_DoubleSpend(t *testing.T) { ts.IssuePayloadWithOptions("block1", node1, tx1, blockfactory.WithStrongParents(ts.BlockID("Genesis"))) ts.IssuePayloadWithOptions("block2", node1, tx2, blockfactory.WithStrongParents(ts.BlockID("Genesis"))) - ts.AssertTransactionsExist(ts.TransactionFramework.SignedTransactions("tx1", "tx2"), true, node1, node2) - ts.AssertTransactionsInCacheBooked(ts.TransactionFramework.SignedTransactions("tx1", "tx2"), true, node1, node2) - ts.AssertTransactionsInCachePending(ts.TransactionFramework.SignedTransactions("tx1", "tx2"), true, node1, node2) + ts.AssertTransactionsExist(ts.TransactionFramework.Transactions("tx1", "tx2"), true, node1, node2) + ts.AssertTransactionsInCacheBooked(ts.TransactionFramework.Transactions("tx1", "tx2"), true, node1, node2) + ts.AssertTransactionsInCachePending(ts.TransactionFramework.Transactions("tx1", "tx2"), true, node1, node2) ts.AssertBlocksInCacheConflicts(map[*blocks.Block][]string{ ts.Block("block1"): {"tx1"}, ts.Block("block2"): {"tx2"}, }, node1, node2) - ts.AssertTransactionInCacheConflicts(map[*iotago.SignedTransaction][]string{ - ts.TransactionFramework.SignedTransaction("tx2"): {"tx2"}, - ts.TransactionFramework.SignedTransaction("tx1"): {"tx1"}, + ts.AssertTransactionInCacheConflicts(map[*iotago.Transaction][]string{ + ts.TransactionFramework.Transaction("tx2"): {"tx2"}, + ts.TransactionFramework.Transaction("tx1"): {"tx1"}, }, node1, node2) } @@ -92,14 +92,14 @@ func Test_DoubleSpend(t *testing.T) { ts.Block("block3"): {"tx1"}, ts.Block("block4"): {"tx2"}, }, node1, node2) - ts.AssertTransactionsInCachePending(ts.TransactionFramework.SignedTransactions("tx1", "tx2"), true, node1, node2) + ts.AssertTransactionsInCachePending(ts.TransactionFramework.Transactions("tx1", "tx2"), true, node1, node2) } // Issue an invalid block and assert that its vote is not cast. { ts.IssueValidationBlock("block5", node2, blockfactory.WithStrongParents(ts.BlockIDs("block3", "block4")...)) - ts.AssertTransactionsInCachePending(ts.TransactionFramework.SignedTransactions("tx1", "tx2"), true, node1, node2) + ts.AssertTransactionsInCachePending(ts.TransactionFramework.Transactions("tx1", "tx2"), true, node1, node2) } // Issue valid blocks that resolve the conflict. @@ -110,8 +110,8 @@ func Test_DoubleSpend(t *testing.T) { ts.AssertBlocksInCacheConflicts(map[*blocks.Block][]string{ ts.Block("block6"): {"tx2"}, }, node1, node2) - ts.AssertTransactionsInCacheAccepted(ts.TransactionFramework.SignedTransactions("tx2"), true, node1, node2) - ts.AssertTransactionsInCacheRejected(ts.TransactionFramework.SignedTransactions("tx1"), true, node1, node2) + ts.AssertTransactionsInCacheAccepted(ts.TransactionFramework.Transactions("tx2"), true, node1, node2) + ts.AssertTransactionsInCacheRejected(ts.TransactionFramework.Transactions("tx1"), true, node1, node2) } } @@ -148,8 +148,8 @@ func Test_MultipleAttachments(t *testing.T) { ts.Block("A.2"): {"tx1"}, ts.Block("B.2"): {"tx1"}, }), ts.Nodes()...) - ts.AssertTransactionInCacheConflicts(map[*iotago.SignedTransaction][]string{ - ts.TransactionFramework.SignedTransaction("tx1"): {"tx1"}, + ts.AssertTransactionInCacheConflicts(map[*iotago.Transaction][]string{ + ts.TransactionFramework.Transaction("tx1"): {"tx1"}, }, ts.Nodes()...) ts.AssertConflictsInCacheAcceptanceState([]string{"tx1"}, acceptance.Accepted, ts.Nodes()...) } @@ -171,8 +171,8 @@ func Test_MultipleAttachments(t *testing.T) { ts.AssertBlocksInCachePreAccepted(ts.Blocks("B.4", "A.5"), false, ts.Nodes()...) ts.AssertBlocksInCacheAccepted(ts.Blocks("A.3"), true, ts.Nodes()...) - ts.AssertTransactionsInCacheBooked(ts.TransactionFramework.SignedTransactions("tx1", "tx2"), true, ts.Nodes()...) - ts.AssertTransactionsInCachePending(ts.TransactionFramework.SignedTransactions("tx1", "tx2"), true, ts.Nodes()...) + ts.AssertTransactionsInCacheBooked(ts.TransactionFramework.Transactions("tx1", "tx2"), true, ts.Nodes()...) + ts.AssertTransactionsInCachePending(ts.TransactionFramework.Transactions("tx1", "tx2"), true, ts.Nodes()...) ts.AssertBlocksInCacheConflicts(lo.MergeMaps(blocksConflicts, map[*blocks.Block][]string{ ts.Block("A.3"): {"tx2"}, @@ -181,9 +181,9 @@ func Test_MultipleAttachments(t *testing.T) { ts.Block("A.5"): {}, ts.Block("B.4"): {}, }), ts.Nodes()...) - ts.AssertTransactionInCacheConflicts(map[*iotago.SignedTransaction][]string{ - ts.TransactionFramework.SignedTransaction("tx1"): {"tx1"}, - ts.TransactionFramework.SignedTransaction("tx2"): {"tx2"}, + ts.AssertTransactionInCacheConflicts(map[*iotago.Transaction][]string{ + ts.TransactionFramework.Transaction("tx1"): {"tx1"}, + ts.TransactionFramework.Transaction("tx2"): {"tx2"}, }, nodeA, nodeB) ts.AssertConflictsInCacheAcceptanceState([]string{"tx1", "tx2"}, acceptance.Accepted, ts.Nodes()...) } @@ -200,9 +200,9 @@ func Test_MultipleAttachments(t *testing.T) { ts.AssertBlocksInCacheAccepted(ts.Blocks("A.1", "B.1"), true, ts.Nodes()...) ts.AssertBlocksInCachePreAccepted(ts.Blocks("A.7", "B.6"), false, ts.Nodes()...) - ts.AssertTransactionsExist(ts.TransactionFramework.SignedTransactions("tx1", "tx2"), true, ts.Nodes()...) - ts.AssertTransactionsInCacheBooked(ts.TransactionFramework.SignedTransactions("tx1", "tx2"), true, ts.Nodes()...) - ts.AssertTransactionsInCacheAccepted(ts.TransactionFramework.SignedTransactions("tx1", "tx2"), true, ts.Nodes()...) + ts.AssertTransactionsExist(ts.TransactionFramework.Transactions("tx1", "tx2"), true, ts.Nodes()...) + ts.AssertTransactionsInCacheBooked(ts.TransactionFramework.Transactions("tx1", "tx2"), true, ts.Nodes()...) + ts.AssertTransactionsInCacheAccepted(ts.TransactionFramework.Transactions("tx1", "tx2"), true, ts.Nodes()...) ts.AssertBlocksInCacheConflicts(lo.MergeMaps(blocksConflicts, map[*blocks.Block][]string{ ts.Block("A.6"): {}, @@ -211,9 +211,9 @@ func Test_MultipleAttachments(t *testing.T) { ts.Block("B.6"): {}, }), ts.Nodes()...) - ts.AssertTransactionInCacheConflicts(map[*iotago.SignedTransaction][]string{ - ts.TransactionFramework.SignedTransaction("tx1"): {"tx1"}, - ts.TransactionFramework.SignedTransaction("tx2"): {"tx2"}, + ts.AssertTransactionInCacheConflicts(map[*iotago.Transaction][]string{ + ts.TransactionFramework.Transaction("tx1"): {"tx1"}, + ts.TransactionFramework.Transaction("tx2"): {"tx2"}, }, nodeA, nodeB) ts.AssertConflictsInCacheAcceptanceState([]string{"tx1", "tx2"}, acceptance.Accepted, nodeA, nodeB) } diff --git a/pkg/testsuite/blocks.go b/pkg/testsuite/blocks.go index 11f65d693..225df6b1e 100644 --- a/pkg/testsuite/blocks.go +++ b/pkg/testsuite/blocks.go @@ -122,7 +122,7 @@ func (t *TestSuite) AssertBlocksInCacheConflicts(blockConflicts map[*blocks.Bloc return ierrors.Errorf("AssertBlocksInCacheConflicts: %s: block %s is root block", node.Name, blockFromCache.ID()) } - expectedConflictIDs := ds.NewSet(lo.Map(conflictAliases, t.TransactionFramework.SignedTransactionID)...) + expectedConflictIDs := ds.NewSet(lo.Map(conflictAliases, t.TransactionFramework.TransactionID)...) actualConflictIDs := blockFromCache.ConflictIDs() if expectedConflictIDs.Size() != actualConflictIDs.Size() { diff --git a/pkg/testsuite/conflicts.go b/pkg/testsuite/conflicts.go index 5e8ffd328..7c7047da0 100644 --- a/pkg/testsuite/conflicts.go +++ b/pkg/testsuite/conflicts.go @@ -13,7 +13,7 @@ func (t *TestSuite) AssertConflictsInCacheAcceptanceState(expectedConflictAliase for _, node := range nodes { for _, conflictAlias := range expectedConflictAliases { t.Eventually(func() error { - acceptanceState := node.Protocol.MainEngineInstance().Ledger.ConflictDAG().AcceptanceState(ds.NewSet(t.TransactionFramework.SignedTransactionID(conflictAlias))) + acceptanceState := node.Protocol.MainEngineInstance().Ledger.ConflictDAG().AcceptanceState(ds.NewSet(t.TransactionFramework.TransactionID(conflictAlias))) if acceptanceState != expectedState { return ierrors.Errorf("assertTransactionsInCacheWithFunc: %s: conflict %s is %s, but expected %s", node.Name, conflictAlias, acceptanceState, expectedState) diff --git a/pkg/testsuite/transactions.go b/pkg/testsuite/transactions.go index 08fe93fb5..12977d5c5 100644 --- a/pkg/testsuite/transactions.go +++ b/pkg/testsuite/transactions.go @@ -12,25 +12,34 @@ import ( iotago "github.com/iotaledger/iota.go/v4" ) -func (t *TestSuite) AssertTransaction(signedTransaction *iotago.SignedTransaction, node *mock.Node) mempool.Transaction { +func (t *TestSuite) AssertTransaction(transaction *iotago.Transaction, node *mock.Node) mempool.Transaction { var loadedTransactionMetadata mempool.TransactionMetadata - signedTransactionID, err := signedTransaction.ID() + transactionID, err := transaction.ID() require.NoError(t.Testing, err) t.Eventually(func() error { var exists bool - loadedTransactionMetadata, exists = node.Protocol.MainEngineInstance().Ledger.TransactionMetadata(signedTransactionID) + loadedTransactionMetadata, exists = node.Protocol.MainEngineInstance().Ledger.TransactionMetadata(transactionID) if !exists { - return ierrors.Errorf("AssertTransaction: %s: signedTransaction %s does not exist", node.Name, signedTransactionID) + return ierrors.Errorf("AssertTransaction: %s: transaction %s does not exist", node.Name, transactionID) } - if signedTransactionID != loadedTransactionMetadata.ID() { - return ierrors.Errorf("AssertTransaction: %s: expected ID %s, got %s", node.Name, signedTransactionID, loadedTransactionMetadata.ID()) + if transactionID != loadedTransactionMetadata.ID() { + return ierrors.Errorf("AssertTransaction: %s: expected ID %s, got %s", node.Name, transactionID, loadedTransactionMetadata.ID()) } //nolint: forcetypeassert // we are in a test and want to assert it anyway - if !cmp.Equal(signedTransaction.Transaction, loadedTransactionMetadata.Transaction().(*iotago.SignedTransaction).Transaction) { - return ierrors.Errorf("AssertTransaction: %s: expected %s, got %s", node.Name, signedTransaction, loadedTransactionMetadata.Transaction()) + if !cmp.Equal(transaction.TransactionEssence, loadedTransactionMetadata.Transaction().(*iotago.Transaction).TransactionEssence) { + return ierrors.Errorf("AssertTransaction: %s: expected TransactionEssence %v, got %v", node.Name, transaction.TransactionEssence, loadedTransactionMetadata.Transaction().(*iotago.Transaction).TransactionEssence) + } + + typedTransaction, ok := loadedTransactionMetadata.Transaction().(*iotago.Transaction) + if !ok { + return ierrors.Errorf("AssertTransaction: %s: expected Transaction type %T, got %T", node.Name, transaction, loadedTransactionMetadata.Transaction()) + } + + if !cmp.Equal(transaction.Outputs, typedTransaction.Outputs) { + return ierrors.Errorf("AssertTransaction: %s: expected Outputs %s, got %s", node.Name, transaction.Outputs, typedTransaction.Outputs) } return nil @@ -39,7 +48,7 @@ func (t *TestSuite) AssertTransaction(signedTransaction *iotago.SignedTransactio return loadedTransactionMetadata.Transaction() } -func (t *TestSuite) AssertTransactionsExist(transactions []*iotago.SignedTransaction, expectedExist bool, nodes ...*mock.Node) { +func (t *TestSuite) AssertTransactionsExist(transactions []*iotago.Transaction, expectedExist bool, nodes ...*mock.Node) { mustNodes(nodes) for _, node := range nodes { @@ -66,7 +75,7 @@ func (t *TestSuite) AssertTransactionsExist(transactions []*iotago.SignedTransac } } -func (t *TestSuite) assertTransactionsInCacheWithFunc(expectedTransactions []*iotago.SignedTransaction, expectedPropertyState bool, propertyFunc func(mempool.TransactionMetadata) bool, nodes ...*mock.Node) { +func (t *TestSuite) assertTransactionsInCacheWithFunc(expectedTransactions []*iotago.Transaction, expectedPropertyState bool, propertyFunc func(mempool.TransactionMetadata) bool, nodes ...*mock.Node) { mustNodes(nodes) for _, node := range nodes { @@ -92,31 +101,31 @@ func (t *TestSuite) assertTransactionsInCacheWithFunc(expectedTransactions []*io } } -func (t *TestSuite) AssertTransactionsInCacheAccepted(expectedTransactions []*iotago.SignedTransaction, expectedFlag bool, nodes ...*mock.Node) { +func (t *TestSuite) AssertTransactionsInCacheAccepted(expectedTransactions []*iotago.Transaction, expectedFlag bool, nodes ...*mock.Node) { t.assertTransactionsInCacheWithFunc(expectedTransactions, expectedFlag, mempool.TransactionMetadata.IsAccepted, nodes...) } -func (t *TestSuite) AssertTransactionsInCacheRejected(expectedTransactions []*iotago.SignedTransaction, expectedFlag bool, nodes ...*mock.Node) { +func (t *TestSuite) AssertTransactionsInCacheRejected(expectedTransactions []*iotago.Transaction, expectedFlag bool, nodes ...*mock.Node) { t.assertTransactionsInCacheWithFunc(expectedTransactions, expectedFlag, mempool.TransactionMetadata.IsRejected, nodes...) } -func (t *TestSuite) AssertTransactionsInCacheBooked(expectedTransactions []*iotago.SignedTransaction, expectedFlag bool, nodes ...*mock.Node) { +func (t *TestSuite) AssertTransactionsInCacheBooked(expectedTransactions []*iotago.Transaction, expectedFlag bool, nodes ...*mock.Node) { t.assertTransactionsInCacheWithFunc(expectedTransactions, expectedFlag, mempool.TransactionMetadata.IsBooked, nodes...) } -func (t *TestSuite) AssertTransactionsInCacheConflicting(expectedTransactions []*iotago.SignedTransaction, expectedFlag bool, nodes ...*mock.Node) { +func (t *TestSuite) AssertTransactionsInCacheConflicting(expectedTransactions []*iotago.Transaction, expectedFlag bool, nodes ...*mock.Node) { t.assertTransactionsInCacheWithFunc(expectedTransactions, expectedFlag, mempool.TransactionMetadata.IsConflicting, nodes...) } -func (t *TestSuite) AssertTransactionsInCacheInvalid(expectedTransactions []*iotago.SignedTransaction, expectedFlag bool, nodes ...*mock.Node) { +func (t *TestSuite) AssertTransactionsInCacheInvalid(expectedTransactions []*iotago.Transaction, expectedFlag bool, nodes ...*mock.Node) { t.assertTransactionsInCacheWithFunc(expectedTransactions, expectedFlag, mempool.TransactionMetadata.IsInvalid, nodes...) } -func (t *TestSuite) AssertTransactionsInCachePending(expectedTransactions []*iotago.SignedTransaction, expectedFlag bool, nodes ...*mock.Node) { +func (t *TestSuite) AssertTransactionsInCachePending(expectedTransactions []*iotago.Transaction, expectedFlag bool, nodes ...*mock.Node) { t.assertTransactionsInCacheWithFunc(expectedTransactions, expectedFlag, mempool.TransactionMetadata.IsPending, nodes...) } -func (t *TestSuite) AssertTransactionInCacheConflicts(transactionConflicts map[*iotago.SignedTransaction][]string, nodes ...*mock.Node) { +func (t *TestSuite) AssertTransactionInCacheConflicts(transactionConflicts map[*iotago.Transaction][]string, nodes ...*mock.Node) { for _, node := range nodes { for transaction, conflictAliases := range transactionConflicts { transactionID, err := transaction.ID() @@ -128,7 +137,7 @@ func (t *TestSuite) AssertTransactionInCacheConflicts(transactionConflicts map[* return ierrors.Errorf("AssertTransactionInCacheConflicts: %s: block %s does not exist", node.Name, transactionID) } - expectedConflictIDs := ds.NewSet(lo.Map(conflictAliases, t.TransactionFramework.SignedTransactionID)...) + expectedConflictIDs := ds.NewSet(lo.Map(conflictAliases, t.TransactionFramework.TransactionID)...) actualConflictIDs := transactionFromCache.ConflictIDs() if expectedConflictIDs.Size() != actualConflictIDs.Size() { diff --git a/pkg/testsuite/transactions_framework.go b/pkg/testsuite/transactions_framework.go index 81cdd8254..e917e7d33 100644 --- a/pkg/testsuite/transactions_framework.go +++ b/pkg/testsuite/transactions_framework.go @@ -19,9 +19,10 @@ import ( type TransactionFramework struct { apiProvider iotago.APIProvider - wallet *mock.HDWallet - states map[string]*utxoledger.Output - transactions map[string]*iotago.SignedTransaction + wallet *mock.HDWallet + states map[string]*utxoledger.Output + signedTransactions map[string]*iotago.SignedTransaction + transactions map[string]*iotago.Transaction } func NewTransactionFramework(protocol *protocol.Protocol, genesisSeed []byte, accounts ...snapshotcreator.AccountDetails) *TransactionFramework { @@ -32,10 +33,12 @@ func NewTransactionFramework(protocol *protocol.Protocol, genesisSeed []byte, ac } tf := &TransactionFramework{ - apiProvider: protocol, - states: map[string]*utxoledger.Output{"Genesis:0": genesisOutput}, - transactions: make(map[string]*iotago.SignedTransaction), - wallet: mock.NewHDWallet("genesis", genesisSeed, 0), + apiProvider: protocol, + states: map[string]*utxoledger.Output{"Genesis:0": genesisOutput}, + signedTransactions: make(map[string]*iotago.SignedTransaction), + transactions: make(map[string]*iotago.Transaction), + + wallet: mock.NewHDWallet("genesis", genesisSeed, 0), } for idx := range accounts { @@ -50,7 +53,7 @@ func NewTransactionFramework(protocol *protocol.Protocol, genesisSeed []byte, ac return tf } -func (t *TransactionFramework) RegisterTransaction(alias string, transaction *iotago.SignedTransaction) { +func (t *TransactionFramework) RegisterTransaction(alias string, transaction *iotago.Transaction) { currentAPI := t.apiProvider.CurrentAPI() (lo.PanicOnErr(transaction.ID())).RegisterAlias(alias) @@ -69,7 +72,13 @@ func (t *TransactionFramework) RegisterTransaction(alias string, transaction *io } } -func (t *TransactionFramework) CreateTransactionWithOptions(alias string, signingWallets []*mock.HDWallet, opts ...options.Option[builder.TransactionBuilder]) (*iotago.SignedTransaction, error) { +func (t *TransactionFramework) RegisterSignedTransaction(alias string, signedTransaction *iotago.SignedTransaction) { + (lo.PanicOnErr(signedTransaction.ID())).RegisterAlias(alias) + + t.signedTransactions[alias] = signedTransaction +} + +func (t *TransactionFramework) CreateSignedTransactionWithOptions(alias string, signingWallets []*mock.HDWallet, opts ...options.Option[builder.TransactionBuilder]) (*iotago.SignedTransaction, error) { currentAPI := t.apiProvider.CurrentAPI() walletKeys := make([]iotago.AddressKeys, 0, len(signingWallets)*2) @@ -88,18 +97,19 @@ func (t *TransactionFramework) CreateTransactionWithOptions(alias string, signin randomPayload := tpkg.Rand12ByteArray() txBuilder.AddTaggedDataPayload(&iotago.TaggedData{Tag: randomPayload[:], Data: randomPayload[:]}) - tx, err := options.Apply(txBuilder, opts).Build(iotago.NewInMemoryAddressSigner(walletKeys...)) + signedTransaction, err := options.Apply(txBuilder, opts).Build(iotago.NewInMemoryAddressSigner(walletKeys...)) if err == nil { - t.RegisterTransaction(alias, tx) + t.RegisterSignedTransaction(alias, signedTransaction) + t.RegisterTransaction(alias, signedTransaction.Transaction) } - return tx, err + return signedTransaction, err } func (t *TransactionFramework) CreateSimpleTransaction(alias string, outputCount int, inputAliases ...string) (*iotago.SignedTransaction, error) { inputStates, outputStates, signingWallets := t.CreateBasicOutputsEqually(outputCount, inputAliases...) - return t.CreateTransactionWithOptions(alias, signingWallets, WithInputs(inputStates), WithOutputs(outputStates)) + return t.CreateSignedTransactionWithOptions(alias, signingWallets, WithInputs(inputStates), WithOutputs(outputStates)) } func (t *TransactionFramework) CreateBasicOutputsEqually(outputCount int, inputAliases ...string) (consumedInputs utxoledger.Outputs, outputs iotago.Outputs[iotago.Output], signingWallets []*mock.HDWallet) { @@ -343,7 +353,7 @@ func (t *TransactionFramework) OutputID(alias string) iotago.OutputID { } func (t *TransactionFramework) SignedTransaction(alias string) *iotago.SignedTransaction { - transaction, exists := t.transactions[alias] + transaction, exists := t.signedTransactions[alias] if !exists { panic(ierrors.Errorf("transaction with given alias does not exist %s", alias)) } @@ -363,6 +373,27 @@ func (t *TransactionFramework) SignedTransactionIDs(aliases ...string) []iotago. return lo.Map(aliases, t.SignedTransactionID) } +func (t *TransactionFramework) Transaction(alias string) *iotago.Transaction { + transaction, exists := t.transactions[alias] + if !exists { + panic(ierrors.Errorf("transaction with given alias does not exist %s", alias)) + } + + return transaction +} + +func (t *TransactionFramework) TransactionID(alias string) iotago.TransactionID { + return lo.PanicOnErr(t.Transaction(alias).ID()) +} + +func (t *TransactionFramework) Transactions(aliases ...string) []*iotago.Transaction { + return lo.Map(aliases, t.Transaction) +} + +func (t *TransactionFramework) TransactionIDs(aliases ...string) []iotago.TransactionID { + return lo.Map(aliases, t.TransactionID) +} + func (t *TransactionFramework) DefaultAddress(addressType ...iotago.AddressType) iotago.Address { return t.wallet.Address(addressType...) } diff --git a/tools/evil-spammer/go.mod b/tools/evil-spammer/go.mod index fe5c76608..0d9e3aa96 100644 --- a/tools/evil-spammer/go.mod +++ b/tools/evil-spammer/go.mod @@ -17,7 +17,7 @@ require ( github.com/iotaledger/hive.go/runtime v0.0.0-20230929122509-67f34bfed40d github.com/iotaledger/iota-core v0.0.0-00010101000000-000000000000 github.com/iotaledger/iota-core/tools/genesis-snapshot v0.0.0-00010101000000-000000000000 - github.com/iotaledger/iota.go/v4 v4.0.0-20231002120511-9ab88bf44daf + github.com/iotaledger/iota.go/v4 v4.0.0-20231003101444-5687809cd68a github.com/mr-tron/base58 v1.2.0 go.uber.org/atomic v1.11.0 ) @@ -28,7 +28,7 @@ require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect github.com/eclipse/paho.mqtt.golang v1.4.3 // indirect - github.com/ethereum/go-ethereum v1.13.1 // indirect + github.com/ethereum/go-ethereum v1.13.2 // indirect github.com/fatih/structs v1.1.0 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/google/go-cmp v0.5.9 // indirect diff --git a/tools/evil-spammer/go.sum b/tools/evil-spammer/go.sum index db415376d..ddf98cf3b 100644 --- a/tools/evil-spammer/go.sum +++ b/tools/evil-spammer/go.sum @@ -60,8 +60,8 @@ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.m github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/ethereum/go-ethereum v1.13.1 h1:UF2FaUKPIy5jeZk3X06ait3y2Q4wI+vJ1l7+UARp+60= -github.com/ethereum/go-ethereum v1.13.1/go.mod h1:xHQKzwkHSl0gnSjZK1mWa06XEdm9685AHqhRknOzqGQ= +github.com/ethereum/go-ethereum v1.13.2 h1:g9mCpfPWqCA1OL4e6C98PeVttb0HadfBRuKTGvMnOvw= +github.com/ethereum/go-ethereum v1.13.2/go.mod h1:gkQ5Ygi64ZBh9M/4iXY1R8WqoNCx1Ey0CkYn2BD4/fw= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= @@ -195,8 +195,8 @@ github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20230929122509-67f34bf github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20230929122509-67f34bfed40d/go.mod h1:IJgaaxbgKCsNat18jlJJEAxCY2oVYR3F30B+M4vJ89I= github.com/iotaledger/hive.go/stringify v0.0.0-20230929122509-67f34bfed40d h1:ekHWRypoaiCXgrJVUQS7rCewsK3FuG1gTbPxu5jYn9c= github.com/iotaledger/hive.go/stringify v0.0.0-20230929122509-67f34bfed40d/go.mod h1:FTo/UWzNYgnQ082GI9QVM9HFDERqf9rw9RivNpqrnTs= -github.com/iotaledger/iota.go/v4 v4.0.0-20231002120511-9ab88bf44daf h1:TNt6qra1H62HctwYhoxujPml/uN2AtnE1zMkB5kkVfI= -github.com/iotaledger/iota.go/v4 v4.0.0-20231002120511-9ab88bf44daf/go.mod h1:+e3bsJFDr9HxmUMe+eQOLNut5wfcB/ivhJdouOJgOnE= +github.com/iotaledger/iota.go/v4 v4.0.0-20231003101444-5687809cd68a h1:xgh1YQvLN+Y3KwX1G9/znGbCaQsfpDtpSLn8nKvaP8s= +github.com/iotaledger/iota.go/v4 v4.0.0-20231003101444-5687809cd68a/go.mod h1:+e3bsJFDr9HxmUMe+eQOLNut5wfcB/ivhJdouOJgOnE= github.com/ipfs/go-cid v0.4.1 h1:A/T3qGvxi4kpKWWcPC/PgbvDA2bjVLO7n4UeVwnbs/s= github.com/ipfs/go-cid v0.4.1/go.mod h1:uQHwDeX4c6CtyrFwdqyhpNcxVewur1M7l7fNU7LKwZk= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= diff --git a/tools/evil-spammer/spammer/spammer.go b/tools/evil-spammer/spammer/spammer.go index 19333aa2d..8e86ee7a6 100644 --- a/tools/evil-spammer/spammer/spammer.go +++ b/tools/evil-spammer/spammer/spammer.go @@ -232,7 +232,7 @@ func (s *Spammer) PostTransaction(signedTx *iotago.SignedTransaction, clt wallet return } - txID := lo.PanicOnErr(signedTx.ID()) + txID := lo.PanicOnErr(signedTx.Transaction.ID()) allSolid := s.handleSolidityForReuseOutputs(clt, signedTx) if !allSolid { s.log.Debug(ErrInputsNotSolid) diff --git a/tools/evil-spammer/wallet/evilwallet.go b/tools/evil-spammer/wallet/evilwallet.go index 05e41ae6f..417c19933 100644 --- a/tools/evil-spammer/wallet/evilwallet.go +++ b/tools/evil-spammer/wallet/evilwallet.go @@ -34,7 +34,7 @@ const ( var ( defaultClientsURLs = []string{"http://localhost:8080", "http://localhost:8090"} - genesisTransactionID = iotago.SlotIdentifierRepresentingData(0, []byte("genesis")) + genesisTransactionID = iotago.TransactionIDRepresentingData(0, []byte("genesis")) dockerFaucetSeed = func() []byte { genesisSeed, err := base58.Decode("7R1itJx5hVuo9w9hjg5cwKFmek4HMSoBDgJZN8hKGxih") @@ -303,11 +303,11 @@ func (e *EvilWallet) requestFaucetFunds(wallet *Wallet) (outputID *Output, err e } // requested output to split and use in spammer - output := e.outputManager.CreateOutputFromAddress(wallet, receiveAddr, faucetTokensPerRequest, iotago.OutputIDFromTransactionIDAndIndex(lo.PanicOnErr(signedTx.ID()), 0), signedTx.Transaction.Outputs[0]) + output := e.outputManager.CreateOutputFromAddress(wallet, receiveAddr, faucetTokensPerRequest, iotago.OutputIDFromTransactionIDAndIndex(lo.PanicOnErr(signedTx.Transaction.ID()), 0), signedTx.Transaction.Outputs[0]) // set remainder output to be reused by the faucet wallet e.faucet.AddUnspentOutput(&Output{ - OutputID: iotago.OutputIDFromTransactionIDAndIndex(lo.PanicOnErr(signedTx.ID()), 1), + OutputID: iotago.OutputIDFromTransactionIDAndIndex(lo.PanicOnErr(signedTx.Transaction.ID()), 1), Address: faucetAddr, Index: 0, Balance: signedTx.Transaction.Outputs[1].BaseTokenAmount(), @@ -336,7 +336,7 @@ func (e *EvilWallet) splitOutputs(splitOutput *Output, inputWallet, outputWallet return iotago.TransactionID{}, err } - return lo.PanicOnErr(signedTx.ID()), nil + return lo.PanicOnErr(signedTx.Transaction.ID()), nil } func (e *EvilWallet) handleInputOutputDuringSplitOutputs(splitOutput *Output, splitNumber int, receiveWallet *Wallet) (input *Output, outputs []*OutputOption) { @@ -461,7 +461,7 @@ func (e *EvilWallet) addOutputsToOutputManager(signedTx *iotago.SignedTransactio for idx, o := range signedTx.Transaction.Outputs { addr := o.UnlockConditionSet().Address().Address out := &Output{ - OutputID: iotago.OutputIDFromTransactionIDAndIndex(lo.PanicOnErr(signedTx.ID()), uint16(idx)), + OutputID: iotago.OutputIDFromTransactionIDAndIndex(lo.PanicOnErr(signedTx.Transaction.ID()), uint16(idx)), Address: addr, Balance: o.BaseTokenAmount(), OutputStruct: o, @@ -504,7 +504,7 @@ func (e *EvilWallet) registerOutputAliases(signedTx *iotago.SignedTransaction, a } for idx := range signedTx.Transaction.Outputs { - id := iotago.OutputIDFromTransactionIDAndIndex(lo.PanicOnErr(signedTx.ID()), uint16(idx)) + id := iotago.OutputIDFromTransactionIDAndIndex(lo.PanicOnErr(signedTx.Transaction.ID()), uint16(idx)) out := e.outputManager.GetOutput(id) // register output alias diff --git a/tools/gendoc/go.mod b/tools/gendoc/go.mod index aa7509b57..ec2ff3eab 100644 --- a/tools/gendoc/go.mod +++ b/tools/gendoc/go.mod @@ -25,7 +25,7 @@ require ( github.com/dustin/go-humanize v1.0.1 // indirect github.com/eclipse/paho.mqtt.golang v1.4.3 // indirect github.com/elastic/gosigar v0.14.2 // indirect - github.com/ethereum/go-ethereum v1.13.1 // indirect + github.com/ethereum/go-ethereum v1.13.2 // indirect github.com/fatih/structs v1.1.0 // indirect github.com/fbiville/markdown-table-formatter v0.3.0 // indirect github.com/felixge/fgprof v0.9.3 // indirect @@ -72,7 +72,7 @@ require ( github.com/iotaledger/hive.go/stringify v0.0.0-20230929122509-67f34bfed40d // indirect github.com/iotaledger/inx-app v1.0.0-rc.3.0.20230927140518-622f63be6182 // indirect github.com/iotaledger/inx/go v1.0.0-rc.2.0.20230927140257-bfa0bb0af2bd // indirect - github.com/iotaledger/iota.go/v4 v4.0.0-20231002120511-9ab88bf44daf // indirect + github.com/iotaledger/iota.go/v4 v4.0.0-20231003101444-5687809cd68a // indirect github.com/ipfs/boxo v0.10.0 // indirect github.com/ipfs/go-cid v0.4.1 // indirect github.com/ipfs/go-datastore v0.6.0 // indirect diff --git a/tools/gendoc/go.sum b/tools/gendoc/go.sum index d76e197ee..56151f64a 100644 --- a/tools/gendoc/go.sum +++ b/tools/gendoc/go.sum @@ -96,8 +96,8 @@ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.m github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/ethereum/go-ethereum v1.13.1 h1:UF2FaUKPIy5jeZk3X06ait3y2Q4wI+vJ1l7+UARp+60= -github.com/ethereum/go-ethereum v1.13.1/go.mod h1:xHQKzwkHSl0gnSjZK1mWa06XEdm9685AHqhRknOzqGQ= +github.com/ethereum/go-ethereum v1.13.2 h1:g9mCpfPWqCA1OL4e6C98PeVttb0HadfBRuKTGvMnOvw= +github.com/ethereum/go-ethereum v1.13.2/go.mod h1:gkQ5Ygi64ZBh9M/4iXY1R8WqoNCx1Ey0CkYn2BD4/fw= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= @@ -311,8 +311,8 @@ github.com/iotaledger/inx-app v1.0.0-rc.3.0.20230927140518-622f63be6182 h1:lQikt github.com/iotaledger/inx-app v1.0.0-rc.3.0.20230927140518-622f63be6182/go.mod h1:q24QEsS887ZWJVX76w2kwSgC84KS7wIKOy1otuqZ2ZM= github.com/iotaledger/inx/go v1.0.0-rc.2.0.20230927140257-bfa0bb0af2bd h1:nFG3Zq/zFA4KhBYFX2IezX1C74zfE0DqCt0LrgTa9Ig= github.com/iotaledger/inx/go v1.0.0-rc.2.0.20230927140257-bfa0bb0af2bd/go.mod h1:c5778OnWpLq108YE+Eb2m8Ri/t/4ydV0TvI/Sy5YivQ= -github.com/iotaledger/iota.go/v4 v4.0.0-20231002120511-9ab88bf44daf h1:TNt6qra1H62HctwYhoxujPml/uN2AtnE1zMkB5kkVfI= -github.com/iotaledger/iota.go/v4 v4.0.0-20231002120511-9ab88bf44daf/go.mod h1:+e3bsJFDr9HxmUMe+eQOLNut5wfcB/ivhJdouOJgOnE= +github.com/iotaledger/iota.go/v4 v4.0.0-20231003101444-5687809cd68a h1:xgh1YQvLN+Y3KwX1G9/znGbCaQsfpDtpSLn8nKvaP8s= +github.com/iotaledger/iota.go/v4 v4.0.0-20231003101444-5687809cd68a/go.mod h1:+e3bsJFDr9HxmUMe+eQOLNut5wfcB/ivhJdouOJgOnE= github.com/ipfs/boxo v0.10.0 h1:tdDAxq8jrsbRkYoF+5Rcqyeb91hgWe2hp7iLu7ORZLY= github.com/ipfs/boxo v0.10.0/go.mod h1:Fg+BnfxZ0RPzR0nOodzdIq3A7KgoWAOWsEIImrIQdBM= github.com/ipfs/go-cid v0.4.1 h1:A/T3qGvxi4kpKWWcPC/PgbvDA2bjVLO7n4UeVwnbs/s= diff --git a/tools/genesis-snapshot/go.mod b/tools/genesis-snapshot/go.mod index 940a8ff26..6a237325e 100644 --- a/tools/genesis-snapshot/go.mod +++ b/tools/genesis-snapshot/go.mod @@ -10,7 +10,7 @@ require ( github.com/iotaledger/hive.go/lo v0.0.0-20230929122509-67f34bfed40d github.com/iotaledger/hive.go/runtime v0.0.0-20230929122509-67f34bfed40d github.com/iotaledger/iota-core v0.0.0-00010101000000-000000000000 - github.com/iotaledger/iota.go/v4 v4.0.0-20231002120511-9ab88bf44daf + github.com/iotaledger/iota.go/v4 v4.0.0-20231003101444-5687809cd68a github.com/mr-tron/base58 v1.2.0 github.com/spf13/pflag v1.0.5 golang.org/x/crypto v0.13.0 @@ -21,7 +21,7 @@ require ( github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect - github.com/ethereum/go-ethereum v1.13.1 // indirect + github.com/ethereum/go-ethereum v1.13.2 // indirect github.com/google/go-cmp v0.5.9 // indirect github.com/google/uuid v1.3.1 // indirect github.com/holiman/uint256 v1.2.3 // indirect diff --git a/tools/genesis-snapshot/go.sum b/tools/genesis-snapshot/go.sum index bfe88c8e9..afe91a46a 100644 --- a/tools/genesis-snapshot/go.sum +++ b/tools/genesis-snapshot/go.sum @@ -12,8 +12,8 @@ github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5il github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= -github.com/ethereum/go-ethereum v1.13.1 h1:UF2FaUKPIy5jeZk3X06ait3y2Q4wI+vJ1l7+UARp+60= -github.com/ethereum/go-ethereum v1.13.1/go.mod h1:xHQKzwkHSl0gnSjZK1mWa06XEdm9685AHqhRknOzqGQ= +github.com/ethereum/go-ethereum v1.13.2 h1:g9mCpfPWqCA1OL4e6C98PeVttb0HadfBRuKTGvMnOvw= +github.com/ethereum/go-ethereum v1.13.2/go.mod h1:gkQ5Ygi64ZBh9M/4iXY1R8WqoNCx1Ey0CkYn2BD4/fw= github.com/fjl/memsize v0.0.2 h1:27txuSD9or+NZlnOWdKUxeBzTAUkWCVh+4Gf2dWFOzA= github.com/fjl/memsize v0.0.2/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= @@ -50,8 +50,8 @@ github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20230929122509-67f34bf github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20230929122509-67f34bfed40d/go.mod h1:IJgaaxbgKCsNat18jlJJEAxCY2oVYR3F30B+M4vJ89I= github.com/iotaledger/hive.go/stringify v0.0.0-20230929122509-67f34bfed40d h1:ekHWRypoaiCXgrJVUQS7rCewsK3FuG1gTbPxu5jYn9c= github.com/iotaledger/hive.go/stringify v0.0.0-20230929122509-67f34bfed40d/go.mod h1:FTo/UWzNYgnQ082GI9QVM9HFDERqf9rw9RivNpqrnTs= -github.com/iotaledger/iota.go/v4 v4.0.0-20231002120511-9ab88bf44daf h1:TNt6qra1H62HctwYhoxujPml/uN2AtnE1zMkB5kkVfI= -github.com/iotaledger/iota.go/v4 v4.0.0-20231002120511-9ab88bf44daf/go.mod h1:+e3bsJFDr9HxmUMe+eQOLNut5wfcB/ivhJdouOJgOnE= +github.com/iotaledger/iota.go/v4 v4.0.0-20231003101444-5687809cd68a h1:xgh1YQvLN+Y3KwX1G9/znGbCaQsfpDtpSLn8nKvaP8s= +github.com/iotaledger/iota.go/v4 v4.0.0-20231003101444-5687809cd68a/go.mod h1:+e3bsJFDr9HxmUMe+eQOLNut5wfcB/ivhJdouOJgOnE= github.com/ipfs/go-cid v0.4.1 h1:A/T3qGvxi4kpKWWcPC/PgbvDA2bjVLO7n4UeVwnbs/s= github.com/ipfs/go-cid v0.4.1/go.mod h1:uQHwDeX4c6CtyrFwdqyhpNcxVewur1M7l7fNU7LKwZk= github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg=