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

WiP: Malfeasance v2: Handlers and Publishers #6307

Draft
wants to merge 51 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
8534e62
Add malfeasance2 package
fasmat Aug 29, 2024
b421adb
Rename malfeasance files
fasmat Aug 29, 2024
697d736
Cleanups
fasmat Aug 29, 2024
421c2ad
Add malfeasance publisher
fasmat Aug 29, 2024
a7a88de
make generate
fasmat Aug 29, 2024
52ab7f9
Cleanup
fasmat Aug 29, 2024
24e7c4d
Make generate
fasmat Oct 9, 2024
70081ad
Merge remote-tracking branch 'origin/develop' into malfeasance-v2
fasmat Oct 11, 2024
9dc9f37
Remove unneeded foreign key statement
fasmat Oct 11, 2024
8ac6735
Merge remote-tracking branch 'origin/develop' into malfeasance-v2
fasmat Oct 15, 2024
d77e0f3
Merge remote-tracking branch 'origin/develop' into malfeasance-v2
fasmat Oct 21, 2024
06c0336
Merge remote-tracking branch 'origin/develop' into malfeasance-v2
fasmat Oct 26, 2024
1e3423f
Merge remote-tracking branch 'origin/develop' into malfeasance-v2
fasmat Nov 4, 2024
e3b99be
Add code for post validation to activation malfeasance handler
fasmat Nov 4, 2024
0041145
Merge remote-tracking branch 'origin/develop' into malfeasance-v2
fasmat Nov 6, 2024
bccceea
Fix compiler complaints
fasmat Nov 6, 2024
9af0a53
Merge remote-tracking branch 'origin/develop' into malfeasance-v2
fasmat Nov 6, 2024
a4fef64
Fix missing decoder for malfeasance activation wire type
fasmat Nov 6, 2024
abf10de
Merge remote-tracking branch 'origin/develop' into malfeasance-v2
fasmat Nov 14, 2024
caf9cf4
Merge remote-tracking branch 'origin/develop' into malfeasance-v2
fasmat Nov 16, 2024
753e0c2
Merge remote-tracking branch 'origin/develop' into malfeasance-v2
fasmat Nov 18, 2024
8627bcb
Work in progress
fasmat Nov 18, 2024
9d6215c
Merge remote-tracking branch 'origin/develop' into malfeasance-v2
fasmat Nov 19, 2024
b50cee6
Merge remote-tracking branch 'origin/develop' into malfeasance-v2
fasmat Nov 19, 2024
67a30d9
Merge remote-tracking branch 'origin/develop' into malfeasance-v2
fasmat Nov 20, 2024
14de6bf
Work in progress
fasmat Nov 20, 2024
04b7068
Merge remote-tracking branch 'origin/develop' into malfeasance-v2
fasmat Nov 20, 2024
fa43109
Merge remote-tracking branch 'origin/develop' into malfeasance-v2
fasmat Nov 21, 2024
2967b17
Merge remote-tracking branch 'origin/develop' into malfeasance-v2
fasmat Nov 21, 2024
558971c
Add legacy malfeasance publisher service
fasmat Nov 25, 2024
81bd45d
Fix nil pointer exception
fasmat Nov 25, 2024
5443d74
Merge remote-tracking branch 'origin/develop' into malfeasance-v2
fasmat Nov 25, 2024
5b8a3b5
Don't 0 index proofs (to avoid none = legacy double publish)
fasmat Nov 25, 2024
a2e45cc
Update ATXv1 Handler to use dedicated malfeasance publisher
fasmat Nov 25, 2024
ce90c7a
Use dedicated publisher for ATX v1
fasmat Nov 25, 2024
baf130c
Fix dependencies during startup
fasmat Nov 25, 2024
1882f83
Add more tests and fix failing ones
fasmat Nov 25, 2024
0752c0f
Fix race in test
fasmat Nov 25, 2024
4e5d059
Merge remote-tracking branch 'origin/develop' into legacy-malfeasance…
fasmat Nov 26, 2024
641a24e
Merge branch 'legacy-malfeasance-builder' into malfeasance-v2
fasmat Nov 26, 2024
b1eee69
Add TODO
fasmat Nov 26, 2024
5123962
Merge remote-tracking branch 'origin/develop' into malfeasance-v2
fasmat Nov 28, 2024
9102e21
Merge remote-tracking branch 'origin/develop' into malfeasance-v2
fasmat Nov 29, 2024
b6a5058
Merge remote-tracking branch 'origin/develop' into malfeasance-v2
fasmat Nov 29, 2024
80bef7c
Rename handler file
fasmat Dec 3, 2024
3d782fc
Merge remote-tracking branch 'origin/develop' into malfeasance-v2
fasmat Dec 3, 2024
559fe8e
Merge remote-tracking branch 'origin/develop' into malfeasance-v2
fasmat Dec 3, 2024
a76a092
Fix typo
fasmat Dec 4, 2024
8c6bb28
Update malfeasance.Info to fetch proof from db instead of passing it …
fasmat Dec 4, 2024
9379bf7
Merge remote-tracking branch 'origin/develop' into malfeasance-v2
fasmat Dec 4, 2024
ffde644
Extend publisher
fasmat Dec 4, 2024
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: 2 additions & 0 deletions activation/e2e/atx_merge_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,7 @@ func Test_MarryAndMerge(t *testing.T) {
require.NoError(t, err)

mFetch := smocks.NewMockFetcher(ctrl)
mMalPublish := activation.NewMockatxMalfeasancePublisher(ctrl)
mLegacyPublish := activation.NewMocklegacyMalfeasancePublisher(ctrl)
mBeacon := activation.NewMockatxReceiver(ctrl)
mTortoise := smocks.NewMockTortoise(ctrl)
Expand All @@ -290,6 +291,7 @@ func Test_MarryAndMerge(t *testing.T) {
mFetch,
goldenATX,
validator,
mMalPublish,
mLegacyPublish,
mBeacon,
mTortoise,
Expand Down
2 changes: 2 additions & 0 deletions activation/e2e/builds_atx_v2_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ func TestBuilder_SwitchesToBuildV2(t *testing.T) {
edVerifier := signing.NewEdVerifier()
mpub := mocks.NewMockPublisher(ctrl)
mFetch := smocks.NewMockFetcher(ctrl)
mMalPublish := activation.NewMockatxMalfeasancePublisher(ctrl)
mLegacyPublish := activation.NewMocklegacyMalfeasancePublisher(ctrl)
mBeacon := activation.NewMockatxReceiver(ctrl)
mTortoise := smocks.NewMockTortoise(ctrl)
Expand All @@ -131,6 +132,7 @@ func TestBuilder_SwitchesToBuildV2(t *testing.T) {
mFetch,
goldenATX,
validator,
mMalPublish,
mLegacyPublish,
mBeacon,
mTortoise,
Expand Down
3 changes: 3 additions & 0 deletions activation/e2e/checkpoint_merged_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ func Test_CheckpointAfterMerge(t *testing.T) {
require.NoError(t, err)

mFetch := smocks.NewMockFetcher(ctrl)
mMalPublish := activation.NewMockatxMalfeasancePublisher(ctrl)
mLegacyPublish := activation.NewMocklegacyMalfeasancePublisher(ctrl)
mBeacon := activation.NewMockatxReceiver(ctrl)
mTortoise := smocks.NewMockTortoise(ctrl)
Expand All @@ -120,6 +121,7 @@ func Test_CheckpointAfterMerge(t *testing.T) {
mFetch,
goldenATX,
validator,
mMalPublish,
mLegacyPublish,
mBeacon,
mTortoise,
Expand Down Expand Up @@ -299,6 +301,7 @@ func Test_CheckpointAfterMerge(t *testing.T) {
mFetch,
goldenATX,
validator,
mMalPublish,
mLegacyPublish,
mBeacon,
mTortoise,
Expand Down
3 changes: 3 additions & 0 deletions activation/e2e/checkpoint_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ func TestCheckpoint_PublishingSoloATXs(t *testing.T) {
atxVersions := activation.AtxVersions{0: types.AtxV2}
edVerifier := signing.NewEdVerifier()
mFetch := smocks.NewMockFetcher(ctrl)
mMalPublish := activation.NewMockatxMalfeasancePublisher(ctrl)
mLegacyPublish := activation.NewMocklegacyMalfeasancePublisher(ctrl)
mBeacon := activation.NewMockatxReceiver(ctrl)
mTortoise := smocks.NewMockTortoise(ctrl)
Expand All @@ -116,6 +117,7 @@ func TestCheckpoint_PublishingSoloATXs(t *testing.T) {
mFetch,
goldenATX,
validator,
mMalPublish,
mLegacyPublish,
mBeacon,
mTortoise,
Expand Down Expand Up @@ -206,6 +208,7 @@ func TestCheckpoint_PublishingSoloATXs(t *testing.T) {
mFetch,
goldenATX,
validator,
mMalPublish,
mLegacyPublish,
mBeacon,
mTortoise,
Expand Down
3 changes: 2 additions & 1 deletion activation/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ func NewHandler(
fetcher system.Fetcher,
goldenATXID types.ATXID,
nipostValidator nipostValidator,
malPublisher atxMalfeasancePublisher,
legacyMalPublisher legacyMalfeasancePublisher,
beacon atxReceiver,
tortoise system.Tortoise,
Expand Down Expand Up @@ -144,7 +145,7 @@ func NewHandler(
fetcher: fetcher,
beacon: beacon,
tortoise: tortoise,
malPublisher: &MalfeasancePublisher{}, // TODO(mafa): pass real publisher when available
malPublisher: malPublisher,
},
}

Expand Down
3 changes: 1 addition & 2 deletions activation/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -215,8 +215,6 @@ func newTestHandler(tb testing.TB, goldenATXID types.ATXID, opts ...HandlerOptio
edVerifier := signing.NewEdVerifier()

mocks := newTestHandlerMocks(tb, goldenATXID)
// TODO(mafa): make mandatory parameter when real publisher is available
opts = append(opts, func(h *Handler) { h.v2.malPublisher = mocks.mMalPublish })
atxHdlr := NewHandler(
"localID",
cdb,
Expand All @@ -226,6 +224,7 @@ func newTestHandler(tb testing.TB, goldenATXID types.ATXID, opts ...HandlerOptio
mocks.mockFetch,
goldenATXID,
mocks.mValidator,
mocks.mMalPublish,
mocks.mLegacyMalPublish,
mocks.mBeacon,
mocks.mTortoise,
Expand Down
15 changes: 6 additions & 9 deletions activation/handler_v1.go
Original file line number Diff line number Diff line change
Expand Up @@ -505,22 +505,19 @@ func (h *HandlerV1) storeAtx(ctx context.Context, atx *types.ActivationTx, watx
}

atxs.AtxAdded(h.cdb, atx)
if proof != nil {
if err := h.malPublisher.PublishProof(ctx, atx.SmesherID, proof); err != nil {
return fmt.Errorf("publishing malfeasance proof: %w", err)
}
}

added := h.cacheAtx(ctx, atx, malicious || proof != nil)
h.beacon.OnAtx(atx)
if added != nil {
if added := h.cacheAtx(ctx, atx, malicious || proof != nil); added != nil {
h.tortoise.OnAtx(atx.TargetEpoch(), atx.ID(), added)
}

h.logger.Debug("finished storing atx in epoch",
zap.Stringer("atx_id", atx.ID()),
zap.Uint32("epoch_id", atx.PublishEpoch.Uint32()),
)
if proof != nil {
if err := h.malPublisher.PublishProof(ctx, atx.SmesherID, proof); err != nil {
return fmt.Errorf("publishing malfeasance proof: %w", err)
}
}
return nil
}

Expand Down
28 changes: 14 additions & 14 deletions activation/handler_v2.go
Original file line number Diff line number Diff line change
Expand Up @@ -495,8 +495,8 @@ func (n nipostSizes) sumUp() (units uint32, weight uint64, err error) {

func (h *HandlerV2) verifyIncludedIDsUniqueness(atx *wire.ActivationTxV2) error {
seen := make(map[uint32]struct{})
for _, niposts := range atx.NIPosts {
for _, post := range niposts.Posts {
for _, niPosts := range atx.NIPosts {
for _, post := range niPosts.Posts {
if _, ok := seen[post.MarriageIndex]; ok {
return fmt.Errorf("ID present twice (duplicated marriage index): %d", post.MarriageIndex)
}
Expand Down Expand Up @@ -528,7 +528,7 @@ func (h *HandlerV2) syntacticallyValidateDeps(
return nil, fmt.Errorf("fetching previous atx: %w", err)
}
if prevAtx.PublishEpoch >= atx.PublishEpoch {
err := fmt.Errorf("previous atx is too new (%d >= %d) (%s) ", prevAtx.PublishEpoch, atx.PublishEpoch, prev)
err := fmt.Errorf("previous atx (%s) is too new (%d >= %d)", prev, prevAtx.PublishEpoch, atx.PublishEpoch)
return nil, err
}
previousAtxs[i] = prevAtx
Expand All @@ -541,9 +541,9 @@ func (h *HandlerV2) syntacticallyValidateDeps(

// validate previous ATXs
nipostSizes := make(nipostSizes, len(atx.NIPosts))
for i, niposts := range atx.NIPosts {
for i, niPosts := range atx.NIPosts {
nipostSizes[i] = new(nipostSize)
for _, post := range niposts.Posts {
for _, post := range niPosts.Posts {
if post.MarriageIndex >= uint32(len(equivocationSet)) {
err := fmt.Errorf("marriage index out of bounds: %d > %d", post.MarriageIndex, len(equivocationSet)-1)
return nil, err
Expand All @@ -563,11 +563,11 @@ func (h *HandlerV2) syntacticallyValidateDeps(
}

// validate poet membership proofs
for i, niposts := range atx.NIPosts {
for i, niPosts := range atx.NIPosts {
// verify PoET memberships in a single go
indexedChallenges := make(map[uint64][]byte)

for _, post := range niposts.Posts {
for _, post := range niPosts.Posts {
if _, ok := indexedChallenges[post.MembershipLeafIndex]; ok {
continue
}
Expand All @@ -591,10 +591,10 @@ func (h *HandlerV2) syntacticallyValidateDeps(
}

membership := types.MultiMerkleProof{
Nodes: niposts.Membership.Nodes,
Nodes: niPosts.Membership.Nodes,
LeafIndices: leafIndices,
}
leaves, err := h.nipostValidator.PoetMembership(ctx, &membership, niposts.Challenge, poetChallenges)
leaves, err := h.nipostValidator.PoetMembership(ctx, &membership, niPosts.Challenge, poetChallenges)
if err != nil {
return nil, fmt.Errorf("validating poet membership: %w", err)
}
Expand All @@ -606,7 +606,7 @@ func (h *HandlerV2) syntacticallyValidateDeps(
return nil, err
}

// validate all niposts
// validate all NIPoSTs
if atx.Initial != nil {
commitment := atx.Initial.CommitmentATX
nipostIdx := 0
Expand All @@ -625,8 +625,8 @@ func (h *HandlerV2) syntacticallyValidateDeps(
}

var smesherCommitment *types.ATXID
for idx, niposts := range atx.NIPosts {
for _, post := range niposts.Posts {
for idx, niPosts := range atx.NIPosts {
for _, post := range niPosts.Posts {
id := equivocationSet[post.MarriageIndex]
commitment, err := atxs.CommitmentATX(h.cdb, id)
if err != nil {
Expand All @@ -635,7 +635,7 @@ func (h *HandlerV2) syntacticallyValidateDeps(
if id == atx.SmesherID {
smesherCommitment = &commitment
}
if err := h.validatePost(ctx, id, atx, commitment, niposts.Challenge, post, idx); err != nil {
if err := h.validatePost(ctx, id, atx, commitment, niPosts.Challenge, post, idx); err != nil {
return nil, err
}
result.ids[id] = idData{
Expand Down Expand Up @@ -847,7 +847,7 @@ func (h *HandlerV2) checkDoubleMerge(ctx context.Context, tx sql.Transaction, at
if err != nil {
return true, fmt.Errorf("creating double merge proof: %w", err)
}
return true, h.malPublisher.Publish(ctx, atx.SmesherID, proof)
return true, h.malPublisher.Publish(ctx, atx.ActivationTxV2.SmesherID, proof)
}

func (h *HandlerV2) checkPrevAtx(ctx context.Context, tx sql.Transaction, atx *activationTx) (bool, error) {
Expand Down
5 changes: 3 additions & 2 deletions activation/handler_v2_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package activation
import (
"context"
"errors"
"fmt"
"math"
"slices"
"testing"
Expand Down Expand Up @@ -996,7 +997,7 @@ func TestHandlerV2_ProcessMergedATX(t *testing.T) {
gomock.Any(),
merged.SmesherID,
gomock.AssignableToTypeOf(&wire.ProofDoubleMerge{}),
).DoAndReturn(func(ctx context.Context, id types.NodeID, proof wire.Proof) error {
).DoAndReturn(func(ctx context.Context, _ types.NodeID, proof wire.Proof) error {
malProof := proof.(*wire.ProofDoubleMerge)
nId, err := malProof.Valid(context.Background(), verifier)
require.NoError(t, err)
Expand Down Expand Up @@ -1504,7 +1505,7 @@ func TestHandlerV2_SyntacticallyValidateDeps(t *testing.T) {
atx.Sign(sig)

_, err := atxHandler.syntacticallyValidateDeps(context.Background(), atx)
require.ErrorContains(t, err, "previous atx is too new")
require.ErrorContains(t, err, fmt.Sprintf("previous atx (%s) is too new", prev.ID()))
})
t.Run("previous ATX by different smesher", func(t *testing.T) {
atxHandler := newV2TestHandler(t, golden)
Expand Down
8 changes: 8 additions & 0 deletions activation/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,14 @@ type atxMalfeasancePublisher interface {
Publish(ctx context.Context, nodeID types.NodeID, proof wire.Proof) error
}

// malfeasancePublisher is an interface for publishing malfeasance proofs.
//
// Additionally the publisher will only gossip proofs when the node is in sync, otherwise it will only store them
// and mark the associated identity as malfeasant.
type malfeasancePublisher interface {
PublishATXProof(ctx context.Context, nodeID types.NodeID, proof []byte) error
}

type atxProvider interface {
GetAtx(id types.ATXID) (*types.ActivationTx, error)
}
Expand Down
108 changes: 103 additions & 5 deletions activation/malfeasance2.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,113 @@ package activation

import (
"context"
"fmt"

"github.com/spacemeshos/go-spacemesh/activation/wire"
"github.com/spacemeshos/go-spacemesh/codec"
"github.com/spacemeshos/go-spacemesh/common/types"
"github.com/spacemeshos/go-spacemesh/signing"
)

// MalfeasancePublisher is the publisher for ATX proofs.
type MalfeasancePublisher struct{}
type MalfeasanceHandlerV2 struct {
malPublisher malfeasancePublisher
edVerifier *signing.EdVerifier
validator nipostValidatorV2
}

func NewMalfeasanceHandlerV2(
malPublisher malfeasancePublisher,
edVerifier *signing.EdVerifier,
validator nipostValidatorV2,
) *MalfeasanceHandlerV2 {
return &MalfeasanceHandlerV2{
malPublisher: malPublisher,
edVerifier: edVerifier,
validator: validator,
}
}

// Publish publishes an ATX proof by encoding it and sending it to the malfeasance publisher.
func (p *MalfeasanceHandlerV2) Publish(ctx context.Context, nodeID types.NodeID, proof wire.Proof) error {
atxProof := &wire.ATXProof{
Version: 0x01, // for now we only have one version
ProofType: proof.Type(),

Proof: codec.MustEncode(proof),
}

return p.malPublisher.PublishATXProof(ctx, nodeID, codec.MustEncode(atxProof))
}

func (mh *MalfeasanceHandlerV2) PostIndex(
ctx context.Context,
smesherID types.NodeID,
commitment types.ATXID,
post *types.Post,
challenge []byte,
numUnits uint32,
idx int,
) error {
return mh.validator.PostV2(ctx, smesherID, commitment, post, challenge, numUnits, PostIndex(idx))
}

func (p *MalfeasancePublisher) Publish(ctx context.Context, id types.NodeID, proof wire.Proof) error {
// TODO(mafa): implement me
return nil
func (mh *MalfeasanceHandlerV2) Signature(d signing.Domain, nodeID types.NodeID, m []byte, sig types.EdSignature) bool {
return mh.edVerifier.Verify(d, nodeID, m, sig)
}

// TODO(mafa): call this validate in the malfeasance handler in `malfeasance` package for publish/gossip:
// - do not publishing proofs for identities managed by node
// - validate and persist before publishing
// - do not handle incoming proofs from peer == `self`
func (mh *MalfeasanceHandlerV2) Validate(ctx context.Context, data []byte) (types.NodeID, error) {
var atxProof wire.ATXProof
if err := codec.Decode(data, &atxProof); err != nil {
return types.EmptyNodeID, fmt.Errorf("decoding ATX malfeasance proof: %w", err)
}

proof, err := atxProof.Decode()
if err != nil {
return types.EmptyNodeID, fmt.Errorf("decoding ATX malfeasance proof: %w", err)
}

id, err := proof.Valid(ctx, mh)
if err != nil {
return types.EmptyNodeID, fmt.Errorf("validating ATX malfeasance proof: %w", err)
}
return id, nil
}

// TODO(mafa): this roughly how the general publisher looks like
//
// func Publish(ctx context.Context, smesherID types.NodeID, data []byte) error {
// // Combine IDs from the present equivocation set for atx.SmesherID and IDs in atx.Marriages.
// set, err := identities.EquivocationSet(mh.cdb, nodeID)
// if err != nil {
// return fmt.Errorf("getting equivocation set: %w", err)
// }
// for _, id := range set {
// if err := identities.SetMalicious(mh.cdb, id, encoded, time.Now()); err != nil {
// return fmt.Errorf("adding malfeasance proof: %w", err)
// }

// mh.cdb.CacheMalfeasanceProof(id, proof)
// mh.tortoise.OnMalfeasance(id)
// }

// if !mh.syncer.ListenToATXGossip() {
// // we are not gossiping proofs when we are not listening to ATX gossip
// return nil
// }

// gossip := mwire.MalfeasanceProofV2{
// Layer: mh.clock.CurrentLayer(),
// ProofType: mwire.InvalidActivation,
// Proof: data,
// }

// if err := mh.publisher.Publish(ctx, pubsub.MalfeasanceProof, codec.MustEncode(&gossip)); err != nil {
// mh.logger.Error("failed to broadcast malfeasance proof", zap.Error(err))
// return fmt.Errorf("broadcast atx malfeasance proof: %w", err)
// }
// return nil
// }
Loading
Loading