Skip to content

Commit

Permalink
Merge pull request #601 from iotaledger/fix/shallow-like-root-block
Browse files Browse the repository at this point in the history
Handle root-block shallow like parents correctly.
  • Loading branch information
piotrm50 authored Dec 5, 2023
2 parents 52beb5d + 92432a8 commit 85a1029
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 2 deletions.
22 changes: 20 additions & 2 deletions pkg/protocol/engine/booker/inmemorybooker/booker.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"github.com/iotaledger/hive.go/lo"
"github.com/iotaledger/hive.go/runtime/module"
"github.com/iotaledger/hive.go/runtime/options"
"github.com/iotaledger/iota-core/pkg/model"
"github.com/iotaledger/iota-core/pkg/protocol/engine"
"github.com/iotaledger/iota-core/pkg/protocol/engine/blocks"
"github.com/iotaledger/iota-core/pkg/protocol/engine/booker"
Expand All @@ -27,7 +28,8 @@ type Booker struct {

ledger ledger.Ledger

retainBlockFailure func(id iotago.BlockID, reason api.BlockFailureReason)
loadBlockFromStorage func(id iotago.BlockID) (*model.Block, bool)
retainBlockFailure func(id iotago.BlockID, reason api.BlockFailureReason)

errorHandler func(error)
apiProvider iotago.APIProvider
Expand All @@ -42,7 +44,7 @@ func NewProvider(opts ...options.Option[Booker]) module.Provider[*engine.Engine,
b.ledger = e.Ledger
b.ledger.HookConstructed(func() {
b.spendDAG = b.ledger.SpendDAG()

b.loadBlockFromStorage = e.Block
b.ledger.MemPool().OnTransactionAttached(func(transaction mempool.TransactionMetadata) {
transaction.OnAccepted(func() {
b.events.TransactionAccepted.Trigger(transaction)
Expand Down Expand Up @@ -193,6 +195,7 @@ func (b *Booker) inheritSpenders(block *blocks.Block) (spenderIDs ds.Set[iotago.
parentBlock, exists := b.blockCache.Block(parent.ID)
if !exists {
b.retainBlockFailure(block.ID(), api.BlockFailureParentNotFound)

return nil, ierrors.Errorf("parent %s does not exist", parent.ID)
}

Expand All @@ -202,6 +205,21 @@ func (b *Booker) inheritSpenders(block *blocks.Block) (spenderIDs ds.Set[iotago.
case iotago.WeakParentType:
spenderIDsToInherit.AddAll(parentBlock.PayloadSpenderIDs())
case iotago.ShallowLikeParentType:
// If parent block is a RootBlock, then make sure that the block contains a transaction;
// otherwise, the reference is invalid.
if parentBlock.IsRootBlock() {
parentModelBlock, exists := b.loadBlockFromStorage(parent.ID)
if !exists {
return nil, ierrors.Wrapf(err, "shallow like parent %s does not exist in storage", parent.ID.String())
}

if _, hasTx := parentModelBlock.SignedTransaction(); !hasTx {
return nil, ierrors.Wrapf(err, "shallow like parent %s does not contain a conflicting transaction", parent.ID.String())
}

break
}

// Check whether the parent contains a conflicting TX,
// otherwise reference is invalid and the block should be marked as invalid as well.
if signedTransaction, hasTx := parentBlock.SignedTransaction(); !hasTx || !parentBlock.PayloadSpenderIDs().Has(lo.PanicOnErr(signedTransaction.Transaction.ID())) {
Expand Down
55 changes: 55 additions & 0 deletions pkg/tests/booker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package tests

import (
"testing"
"time"

"github.com/iotaledger/hive.go/lo"
"github.com/iotaledger/hive.go/runtime/options"
Expand Down Expand Up @@ -775,3 +776,57 @@ func Test_SpendPendingCommittedRace(t *testing.T) {
ts.AssertTransactionsExist(wallet.Transactions("tx1", "tx2"), false, node1, node2)
}
}

func Test_RootBlockShallowLike(t *testing.T) {
ts := testsuite.NewTestSuite(t,
testsuite.WithProtocolParametersOptions(
iotago.WithTimeProviderOptions(
0,
testsuite.GenesisTimeWithOffsetBySlots(1000, testsuite.DefaultSlotDurationInSeconds),
testsuite.DefaultSlotDurationInSeconds,
3,
),
iotago.WithLivenessOptions(
10,
10,
2,
4,
5,
),
),

testsuite.WithWaitFor(5*time.Second),
)
defer ts.Shutdown()

node1 := ts.AddValidatorNode("node1")
wallet := ts.AddDefaultWallet(node1)
ts.Run(true, map[string][]options.Option[protocol.Protocol]{})

tx1 := wallet.CreateBasicOutputsEquallyFromInput("tx1", 1, "Genesis:0")

ts.IssueBasicBlockWithOptions("block1", wallet, tx1, mock.WithIssuingTime(ts.API.TimeProvider().SlotStartTime(1)))
ts.IssueBasicBlockWithOptions("block2", wallet, &iotago.TaggedData{}, mock.WithIssuingTime(ts.API.TimeProvider().SlotStartTime(1)))

ts.AssertTransactionsExist(wallet.Transactions("tx1"), true, node1)

ts.AssertBlocksInCacheConflicts(map[*blocks.Block][]string{
ts.Block("block1"): {"tx1"},
}, node1)

ts.AssertTransactionInCacheConflicts(map[*iotago.Transaction][]string{
wallet.Transaction("tx1"): {"tx1"},
}, node1)

ts.IssueBlocksAtSlots("", []iotago.SlotIndex{2, 3, 4}, 2, "block", ts.Nodes(), true, false)

ts.AssertActiveRootBlocks(ts.Blocks("Genesis", "block1", "block2", "2.1-node1"), ts.Nodes()...)

ts.IssueBasicBlockWithOptions("block-shallow-like-valid", wallet, &iotago.TaggedData{}, mock.WithStrongParents(ts.BlockID("4.1-node1")), mock.WithShallowLikeParents(ts.BlockID("block1")), mock.WithIssuingTime(ts.API.TimeProvider().SlotStartTime(5)))
ts.AssertBlocksInCacheBooked(ts.Blocks("block-shallow-like-valid"), true, node1)
ts.AssertBlocksInCacheInvalid(ts.Blocks("block-shallow-like-valid"), false, node1)

ts.IssueBasicBlockWithOptions("block-shallow-like-invalid", wallet, &iotago.TaggedData{}, mock.WithStrongParents(ts.BlockID("4.1-node1")), mock.WithShallowLikeParents(ts.BlockID("block2")), mock.WithIssuingTime(ts.API.TimeProvider().SlotStartTime(5)))
ts.AssertBlocksInCacheBooked(ts.Blocks("block-shallow-like-invalid"), false, node1)
ts.AssertBlocksInCacheInvalid(ts.Blocks("block-shallow-like-invalid"), true, node1)
}

0 comments on commit 85a1029

Please sign in to comment.