diff --git a/pkg/protocol/engine/ledger/ledger/ledger.go b/pkg/protocol/engine/ledger/ledger/ledger.go index 5818a92ba..abe88c9f5 100644 --- a/pkg/protocol/engine/ledger/ledger/ledger.go +++ b/pkg/protocol/engine/ledger/ledger/ledger.go @@ -724,9 +724,14 @@ func (l *Ledger) resolveState(stateRef iotago.Input) *promise.Promise[mempool.St } return p.Resolve(loadedCommitment) - case iotago.InputBlockIssuanceCredit, iotago.InputReward: + case iotago.InputBlockIssuanceCredit: // these are always resolved as they depend on the commitment or UTXO inputs - return p.Resolve(stateRef) + //nolint:forcetypeassert // we can safely assume that this is a BlockIssuanceCreditInput + return p.Resolve(stateRef.(*iotago.BlockIssuanceCreditInput)) + case iotago.InputReward: + // these are always resolved as they depend on the commitment or UTXO inputs + //nolint:forcetypeassert // we can safely assume that this is a RewardInput + return p.Resolve(stateRef.(*iotago.RewardInput)) default: return p.Reject(ierrors.Errorf("unsupported input type %s", stateRef.Type())) } diff --git a/pkg/protocol/engine/ledger/tests/state.go b/pkg/protocol/engine/ledger/tests/state.go index 03513f308..1908a1ce0 100644 --- a/pkg/protocol/engine/ledger/tests/state.go +++ b/pkg/protocol/engine/ledger/tests/state.go @@ -27,6 +27,10 @@ func (m *MockedState) Type() iotago.StateType { return iotago.InputUTXO } +func (m *MockedState) ReadOnly() bool { + return false +} + func (m *MockedState) OutputID() iotago.OutputID { return m.id } diff --git a/pkg/protocol/engine/ledger/tests/state_resolver.go b/pkg/protocol/engine/ledger/tests/state_resolver.go index 82561379c..05a55241f 100644 --- a/pkg/protocol/engine/ledger/tests/state_resolver.go +++ b/pkg/protocol/engine/ledger/tests/state_resolver.go @@ -33,9 +33,9 @@ func (s *MockStateResolver) DestroyOutputState(stateID mempool.StateID) { func (s *MockStateResolver) ResolveOutputState(reference iotago.Input) *promise.Promise[mempool.State] { if reference.Type() == iotago.InputUTXO { - output, exists := s.statesByID.Get(reference.StateID()) + output, exists := s.statesByID.Get(reference.ReferencedStateID()) if !exists { - return promise.New[mempool.State]().Reject(ierrors.Errorf("output %s not found: %w", reference.StateID().ToHex(), mempool.ErrStateNotFound)) + return promise.New[mempool.State]().Reject(ierrors.Errorf("output %s not found: %w", reference.ReferencedStateID().ToHex(), mempool.ErrStateNotFound)) } return promise.New[mempool.State]().Resolve(output) diff --git a/pkg/protocol/engine/ledger/tests/stored_state_reference.go b/pkg/protocol/engine/ledger/tests/stored_state_reference.go deleted file mode 100644 index a4d6f520f..000000000 --- a/pkg/protocol/engine/ledger/tests/stored_state_reference.go +++ /dev/null @@ -1,38 +0,0 @@ -package ledgertests - -import ( - "github.com/iotaledger/hive.go/lo" - iotago "github.com/iotaledger/iota.go/v4" -) - -// StoredStateReference is a reference to a State that is stored in the ledger state. -type StoredStateReference iotago.OutputID - -func (l StoredStateReference) StateID() iotago.Identifier { - return iotago.IdentifierFromData(lo.PanicOnErr(l.OutputID().Bytes())) -} - -// Type returns the type of the StateReference. -func (l StoredStateReference) Type() iotago.StateType { - return 0 -} - -// Size returns the size of the StateReference. -func (l StoredStateReference) Size() int { - return 0 -} - -// WorkScore returns the workscore of the StateReference. -func (l StoredStateReference) WorkScore(_ *iotago.WorkScoreStructure) (iotago.WorkScore, error) { - return 0, nil -} - -// OutputID returns the ID of the referenced State in the ledger state. -func (l StoredStateReference) OutputID() iotago.OutputID { - return iotago.OutputID(l) -} - -// Index returns the Index of the referenced State. -func (l StoredStateReference) Index() uint16 { - return iotago.OutputID(l).Index() -} diff --git a/pkg/protocol/engine/mempool/state.go b/pkg/protocol/engine/mempool/state.go index 0b0c9ed9e..984f38dfd 100644 --- a/pkg/protocol/engine/mempool/state.go +++ b/pkg/protocol/engine/mempool/state.go @@ -6,4 +6,6 @@ type State interface { StateID() StateID Type() iotago.StateType + + ReadOnly() bool } diff --git a/pkg/protocol/engine/mempool/tests/testframework.go b/pkg/protocol/engine/mempool/tests/testframework.go index 8746772a7..7224922a4 100644 --- a/pkg/protocol/engine/mempool/tests/testframework.go +++ b/pkg/protocol/engine/mempool/tests/testframework.go @@ -92,7 +92,7 @@ func (t *TestFramework) CreateTransaction(alias string, referencedContextStates, TransactionOutputIndex: i, } - t.stateIDByAlias[alias+":"+strconv.Itoa(int(i))] = t.referencesByAlias[alias+":"+strconv.Itoa(int(i))].StateID() + t.stateIDByAlias[alias+":"+strconv.Itoa(int(i))] = t.referencesByAlias[alias+":"+strconv.Itoa(int(i))].ReferencedStateID() } } @@ -170,11 +170,11 @@ func (t *TestFramework) OutputStateMetadata(alias string) (mempool.StateMetadata func (t *TestFramework) StateID(alias string) mempool.StateID { if alias == "genesis" { - return (&iotago.UTXOInput{}).StateID() + return (&iotago.UTXOInput{}).ReferencedStateID() } stateID, exists := t.stateIDByAlias[alias] - require.True(t.test, exists, "StateID with alias '%s' does not exist", alias) + require.True(t.test, exists, "ReferencedStateID with alias '%s' does not exist", alias) return stateID } diff --git a/pkg/protocol/engine/mempool/v1/mempool.go b/pkg/protocol/engine/mempool/v1/mempool.go index 91e273c3a..c4dc79d3c 100644 --- a/pkg/protocol/engine/mempool/v1/mempool.go +++ b/pkg/protocol/engine/mempool/v1/mempool.go @@ -129,7 +129,7 @@ func (m *MemPool[VoteRank]) TransactionMetadata(id iotago.TransactionID) (transa // StateMetadata returns the metadata of the output state with the given ID. func (m *MemPool[VoteRank]) StateMetadata(stateReference mempool.StateReference) (state mempool.StateMetadata, err error) { - stateRequest, exists := m.cachedStateRequests.Get(stateReference.StateID()) + stateRequest, exists := m.cachedStateRequests.Get(stateReference.ReferencedStateID()) // create a new request that does not wait for missing states if !exists || !stateRequest.WasCompleted() { @@ -225,7 +225,7 @@ func (m *MemPool[VoteRank]) solidifyInputs(transaction *TransactionMetadata) { for i, inputReference := range transaction.inputReferences { stateReference, index := inputReference, i - request, created := m.cachedStateRequests.GetOrCreate(stateReference.StateID(), func() *promise.Promise[*StateMetadata] { + request, created := m.cachedStateRequests.GetOrCreate(stateReference.ReferencedStateID(), func() *promise.Promise[*StateMetadata] { return m.requestState(stateReference, true) }) @@ -261,14 +261,26 @@ func (m *MemPool[VoteRank]) executeTransaction(executionContext context.Context, func (m *MemPool[VoteRank]) bookTransaction(transaction *TransactionMetadata) { if m.optForkAllTransactions { - m.forkTransaction(transaction, ds.NewSet(lo.Map(transaction.inputs, func(stateMetadata *StateMetadata) mempool.StateID { - return stateMetadata.state.StateID() - })...)) + m.forkTransaction( + transaction, + ds.NewSet( + lo.Map( + lo.Filter(transaction.inputs, func(metadata *StateMetadata) bool { + return !metadata.state.ReadOnly() + }), + func(stateMetadata *StateMetadata) mempool.StateID { + return stateMetadata.state.StateID() + }, + )..., + ), + ) } else { lo.ForEach(transaction.inputs, func(input *StateMetadata) { - input.OnDoubleSpent(func() { - m.forkTransaction(transaction, ds.NewSet(input.state.StateID())) - }) + if !input.state.ReadOnly() { + input.OnDoubleSpent(func() { + m.forkTransaction(transaction, ds.NewSet(input.state.StateID())) + }) + } }) } diff --git a/pkg/protocol/engine/utxoledger/output.go b/pkg/protocol/engine/utxoledger/output.go index 86b557651..7d5654ea1 100644 --- a/pkg/protocol/engine/utxoledger/output.go +++ b/pkg/protocol/engine/utxoledger/output.go @@ -47,6 +47,10 @@ func (o *Output) Type() iotago.StateType { return iotago.InputUTXO } +func (o *Output) ReadOnly() bool { + return false +} + func (o *Output) OutputID() iotago.OutputID { return o.outputID }