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

[Merged by Bors] - use tortoise data to enforce that referenced atxs are available when processing block #4966

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions blocks/generator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,10 +93,6 @@ func genTx(t testing.TB, signer *signing.EdSigner, dest types.Address, amount, n
return tx
}

func createTransactions(tb testing.TB, numOfTxs int) []types.TransactionID {
return createAndSaveTxs(tb, numOfTxs, nil)
}

func createAndSaveTxs(tb testing.TB, numOfTxs int, db sql.Executor) []types.TransactionID {
tb.Helper()
txIDs := make([]types.TransactionID, 0, numOfTxs)
Expand Down
60 changes: 37 additions & 23 deletions blocks/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,19 @@ import (
)

var (
errMalformedData = fmt.Errorf("%w: malformed data", pubsub.ErrValidationReject)
errWrongHash = fmt.Errorf("%w: incorrect hash", pubsub.ErrValidationReject)
errInvalidRewards = errors.New("invalid rewards")
errDuplicateTX = errors.New("duplicate TxID in proposal")
errMalformedData = fmt.Errorf("%w: malformed data", pubsub.ErrValidationReject)
errWrongHash = fmt.Errorf("%w: incorrect hash", pubsub.ErrValidationReject)
errDuplicateTX = errors.New("duplicate TxID in proposal")
)

// Handler processes Block fetched from peers during sync.
type Handler struct {
logger log.Log

fetcher system.Fetcher
db *sql.Database
mesh meshProvider
fetcher system.Fetcher
db *sql.Database
tortoise tortoiseProvider
mesh meshProvider
}

// Opt for configuring BlockHandler.
Expand All @@ -42,12 +42,13 @@ func WithLogger(logger log.Log) Opt {
}

// NewHandler creates new Handler.
func NewHandler(f system.Fetcher, db *sql.Database, m meshProvider, opts ...Opt) *Handler {
func NewHandler(f system.Fetcher, db *sql.Database, tortoise tortoiseProvider, m meshProvider, opts ...Opt) *Handler {
h := &Handler{
logger: log.NewNop(),
fetcher: f,
db: db,
mesh: m,
logger: log.NewNop(),
fetcher: f,
db: db,
tortoise: tortoise,
mesh: m,
}
for _, opt := range opts {
opt(h)
Expand All @@ -61,7 +62,7 @@ func (h *Handler) HandleSyncedBlock(ctx context.Context, expHash types.Hash32, p

var b types.Block
if err := codec.Decode(data, &b); err != nil {
logger.With().Error("malformed block", log.Err(err))
logger.With().Debug("malformed block", log.Err(err))
return errMalformedData
}
// set the block ID when received
Expand All @@ -72,11 +73,11 @@ func (h *Handler) HandleSyncedBlock(ctx context.Context, expHash types.Hash32, p
}

if b.LayerIndex <= types.GetEffectiveGenesis() {
return fmt.Errorf("block before effective genesis: layer %v", b.LayerIndex)
return fmt.Errorf("%w: block before effective genesis: layer %v", pubsub.ErrValidationReject, b.LayerIndex)
}

if err := ValidateRewards(b.Rewards); err != nil {
return fmt.Errorf("%w: %w", errInvalidRewards, err)
return fmt.Errorf("%w: %s", pubsub.ErrValidationReject, err.Error())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a new API for this introduced in Go 1.20:

Suggested change
return fmt.Errorf("%w: %s", pubsub.ErrValidationReject, err.Error())
return errors.Join(pubsub.ErrValidationReject, err)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i don't think that all errors should be chained by default. error is a part of api, so that it can be handled properly. nobody cares about errInvalidRewards (should have been just string), thats just an information. pubsub.ErrValidationReject part of api

}

logger = logger.WithFields(b.ID(), b.LayerIndex)
Expand All @@ -89,17 +90,22 @@ func (h *Handler) HandleSyncedBlock(ctx context.Context, expHash types.Hash32, p
}
logger.With().Info("new block")

h.fetcher.RegisterPeerHashes(peer, types.TransactionIDsToHashes(b.TxIDs))
if err := h.checkTransactions(ctx, &b); err != nil {
logger.With().Warning("failed to fetch block TXs", log.Err(err))
return err
if missing := h.tortoise.GetMissingActiveSet(b.LayerIndex.GetEpoch(), toAtxIDs(b.Rewards)); len(missing) > 0 {
h.fetcher.RegisterPeerHashes(peer, types.ATXIDsToHashes(missing))
if err := h.fetcher.GetAtxs(ctx, missing); err != nil {
return err
}
}
if len(b.TxIDs) > 0 {
if err := h.checkTransactions(ctx, peer, &b); err != nil {
logger.With().Warning("failed to fetch block TXs", log.Err(err))
return err
}
}

if err := h.mesh.AddBlockWithTXs(ctx, &b); err != nil {
logger.With().Error("failed to save block", log.Err(err))
return fmt.Errorf("save block: %w", err)
}

return nil
}

Expand All @@ -121,20 +127,28 @@ func ValidateRewards(rewards []types.AnyReward) error {
return nil
}

func (h *Handler) checkTransactions(ctx context.Context, b *types.Block) error {
func (h *Handler) checkTransactions(ctx context.Context, peer p2p.Peer, b *types.Block) error {
if len(b.TxIDs) == 0 {
return nil
}

set := make(map[types.TransactionID]struct{}, len(b.TxIDs))
for _, tx := range b.TxIDs {
if _, exist := set[tx]; exist {
return errDuplicateTX
}
set[tx] = struct{}{}
}
h.fetcher.RegisterPeerHashes(peer, types.TransactionIDsToHashes(b.TxIDs))
if err := h.fetcher.GetBlockTxs(ctx, b.TxIDs); err != nil {
return fmt.Errorf("block get TXs: %w", err)
}
return nil
}

func toAtxIDs(rewards []types.AnyReward) []types.ATXID {
rst := make([]types.ATXID, 0, len(rewards))
for i := range rewards {
rst = append(rst, rewards[i].AtxID)
}
return rst
}
Loading