Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Implement read only flag in Mempool #396

Merged
merged 25 commits into from
Oct 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
f3653e0
Implement unit test for read only flag.
piotrm50 Oct 3, 2023
ee1dbdd
Merge remote-tracking branch 'origin/develop' into feat/introduce-rea…
piotrm50 Oct 3, 2023
2986f9f
Post-merge fixes
piotrm50 Oct 3, 2023
e77d5da
Fix linter problem
piotrm50 Oct 3, 2023
b191e42
Implement read only flag
piotrm50 Oct 3, 2023
60b3033
Refactor: simplified code
hmoog Oct 3, 2023
1200b92
Refactor: re-added comment
hmoog Oct 3, 2023
e7bba13
Merge branch 'develop' of github.com:iotaledger/iota-core into feat/i…
hmoog Oct 3, 2023
46b234f
Refactor: rverted some changes
hmoog Oct 3, 2023
3696eec
Refactor: reverted changes
hmoog Oct 3, 2023
8a53693
Refactor: reverted more changes
hmoog Oct 3, 2023
43bb5b4
Refactor: reverted more changes
hmoog Oct 3, 2023
21168a4
Refactor: reverted more code
hmoog Oct 3, 2023
e79b0e4
Refactor: cleaned up more code
hmoog Oct 3, 2023
5f3de1e
Refactor: minimized code changes
hmoog Oct 3, 2023
22b6e3e
Refactor: reverted more code
hmoog Oct 3, 2023
25341cd
Refactor: added the ability to inject states
hmoog Oct 3, 2023
9ee6077
Refactor: fixed errors
hmoog Oct 3, 2023
dc85348
Refactor: reverted accidental changes
hmoog Oct 3, 2023
a28e0f5
Refactor: reverted changes
hmoog Oct 3, 2023
b6daf06
Refactor: fixed go.mod
hmoog Oct 3, 2023
761f77c
Refactor: fixed linter issue
hmoog Oct 3, 2023
2b9de43
Refactor: upgraded to latest hive.go
hmoog Oct 3, 2023
d98fbdd
Refactor: changes to isReadOnly test in statediff
hmoog Oct 3, 2023
13d95c6
Merge branch 'develop' of github.com:iotaledger/iota-core into feat/i…
hmoog Oct 3, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -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.20231001095511-32be422a567e
github.com/iotaledger/inx/go v1.0.0-rc.2.0.20231001095356-923e8f138951
github.com/iotaledger/iota.go/v4 v4.0.0-20231003154311-26aa2f0fd388
github.com/iotaledger/iota.go/v4 v4.0.0-20231003181920-a3245ad7a737
github.com/labstack/echo/v4 v4.11.1
github.com/labstack/gommon v0.4.0
github.com/libp2p/go-libp2p v0.30.0
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -305,8 +305,8 @@ github.com/iotaledger/inx-app v1.0.0-rc.3.0.20231001095511-32be422a567e h1:Mwoe7
github.com/iotaledger/inx-app v1.0.0-rc.3.0.20231001095511-32be422a567e/go.mod h1:jhzexR5X8m6qcmrwt5OX477O/ZwT7Ak9sPT83ByPkAo=
github.com/iotaledger/inx/go v1.0.0-rc.2.0.20231001095356-923e8f138951 h1:qUf1W0fE1IyZzVy3Exv0Kj+SKECXG3S26c9m2ETb07U=
github.com/iotaledger/inx/go v1.0.0-rc.2.0.20231001095356-923e8f138951/go.mod h1:c5778OnWpLq108YE+Eb2m8Ri/t/4ydV0TvI/Sy5YivQ=
github.com/iotaledger/iota.go/v4 v4.0.0-20231003154311-26aa2f0fd388 h1:IGqHrJRmrzfalf1py+FGWzkWoaA5o6avBSYWznVG08s=
github.com/iotaledger/iota.go/v4 v4.0.0-20231003154311-26aa2f0fd388/go.mod h1:+e3bsJFDr9HxmUMe+eQOLNut5wfcB/ivhJdouOJgOnE=
github.com/iotaledger/iota.go/v4 v4.0.0-20231003181920-a3245ad7a737 h1:6fuDHswgN9zTwsMuKRKNClnT+rJCojvWf3Hk8f03cvc=
github.com/iotaledger/iota.go/v4 v4.0.0-20231003181920-a3245ad7a737/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=
Expand Down
6 changes: 3 additions & 3 deletions pkg/protocol/engine/ledger/ledger/ledger.go
Original file line number Diff line number Diff line change
Expand Up @@ -691,7 +691,7 @@ func (l *Ledger) resolveAccountOutput(accountID iotago.AccountID, slot iotago.Sl
return accountOutput, nil
}

func (l *Ledger) resolveState(stateRef iotago.Input) *promise.Promise[mempool.State] {
func (l *Ledger) resolveState(stateRef mempool.StateReference) *promise.Promise[mempool.State] {
p := promise.New[mempool.State]()

l.utxoLedger.ReadLockLedger()
Expand Down Expand Up @@ -727,8 +727,8 @@ func (l *Ledger) resolveState(stateRef iotago.Input) *promise.Promise[mempool.St

return p.Resolve(loadedCommitment)
case iotago.InputBlockIssuanceCredit, iotago.InputReward:
// these are always resolved as they depend on the commitment or UTXO inputs
return p.Resolve(stateRef)
//nolint:forcetypeassert
return p.Resolve(stateRef.(mempool.State))
default:
return p.Reject(ierrors.Errorf("unsupported input type %s", stateRef.Type()))
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/protocol/engine/ledger/ledger/vm.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ func NewVM(ledger *Ledger) *VM {
}
}

func (v *VM) Inputs(transaction mempool.Transaction) (inputReferences []iotago.Input, err error) {
func (v *VM) Inputs(transaction mempool.Transaction) (inputReferences []mempool.StateReference, err error) {
stardustTransaction, ok := transaction.(*iotago.Transaction)
if !ok {
return nil, iotago.ErrTxTypeInvalid
Expand Down
4 changes: 4 additions & 0 deletions pkg/protocol/engine/ledger/tests/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ func (m *MockedState) Type() iotago.StateType {
return iotago.InputUTXO
}

func (m *MockedState) IsReadOnly() bool {
return false
}

func (m *MockedState) OutputID() iotago.OutputID {
return m.id
}
Expand Down
6 changes: 3 additions & 3 deletions pkg/protocol/engine/ledger/tests/state_resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,10 @@ func (s *MockStateResolver) DestroyOutputState(stateID mempool.StateID) {
s.statesByID.Delete(stateID)
}

func (s *MockStateResolver) ResolveOutputState(outputID mempool.StateID) *promise.Promise[mempool.State] {
output, exists := s.statesByID.Get(outputID)
func (s *MockStateResolver) ResolveOutputState(reference mempool.StateReference) *promise.Promise[mempool.State] {
output, exists := s.statesByID.Get(reference.ReferencedStateID())
if !exists {
return promise.New[mempool.State]().Reject(ierrors.Errorf("output %s not found: %w", outputID.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)
Expand Down
38 changes: 0 additions & 38 deletions pkg/protocol/engine/ledger/tests/stored_state_reference.go

This file was deleted.

2 changes: 2 additions & 0 deletions pkg/protocol/engine/mempool/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,6 @@ type State interface {
StateID() StateID

Type() iotago.StateType

IsReadOnly() bool
}
7 changes: 6 additions & 1 deletion pkg/protocol/engine/mempool/state_reference.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,9 @@ package mempool

import iotago "github.com/iotaledger/iota.go/v4"

type StateReference = iotago.Input
type StateReference interface {
ReferencedStateID() iotago.Identifier

// Type returns the type of Input.
Type() iotago.StateType
}
34 changes: 31 additions & 3 deletions pkg/protocol/engine/mempool/tests/testframework.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,18 @@ func NewTestFramework(test *testing.T, instance mempool.MemPool[vote.MockedRank]

return t
}

func (t *TestFramework) InjectState(alias string, state mempool.State) {
t.referencesByAlias[alias] = NewStateReference(state.StateID(), state.Type())

t.ledgerState.AddOutputState(state)
}

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)
Expand Down Expand Up @@ -92,7 +100,7 @@ func (t *TestFramework) CreateTransaction(alias string, referencedStates []strin
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()
}
}

Expand Down Expand Up @@ -170,11 +178,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
}
Expand Down Expand Up @@ -399,3 +407,23 @@ func (t *TestFramework) Cleanup() {
t.signedTransactionByAlias = make(map[string]mempool.SignedTransaction)
t.blockIDsByAlias = make(map[string]iotago.BlockID)
}

type genericReference struct {
referencedStateID iotago.Identifier
stateType iotago.StateType
}

func NewStateReference(referencedStateID iotago.Identifier, stateType iotago.StateType) mempool.StateReference {
return &genericReference{
referencedStateID: referencedStateID,
stateType: stateType,
}
}

func (g *genericReference) ReferencedStateID() iotago.Identifier {
return g.referencedStateID
}

func (g *genericReference) Type() iotago.StateType {
return g.stateType
}
74 changes: 67 additions & 7 deletions pkg/protocol/engine/mempool/tests/tests.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,14 @@ func TestAllWithoutForkingEverything(t *testing.T, frameworkProvider func(*testi

func TestAllWithForkingEverything(t *testing.T, frameworkProvider func(*testing.T) *TestFramework) {
for testName, testCase := range map[string]func(*testing.T, *TestFramework){
"TestConflictPropagationForkAll": TestConflictPropagationForkAll,
"TestSetTxOrphanageMultipleAttachments": TestSetTxOrphanageMultipleAttachments,
"TestProcessTransaction": TestProcessTransaction,
"TestProcessTransactionsOutOfOrder": TestProcessTransactionsOutOfOrder,
"TestSetTransactionOrphanage": TestSetTransactionOrphanage,
"TestInvalidTransaction": TestInvalidTransaction,
"TestStoreAttachmentInEvictedSlot": TestStoreAttachmentInEvictedSlot,
"TestConflictPropagationForkAll": TestConflictPropagationForkAll,
"TestSetTxOrphanageMultipleAttachments": TestSetTxOrphanageMultipleAttachments,
"TestProcessTransactionWithReadOnlyInputs": TestProcessTransactionWithReadOnlyInputs,
"TestProcessTransaction": TestProcessTransaction,
"TestProcessTransactionsOutOfOrder": TestProcessTransactionsOutOfOrder,
"TestSetTransactionOrphanage": TestSetTransactionOrphanage,
"TestInvalidTransaction": TestInvalidTransaction,
"TestStoreAttachmentInEvictedSlot": TestStoreAttachmentInEvictedSlot,
} {
t.Run(testName, func(t *testing.T) { testCase(t, frameworkProvider(t)) })
}
Expand Down Expand Up @@ -79,6 +80,65 @@ func TestProcessTransaction(t *testing.T, tf *TestFramework) {
})
}

func TestProcessTransactionWithReadOnlyInputs(t *testing.T, tf *TestFramework) {
tf.InjectState("readOnlyInput", &iotago.Commitment{
ProtocolVersion: 0,
Slot: 0,
PreviousCommitmentID: iotago.CommitmentID{},
RootsID: iotago.Identifier{},
CumulativeWeight: 0,
ReferenceManaCost: 0,
})

tf.CreateTransaction("tx1", []string{"genesis", "readOnlyInput"}, 1)
tf.CreateTransaction("tx2", []string{"tx1:0", "readOnlyInput"}, 1)

tf.SignedTransactionFromTransaction("tx2", "tx2")
tf.SignedTransactionFromTransaction("tx1", "tx1")

require.NoError(t, tf.AttachTransactions("tx1", "tx2"))

tf.RequireBooked("tx1", "tx2")

tx1Metadata, exists := tf.TransactionMetadata("tx1")
require.True(t, exists)
_ = tx1Metadata.Outputs().ForEach(func(state mempool.StateMetadata) error {
if state.State().Type() == iotago.InputUTXO {
require.False(t, state.IsAccepted())
require.Equal(t, 1, state.PendingSpenderCount())
}

return nil
})

tx2Metadata, exists := tf.TransactionMetadata("tx2")
require.True(t, exists)

_ = tx2Metadata.Outputs().ForEach(func(state mempool.StateMetadata) error {
if state.State().Type() == iotago.InputUTXO {
require.False(t, state.IsAccepted())
require.Equal(t, 0, state.PendingSpenderCount())
}

if state.State().Type() == iotago.InputCommitment {
require.False(t, state.IsAccepted())
require.Equal(t, 2, state.PendingSpenderCount())
}

return nil
})

conflictSetsTx1, exists := tf.ConflictDAG.ConflictSets(tf.TransactionID("tx1"))
require.True(t, exists)
require.Equal(t, 1, conflictSetsTx1.Size())
require.True(t, conflictSetsTx1.Has(tf.StateID("genesis")))

conflictSetsTx2, exists := tf.ConflictDAG.ConflictSets(tf.TransactionID("tx2"))
require.True(t, exists)
require.Equal(t, 1, conflictSetsTx2.Size())
require.True(t, conflictSetsTx2.Has(tf.StateID("tx1:0")))
}

func TestProcessTransactionsOutOfOrder(t *testing.T, tf *TestFramework) {
tf.CreateSignedTransaction("tx1", []string{"genesis"}, 1)
tf.CreateSignedTransaction("tx2", []string{"tx1:0"}, 1)
Expand Down
8 changes: 0 additions & 8 deletions pkg/protocol/engine/mempool/tests/transaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,14 +49,6 @@ func (t *Transaction) Inputs() ([]mempool.StateReference, error) {
return t.inputs, nil
}

func (t *Transaction) CommitmentInput() *iotago.CommitmentInput {
return nil
}

func (t *Transaction) ContextInputs() (iotago.TransactionContextInputs, error) {
return nil, nil
}

func (t *Transaction) String() string {
return "Transaction(" + t.id.String() + ")"
}
Expand Down
18 changes: 12 additions & 6 deletions pkg/protocol/engine/mempool/v1/mempool.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,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() {
Expand Down Expand Up @@ -229,7 +229,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)
})

Expand Down Expand Up @@ -265,14 +265,20 @@ 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 {
inputsToFork := lo.Filter(transaction.inputs, func(metadata *StateMetadata) bool {
return !metadata.state.IsReadOnly()
})

m.forkTransaction(transaction, ds.NewSet(lo.Map(inputsToFork, 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.IsReadOnly() {
input.OnDoubleSpent(func() {
m.forkTransaction(transaction, ds.NewSet(input.state.StateID()))
})
}
})
}

Expand Down
6 changes: 3 additions & 3 deletions pkg/protocol/engine/mempool/v1/mempool_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ func TestMempoolV1_ResourceCleanup(t *testing.T) {
ledgerState := ledgertests.New(ledgertests.NewMockedState(iotago.TransactionID{}, 0))
conflictDAG := conflictdagv1.New[iotago.TransactionID, mempool.StateID, vote.MockedRank](func() int { return 0 })
memPoolInstance := New[vote.MockedRank](new(mempooltests.VM), func(reference mempool.StateReference) *promise.Promise[mempool.State] {
return ledgerState.ResolveOutputState(reference.StateID())
return ledgerState.ResolveOutputState(reference)
}, workers, conflictDAG, func(error) {})

tf := mempooltests.NewTestFramework(t, memPoolInstance, conflictDAG, ledgerState, workers)
Expand Down Expand Up @@ -104,7 +104,7 @@ func newTestFramework(t *testing.T) *mempooltests.TestFramework {
conflictDAG := conflictdagv1.New[iotago.TransactionID, mempool.StateID, vote.MockedRank](account.NewAccounts().SelectCommittee().SeatCount)

return mempooltests.NewTestFramework(t, New[vote.MockedRank](new(mempooltests.VM), func(reference mempool.StateReference) *promise.Promise[mempool.State] {
return ledgerState.ResolveOutputState(reference.StateID())
return ledgerState.ResolveOutputState(reference)
}, workers, conflictDAG, func(error) {}), conflictDAG, ledgerState, workers)
}

Expand All @@ -115,6 +115,6 @@ func newForkingTestFramework(t *testing.T) *mempooltests.TestFramework {
conflictDAG := conflictdagv1.New[iotago.TransactionID, mempool.StateID, vote.MockedRank](account.NewAccounts().SelectCommittee().SeatCount)

return mempooltests.NewTestFramework(t, New[vote.MockedRank](new(mempooltests.VM), func(reference mempool.StateReference) *promise.Promise[mempool.State] {
return ledgerState.ResolveOutputState(reference.StateID())
return ledgerState.ResolveOutputState(reference)
}, workers, conflictDAG, func(error) {}, WithForkAllTransactions[vote.MockedRank](true)), conflictDAG, ledgerState, workers)
}
20 changes: 9 additions & 11 deletions pkg/protocol/engine/mempool/v1/state_diff.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,19 +98,17 @@ func (s *StateDiff) RollbackTransaction(transaction *TransactionMetadata) error
return nil
}

func (s *StateDiff) compactStateChanges(output *StateMetadata, newValue int) {
if output.state.Type() != iotago.InputUTXO {
return
}

func (s *StateDiff) compactStateChanges(stateMetadata *StateMetadata, usageCounter int) {
switch {
case newValue > 0:
s.createdOutputs.Set(output.state.StateID(), output)
case newValue < 0:
s.spentOutputs.Set(output.state.StateID(), output)
case usageCounter > 0:
s.createdOutputs.Set(stateMetadata.state.StateID(), stateMetadata)
case usageCounter < 0:
if !stateMetadata.state.IsReadOnly() {
s.spentOutputs.Set(stateMetadata.state.StateID(), stateMetadata)
}
default:
s.createdOutputs.Delete(output.state.StateID())
s.spentOutputs.Delete(output.state.StateID())
s.createdOutputs.Delete(stateMetadata.state.StateID())
s.spentOutputs.Delete(stateMetadata.state.StateID())
}
}

Expand Down
Loading