Skip to content

Commit 83b0499

Browse files
committed
Add double marry malfeasance proof (#6219)
## Motivation Closes #6218
1 parent 00d476f commit 83b0499

19 files changed

+857
-175
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -708,7 +708,7 @@ and permanent ineligibility for rewards.
708708

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

activation/builder_v2_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ func TestBuilder_BuildsInitialAtxV2(t *testing.T) {
6868
require.Empty(t, atx.Marriages)
6969
require.Equal(t, posEpoch+1, atx.PublishEpoch)
7070
require.Equal(t, sig.NodeID(), atx.SmesherID)
71-
require.True(t, signing.NewEdVerifier().Verify(signing.ATX, atx.SmesherID, atx.SignedBytes(), atx.Signature))
71+
require.True(t, signing.NewEdVerifier().Verify(signing.ATX, atx.SmesherID, atx.ID().Bytes(), atx.Signature))
7272
}
7373

7474
func TestBuilder_SwitchesToBuildV2(t *testing.T) {
@@ -106,5 +106,5 @@ func TestBuilder_SwitchesToBuildV2(t *testing.T) {
106106
require.Empty(t, atx2.Marriages)
107107
require.Equal(t, atx1.PublishEpoch+1, atx2.PublishEpoch)
108108
require.Equal(t, sig.NodeID(), atx2.SmesherID)
109-
require.True(t, signing.NewEdVerifier().Verify(signing.ATX, atx2.SmesherID, atx2.SignedBytes(), atx2.Signature))
109+
require.True(t, signing.NewEdVerifier().Verify(signing.ATX, atx2.SmesherID, atx2.ID().Bytes(), atx2.Signature))
110110
}

activation/handler_v2.go

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818

1919
"github.com/spacemeshos/go-spacemesh/activation/wire"
2020
"github.com/spacemeshos/go-spacemesh/atxsdata"
21+
"github.com/spacemeshos/go-spacemesh/codec"
2122
"github.com/spacemeshos/go-spacemesh/common/types"
2223
"github.com/spacemeshos/go-spacemesh/datastore"
2324
"github.com/spacemeshos/go-spacemesh/events"
@@ -626,9 +627,7 @@ func (h *HandlerV2) syntacticallyValidateDeps(
626627
zap.Int("index", invalidIdx.Index),
627628
)
628629
// TODO(mafa): finish proof
629-
proof := &wire.ATXProof{
630-
ProofType: wire.InvalidPost,
631-
}
630+
var proof wire.Proof
632631
if err := h.malPublisher.Publish(ctx, id, proof); err != nil {
633632
return nil, fmt.Errorf("publishing malfeasance proof for invalid post: %w", err)
634633
}
@@ -669,7 +668,7 @@ func (h *HandlerV2) checkMalicious(
669668
return nil
670669
}
671670

672-
malicious, err = h.checkDoubleMarry(ctx, tx, watx.ID(), marrying)
671+
malicious, err = h.checkDoubleMarry(ctx, tx, watx, marrying)
673672
if err != nil {
674673
return fmt.Errorf("checking double marry: %w", err)
675674
}
@@ -704,18 +703,31 @@ func (h *HandlerV2) checkMalicious(
704703
func (h *HandlerV2) checkDoubleMarry(
705704
ctx context.Context,
706705
tx *sql.Tx,
707-
atxID types.ATXID,
706+
atx *wire.ActivationTxV2,
708707
marrying []marriage,
709708
) (bool, error) {
710709
for _, m := range marrying {
711710
mATX, err := identities.MarriageATX(tx, m.id)
712711
if err != nil {
713712
return false, fmt.Errorf("checking if ID is married: %w", err)
714713
}
715-
if mATX != atxID {
716-
// TODO(mafa): finish proof
717-
proof := &wire.ATXProof{
718-
ProofType: wire.DoubleMarry,
714+
if mATX != atx.ID() {
715+
var blob sql.Blob
716+
v, err := atxs.LoadBlob(ctx, tx, mATX.Bytes(), &blob)
717+
if err != nil {
718+
return true, fmt.Errorf("creating double marry proof: %w", err)
719+
}
720+
if v != types.AtxV2 {
721+
h.logger.Fatal("Failed to create double marry malfeasance proof: ATX is not v2",
722+
zap.Stringer("atx_id", mATX),
723+
)
724+
}
725+
var otherAtx wire.ActivationTxV2
726+
codec.MustDecode(blob.Bytes, &otherAtx)
727+
728+
proof, err := wire.NewDoubleMarryProof(tx, atx, &otherAtx, m.id)
729+
if err != nil {
730+
return true, fmt.Errorf("creating double marry proof: %w", err)
719731
}
720732
return true, h.malPublisher.Publish(ctx, m.id, proof)
721733
}
@@ -747,9 +759,7 @@ func (h *HandlerV2) checkDoublePost(
747759
zap.Uint32("epoch", atx.PublishEpoch.Uint32()),
748760
)
749761
// TODO(mafa): finish proof
750-
proof := &wire.ATXProof{
751-
ProofType: wire.DoublePublish,
752-
}
762+
var proof wire.Proof
753763
return true, h.malPublisher.Publish(ctx, id, proof)
754764
}
755765
return false, nil
@@ -776,10 +786,7 @@ func (h *HandlerV2) checkDoubleMerge(ctx context.Context, tx *sql.Tx, watx *wire
776786
zap.Stringer("smesher_id", watx.SmesherID),
777787
)
778788

779-
// TODO(mafa): finish proof
780-
proof := &wire.ATXProof{
781-
ProofType: wire.DoubleMerge,
782-
}
789+
var proof wire.Proof
783790
return true, h.malPublisher.Publish(ctx, watx.SmesherID, proof)
784791
}
785792

activation/handler_v2_test.go

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717

1818
"github.com/spacemeshos/go-spacemesh/activation/wire"
1919
"github.com/spacemeshos/go-spacemesh/atxsdata"
20+
"github.com/spacemeshos/go-spacemesh/codec"
2021
"github.com/spacemeshos/go-spacemesh/common/types"
2122
"github.com/spacemeshos/go-spacemesh/datastore"
2223
"github.com/spacemeshos/go-spacemesh/fetch"
@@ -1561,17 +1562,7 @@ func TestHandlerV2_SyntacticallyValidateDeps(t *testing.T) {
15611562
gomock.Any(),
15621563
).
15631564
Return(verifying.ErrInvalidIndex{Index: 7})
1564-
atxHandler.mMalPublish.EXPECT().Publish(
1565-
gomock.Any(),
1566-
sig.NodeID(),
1567-
gomock.Cond(func(data any) bool {
1568-
proof, ok := data.(*wire.ATXProof)
1569-
if !ok {
1570-
return false
1571-
}
1572-
return proof.ProofType == wire.InvalidPost
1573-
}),
1574-
)
1565+
atxHandler.mMalPublish.EXPECT().Publish(gomock.Any(), sig.NodeID(), gomock.Any())
15751566
_, err := atxHandler.syntacticallyValidateDeps(context.Background(), atx)
15761567
vErr := &verifying.ErrInvalidIndex{}
15771568
require.ErrorAs(t, err, vErr)
@@ -1719,13 +1710,18 @@ func Test_Marriages(t *testing.T) {
17191710
gomock.Any(),
17201711
sig.NodeID(),
17211712
gomock.Cond(func(data any) bool {
1722-
proof, ok := data.(*wire.ATXProof)
1723-
if !ok {
1724-
return false
1725-
}
1726-
return proof.ProofType == wire.DoubleMarry
1713+
_, ok := data.(*wire.ProofDoubleMarry)
1714+
return ok
17271715
}),
1728-
)
1716+
).DoAndReturn(func(ctx context.Context, id types.NodeID, proof wire.Proof) error {
1717+
malProof := proof.(*wire.ProofDoubleMarry)
1718+
nId, err := malProof.Valid(atxHandler.edVerifier)
1719+
require.NoError(t, err)
1720+
require.Equal(t, sig.NodeID(), nId)
1721+
b := codec.MustEncode(malProof)
1722+
_ = b
1723+
return nil
1724+
})
17291725
err = atxHandler.processATX(context.Background(), "", atx2, time.Now())
17301726
require.NoError(t, err)
17311727

activation/interface.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ type syncer interface {
101101
// Additionally the publisher will only gossip proofs when the node is in sync, otherwise it will only store them.
102102
// and mark the associated identity as malfeasant.
103103
type malfeasancePublisher interface {
104-
Publish(ctx context.Context, id types.NodeID, proof *wire.ATXProof) error
104+
Publish(ctx context.Context, id types.NodeID, proof wire.Proof) error
105105
}
106106

107107
type atxProvider interface {

activation/malfeasance2.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import (
1010
// MalfeasancePublisher is the publisher for ATX proofs.
1111
type MalfeasancePublisher struct{}
1212

13-
func (p *MalfeasancePublisher) Publish(ctx context.Context, id types.NodeID, proof *wire.ATXProof) error {
13+
func (p *MalfeasancePublisher) Publish(ctx context.Context, id types.NodeID, proof wire.Proof) error {
1414
// TODO(mafa): implement me
1515
return nil
1616
}

activation/mocks.go

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

activation/nipost_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ func Test_NIPost_PostClientHandling(t *testing.T) {
214214
})
215215

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

activation/wire/malfeasance.go

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,60 @@
11
package wire
22

33
import (
4+
"github.com/spacemeshos/go-scale"
5+
46
"github.com/spacemeshos/go-spacemesh/common/types"
7+
"github.com/spacemeshos/go-spacemesh/signing"
58
)
69

710
//go:generate scalegen
811

12+
// MerkleTreeIndex is the index of the leaf containing the given field in the merkle tree.
13+
type MerkleTreeIndex uint16
14+
15+
const (
16+
PublishEpochIndex MerkleTreeIndex = iota
17+
PositioningATXIndex
18+
CoinbaseIndex
19+
InitialPostIndex
20+
PreviousATXsRootIndex
21+
NIPostsRootIndex
22+
VRFNonceIndex
23+
MarriagesRootIndex
24+
MarriageATXIndex
25+
)
26+
927
// ProofType is an identifier for the type of proof that is encoded in the ATXProof.
1028
type ProofType byte
1129

1230
const (
13-
DoublePublish ProofType = iota + 1
14-
DoubleMarry
15-
DoubleMerge
16-
InvalidPost
31+
// TODO(mafa): legacy types for future migration to new malfeasance proofs.
32+
LegacyDoublePublish ProofType = 0x00
33+
LegacyInvalidPost ProofType = 0x01
34+
LegacyInvalidPrevATX ProofType = 0x02
35+
36+
DoublePublish ProofType = 0x10
37+
DoubleMarry ProofType = 0x11
38+
DoubleMerge ProofType = 0x12
39+
InvalidPost ProofType = 0x13
1740
)
1841

42+
// ProofVersion is an identifier for the version of the proof that is encoded in the ATXProof.
43+
type ProofVersion byte
44+
1945
type ATXProof struct {
20-
// LayerID is the layer in which the proof was created. This can be used to implement different versions of the ATX
21-
// proof in the future.
22-
Layer types.LayerID
46+
// Version is the version identifier of the proof. This can be used to extend the ATX proof in the future.
47+
Version ProofVersion
2348
// ProofType is the type of proof that is being provided.
2449
ProofType ProofType
2550
// Proof is the actual proof. Its type depends on the ProofType.
2651
Proof []byte `scale:"max=1048576"` // max size of proof is 1MiB
2752
}
53+
54+
// Proof is an interface for all types of proofs that can be provided in an ATXProof.
55+
// Generally the proof should be able to validate itself and be scale encoded.
56+
type Proof interface {
57+
scale.Encodable
58+
59+
Valid(edVerifier *signing.EdVerifier) (types.NodeID, error)
60+
}

0 commit comments

Comments
 (0)