Skip to content

Commit

Permalink
Add double marry malfeasance proof (#6219)
Browse files Browse the repository at this point in the history
## Motivation

Closes #6218
  • Loading branch information
fasmat committed Aug 7, 2024
1 parent 00d476f commit 83b0499
Show file tree
Hide file tree
Showing 19 changed files with 857 additions and 175 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -708,7 +708,7 @@ and permanent ineligibility for rewards.

* [#5470](https://github.com/spacemeshos/go-spacemesh/pull/5470)
Fixed a bug in event reporting where the node reports a disconnection from the PoST service as a "PoST failed" event.
Disconnections cannot be avoided completely and do not interrupt the PoST proofing process. As long as the PoST
Disconnections cannot be avoided completely and do not interrupt the PoST proving process. As long as the PoST
service reconnects within a reasonable time, the node will continue to operate normally without reporting any errors
via the event API.

Expand Down
4 changes: 2 additions & 2 deletions activation/builder_v2_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ func TestBuilder_BuildsInitialAtxV2(t *testing.T) {
require.Empty(t, atx.Marriages)
require.Equal(t, posEpoch+1, atx.PublishEpoch)
require.Equal(t, sig.NodeID(), atx.SmesherID)
require.True(t, signing.NewEdVerifier().Verify(signing.ATX, atx.SmesherID, atx.SignedBytes(), atx.Signature))
require.True(t, signing.NewEdVerifier().Verify(signing.ATX, atx.SmesherID, atx.ID().Bytes(), atx.Signature))
}

func TestBuilder_SwitchesToBuildV2(t *testing.T) {
Expand Down Expand Up @@ -106,5 +106,5 @@ func TestBuilder_SwitchesToBuildV2(t *testing.T) {
require.Empty(t, atx2.Marriages)
require.Equal(t, atx1.PublishEpoch+1, atx2.PublishEpoch)
require.Equal(t, sig.NodeID(), atx2.SmesherID)
require.True(t, signing.NewEdVerifier().Verify(signing.ATX, atx2.SmesherID, atx2.SignedBytes(), atx2.Signature))
require.True(t, signing.NewEdVerifier().Verify(signing.ATX, atx2.SmesherID, atx2.ID().Bytes(), atx2.Signature))
}
39 changes: 23 additions & 16 deletions activation/handler_v2.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (

"github.com/spacemeshos/go-spacemesh/activation/wire"
"github.com/spacemeshos/go-spacemesh/atxsdata"
"github.com/spacemeshos/go-spacemesh/codec"
"github.com/spacemeshos/go-spacemesh/common/types"
"github.com/spacemeshos/go-spacemesh/datastore"
"github.com/spacemeshos/go-spacemesh/events"
Expand Down Expand Up @@ -626,9 +627,7 @@ func (h *HandlerV2) syntacticallyValidateDeps(
zap.Int("index", invalidIdx.Index),
)
// TODO(mafa): finish proof
proof := &wire.ATXProof{
ProofType: wire.InvalidPost,
}
var proof wire.Proof
if err := h.malPublisher.Publish(ctx, id, proof); err != nil {
return nil, fmt.Errorf("publishing malfeasance proof for invalid post: %w", err)
}
Expand Down Expand Up @@ -669,7 +668,7 @@ func (h *HandlerV2) checkMalicious(
return nil
}

malicious, err = h.checkDoubleMarry(ctx, tx, watx.ID(), marrying)
malicious, err = h.checkDoubleMarry(ctx, tx, watx, marrying)
if err != nil {
return fmt.Errorf("checking double marry: %w", err)
}
Expand Down Expand Up @@ -704,18 +703,31 @@ func (h *HandlerV2) checkMalicious(
func (h *HandlerV2) checkDoubleMarry(
ctx context.Context,
tx *sql.Tx,
atxID types.ATXID,
atx *wire.ActivationTxV2,
marrying []marriage,
) (bool, error) {
for _, m := range marrying {
mATX, err := identities.MarriageATX(tx, m.id)
if err != nil {
return false, fmt.Errorf("checking if ID is married: %w", err)
}
if mATX != atxID {
// TODO(mafa): finish proof
proof := &wire.ATXProof{
ProofType: wire.DoubleMarry,
if mATX != atx.ID() {
var blob sql.Blob
v, err := atxs.LoadBlob(ctx, tx, mATX.Bytes(), &blob)
if err != nil {
return true, fmt.Errorf("creating double marry proof: %w", err)
}
if v != types.AtxV2 {
h.logger.Fatal("Failed to create double marry malfeasance proof: ATX is not v2",
zap.Stringer("atx_id", mATX),
)
}
var otherAtx wire.ActivationTxV2
codec.MustDecode(blob.Bytes, &otherAtx)

proof, err := wire.NewDoubleMarryProof(tx, atx, &otherAtx, m.id)
if err != nil {
return true, fmt.Errorf("creating double marry proof: %w", err)
}
return true, h.malPublisher.Publish(ctx, m.id, proof)
}
Expand Down Expand Up @@ -747,9 +759,7 @@ func (h *HandlerV2) checkDoublePost(
zap.Uint32("epoch", atx.PublishEpoch.Uint32()),
)
// TODO(mafa): finish proof
proof := &wire.ATXProof{
ProofType: wire.DoublePublish,
}
var proof wire.Proof
return true, h.malPublisher.Publish(ctx, id, proof)
}
return false, nil
Expand All @@ -776,10 +786,7 @@ func (h *HandlerV2) checkDoubleMerge(ctx context.Context, tx *sql.Tx, watx *wire
zap.Stringer("smesher_id", watx.SmesherID),
)

// TODO(mafa): finish proof
proof := &wire.ATXProof{
ProofType: wire.DoubleMerge,
}
var proof wire.Proof
return true, h.malPublisher.Publish(ctx, watx.SmesherID, proof)
}

Expand Down
30 changes: 13 additions & 17 deletions activation/handler_v2_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (

"github.com/spacemeshos/go-spacemesh/activation/wire"
"github.com/spacemeshos/go-spacemesh/atxsdata"
"github.com/spacemeshos/go-spacemesh/codec"
"github.com/spacemeshos/go-spacemesh/common/types"
"github.com/spacemeshos/go-spacemesh/datastore"
"github.com/spacemeshos/go-spacemesh/fetch"
Expand Down Expand Up @@ -1561,17 +1562,7 @@ func TestHandlerV2_SyntacticallyValidateDeps(t *testing.T) {
gomock.Any(),
).
Return(verifying.ErrInvalidIndex{Index: 7})
atxHandler.mMalPublish.EXPECT().Publish(
gomock.Any(),
sig.NodeID(),
gomock.Cond(func(data any) bool {
proof, ok := data.(*wire.ATXProof)
if !ok {
return false
}
return proof.ProofType == wire.InvalidPost
}),
)
atxHandler.mMalPublish.EXPECT().Publish(gomock.Any(), sig.NodeID(), gomock.Any())
_, err := atxHandler.syntacticallyValidateDeps(context.Background(), atx)
vErr := &verifying.ErrInvalidIndex{}
require.ErrorAs(t, err, vErr)
Expand Down Expand Up @@ -1719,13 +1710,18 @@ func Test_Marriages(t *testing.T) {
gomock.Any(),
sig.NodeID(),
gomock.Cond(func(data any) bool {
proof, ok := data.(*wire.ATXProof)
if !ok {
return false
}
return proof.ProofType == wire.DoubleMarry
_, ok := data.(*wire.ProofDoubleMarry)
return ok
}),
)
).DoAndReturn(func(ctx context.Context, id types.NodeID, proof wire.Proof) error {
malProof := proof.(*wire.ProofDoubleMarry)
nId, err := malProof.Valid(atxHandler.edVerifier)
require.NoError(t, err)
require.Equal(t, sig.NodeID(), nId)
b := codec.MustEncode(malProof)
_ = b
return nil
})
err = atxHandler.processATX(context.Background(), "", atx2, time.Now())
require.NoError(t, err)

Expand Down
2 changes: 1 addition & 1 deletion activation/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ type syncer interface {
// 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 {
Publish(ctx context.Context, id types.NodeID, proof *wire.ATXProof) error
Publish(ctx context.Context, id types.NodeID, proof wire.Proof) error
}

type atxProvider interface {
Expand Down
2 changes: 1 addition & 1 deletion activation/malfeasance2.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
// MalfeasancePublisher is the publisher for ATX proofs.
type MalfeasancePublisher struct{}

func (p *MalfeasancePublisher) Publish(ctx context.Context, id types.NodeID, proof *wire.ATXProof) error {
func (p *MalfeasancePublisher) Publish(ctx context.Context, id types.NodeID, proof wire.Proof) error {
// TODO(mafa): implement me
return nil
}
6 changes: 3 additions & 3 deletions activation/mocks.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion activation/nipost_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ func Test_NIPost_PostClientHandling(t *testing.T) {
})

t.Run("connect, disconnect, then cancel before reconnect", func(t *testing.T) {
// post client connects, starts post, disconnects in between and proofing is canceled before reconnection
// post client connects, starts post, disconnects in between and proving is canceled before reconnection
sig, err := signing.NewEdSigner()
require.NoError(t, err)

Expand Down
47 changes: 40 additions & 7 deletions activation/wire/malfeasance.go
Original file line number Diff line number Diff line change
@@ -1,27 +1,60 @@
package wire

import (
"github.com/spacemeshos/go-scale"

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

//go:generate scalegen

// MerkleTreeIndex is the index of the leaf containing the given field in the merkle tree.
type MerkleTreeIndex uint16

const (
PublishEpochIndex MerkleTreeIndex = iota
PositioningATXIndex
CoinbaseIndex
InitialPostIndex
PreviousATXsRootIndex
NIPostsRootIndex
VRFNonceIndex
MarriagesRootIndex
MarriageATXIndex
)

// ProofType is an identifier for the type of proof that is encoded in the ATXProof.
type ProofType byte

const (
DoublePublish ProofType = iota + 1
DoubleMarry
DoubleMerge
InvalidPost
// TODO(mafa): legacy types for future migration to new malfeasance proofs.
LegacyDoublePublish ProofType = 0x00
LegacyInvalidPost ProofType = 0x01
LegacyInvalidPrevATX ProofType = 0x02

DoublePublish ProofType = 0x10
DoubleMarry ProofType = 0x11
DoubleMerge ProofType = 0x12
InvalidPost ProofType = 0x13
)

// ProofVersion is an identifier for the version of the proof that is encoded in the ATXProof.
type ProofVersion byte

type ATXProof struct {
// LayerID is the layer in which the proof was created. This can be used to implement different versions of the ATX
// proof in the future.
Layer types.LayerID
// Version is the version identifier of the proof. This can be used to extend the ATX proof in the future.
Version ProofVersion
// ProofType is the type of proof that is being provided.
ProofType ProofType
// Proof is the actual proof. Its type depends on the ProofType.
Proof []byte `scale:"max=1048576"` // max size of proof is 1MiB
}

// Proof is an interface for all types of proofs that can be provided in an ATXProof.
// Generally the proof should be able to validate itself and be scale encoded.
type Proof interface {
scale.Encodable

Valid(edVerifier *signing.EdVerifier) (types.NodeID, error)
}
Loading

0 comments on commit 83b0499

Please sign in to comment.