From 936f9773a15bca0be6e6211955d44119ccd0c837 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartosz=20R=C3=B3=C5=BCa=C5=84ski?= Date: Thu, 18 Apr 2024 12:12:44 +0000 Subject: [PATCH] Use pre-encoded ATX blobs in checkpoint recovery (#5858) ## Motivation It avoids encoding the ATX again when putting into the new DB. Co-authored-by: Matthias <5011972+fasmat@users.noreply.github.com> --- checkpoint/recovery.go | 71 +++++++---- checkpoint/recovery_test.go | 244 +++++++++++++++++++----------------- node/node.go | 33 +++-- sql/atxs/atxs.go | 15 +++ 4 files changed, 205 insertions(+), 158 deletions(-) diff --git a/checkpoint/recovery.go b/checkpoint/recovery.go index 57ca8b5ecb..f468d3ec3f 100644 --- a/checkpoint/recovery.go +++ b/checkpoint/recovery.go @@ -96,8 +96,14 @@ func copyToLocalFile( return dst, nil } +type AtxDep struct { + ID types.ATXID + PublishEpoch types.EpochID + Blob []byte +} + type PreservedData struct { - Deps []*types.VerifiedActivationTx + Deps []*AtxDep Proofs []*types.PoetProofMessage } @@ -190,7 +196,7 @@ func recoverFromLocalFile( log.Int("num accounts", len(data.accounts)), log.Int("num atxs", len(data.atxs)), ) - deps := make(map[types.ATXID]*types.VerifiedActivationTx) + deps := make(map[types.ATXID]*AtxDep) proofs := make(map[types.PoetProofRef]*types.PoetProofMessage) if cfg.PreserveOwnAtx { logger.With().Info("preserving own atx deps", @@ -216,25 +222,30 @@ func recoverFromLocalFile( maps.Copy(proofs, nodeProofs) } } - if err := db.Close(); err != nil { - return nil, fmt.Errorf("close old db: %w", err) - } + allDeps := maps.Values(deps) // sort ATXs them by publishEpoch and then by ID - slices.SortFunc(allDeps, func(i, j *types.VerifiedActivationTx) int { - return bytes.Compare(i.ID().Bytes(), j.ID().Bytes()) + slices.SortFunc(allDeps, func(i, j *AtxDep) int { + return bytes.Compare(i.ID.Bytes(), j.ID.Bytes()) }) - slices.SortStableFunc(allDeps, func(i, j *types.VerifiedActivationTx) int { + slices.SortStableFunc(allDeps, func(i, j *AtxDep) int { return int(i.PublishEpoch) - int(j.PublishEpoch) }) allProofs := make([]*types.PoetProofMessage, 0, len(proofs)) for _, dep := range allDeps { - proof, ok := proofs[types.PoetProofRef(dep.GetPoetProofRef())] + poetProofRef, err := atxs.PoetProofRef(context.Background(), db, dep.ID) + if err != nil { + return nil, fmt.Errorf("get poet proof ref (%v): %w", dep.ID, err) + } + proof, ok := proofs[poetProofRef] if !ok { - return nil, fmt.Errorf("missing poet proof for atx %v", dep.ID()) + return nil, fmt.Errorf("missing poet proof for atx %v", dep.ID) } allProofs = append(allProofs, proof) } + if err := db.Close(); err != nil { + return nil, fmt.Errorf("close old db: %w", err) + } // all is ready. backup the old data and create new. backupDir, err := backupOldDb(fs, cfg.DataDir, cfg.DbFile) @@ -360,7 +371,7 @@ func collectOwnAtxDeps( nodeID types.NodeID, goldenATX types.ATXID, data *recoveryData, -) (map[types.ATXID]*types.VerifiedActivationTx, map[types.PoetProofRef]*types.PoetProofMessage, error) { +) (map[types.ATXID]*AtxDep, map[types.PoetProofRef]*types.PoetProofMessage, error) { atxid, err := atxs.GetLastIDByNodeID(db, nodeID) if err != nil && !errors.Is(err, sql.ErrNotFound) { return nil, nil, fmt.Errorf("query own last atx id: %w", err) @@ -389,7 +400,7 @@ func collectOwnAtxDeps( all[cAtx.ID] = struct{}{} } var ( - deps map[types.ATXID]*types.VerifiedActivationTx + deps map[types.ATXID]*AtxDep proofs map[types.PoetProofRef]*types.PoetProofMessage ) if ref != types.EmptyATXID { @@ -424,8 +435,8 @@ func collectDeps( db *sql.Database, ref types.ATXID, all map[types.ATXID]struct{}, -) (map[types.ATXID]*types.VerifiedActivationTx, map[types.PoetProofRef]*types.PoetProofMessage, error) { - deps := make(map[types.ATXID]*types.VerifiedActivationTx) +) (map[types.ATXID]*AtxDep, map[types.PoetProofRef]*types.PoetProofMessage, error) { + deps := make(map[types.ATXID]*AtxDep) if err := collect(db, ref, all, deps); err != nil { return nil, nil, err } @@ -440,7 +451,7 @@ func collect( db *sql.Database, ref types.ATXID, all map[types.ATXID]struct{}, - deps map[types.ATXID]*types.VerifiedActivationTx, + deps map[types.ATXID]*AtxDep, ) error { if _, ok := all[ref]; ok { return nil @@ -471,26 +482,40 @@ func collect( if err = collect(db, atx.PositioningATX, all, deps); err != nil { return err } - deps[ref] = atx + var blob sql.Blob + err = atxs.LoadBlob(context.Background(), db, ref.Bytes(), &blob) + if err != nil { + return fmt.Errorf("load atx blob %v: %w", ref, err) + } + + deps[ref] = &AtxDep{ + ID: ref, + PublishEpoch: atx.PublishEpoch, + Blob: blob.Bytes, + } all[ref] = struct{}{} return nil } func poetProofs( db *sql.Database, - vAtxs map[types.ATXID]*types.VerifiedActivationTx, + atxIds map[types.ATXID]*AtxDep, ) (map[types.PoetProofRef]*types.PoetProofMessage, error) { - proofs := make(map[types.PoetProofRef]*types.PoetProofMessage, len(vAtxs)) - for _, vatx := range vAtxs { - proof, err := poets.Get(db, types.PoetProofRef(vatx.GetPoetProofRef())) + proofs := make(map[types.PoetProofRef]*types.PoetProofMessage, len(atxIds)) + for atx := range atxIds { + ref, err := atxs.PoetProofRef(context.Background(), db, atx) + if err != nil { + return nil, fmt.Errorf("get poet proof ref: %w", err) + } + proof, err := poets.Get(db, ref) if err != nil { - return nil, fmt.Errorf("get poet proof (%v): %w", vatx.ID(), err) + return nil, fmt.Errorf("get poet proof (atx: %v): %w", atx, err) } var msg types.PoetProofMessage if err := codec.Decode(proof, &msg); err != nil { - return nil, fmt.Errorf("decode poet proof (%v): %w", vatx.ID(), err) + return nil, fmt.Errorf("decode poet proof (%v): %w", atx, err) } - proofs[types.PoetProofRef(vatx.GetPoetProofRef())] = &msg + proofs[ref] = &msg } return proofs, nil } diff --git a/checkpoint/recovery_test.go b/checkpoint/recovery_test.go index 24fe96e0f5..66440d70bd 100644 --- a/checkpoint/recovery_test.go +++ b/checkpoint/recovery_test.go @@ -227,11 +227,9 @@ func TestRecover_SameRecoveryInfo(t *testing.T) { func validateAndPreserveData( tb testing.TB, db *sql.Database, - deps []*types.VerifiedActivationTx, - proofs []*types.PoetProofMessage, + deps []*checkpoint.AtxDep, ) { lg := logtest.New(tb) - poetDb := activation.NewPoetDb(db, lg) ctrl := gomock.NewController(tb) mclock := activation.NewMocklayerClock(ctrl) mfetch := smocks.NewMockFetcher(ctrl) @@ -255,15 +253,16 @@ func validateAndPreserveData( lg, ) mfetch.EXPECT().GetAtxs(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() - for i, vatx := range deps { - encoded, err := codec.Encode(wire.ActivationTxToWireV1(vatx.ActivationTx)) - require.NoError(tb, err) + for _, dep := range deps { + var atx wire.ActivationTxV1 + require.NoError(tb, codec.Decode(dep.Blob, &atx)) + vatx := wire.ActivationTxFromWireV1(&atx) mclock.EXPECT().CurrentLayer().Return(vatx.PublishEpoch.FirstLayer()) mfetch.EXPECT().RegisterPeerHashes(gomock.Any(), gomock.Any()) - mfetch.EXPECT().GetPoetProof(gomock.Any(), vatx.GetPoetProofRef()) + mfetch.EXPECT().GetPoetProof(gomock.Any(), gomock.Any()) if vatx.InitialPost != nil { mvalidator.EXPECT(). - InitialNIPostChallenge(&vatx.ActivationTx.NIPostChallenge, gomock.Any(), goldenAtx). + InitialNIPostChallenge(&vatx.NIPostChallenge, gomock.Any(), goldenAtx). AnyTimes() mvalidator.EXPECT().Post( gomock.Any(), @@ -277,7 +276,7 @@ func validateAndPreserveData( mvalidator.EXPECT(). VRFNonce(vatx.SmesherID, *vatx.CommitmentATX, vatx.VRFNonce, gomock.Any(), vatx.NumUnits) } else { - mvalidator.EXPECT().NIPostChallenge(&vatx.ActivationTx.NIPostChallenge, cdb, vatx.SmesherID) + mvalidator.EXPECT().NIPostChallenge(&vatx.NIPostChallenge, cdb, vatx.SmesherID) } mvalidator.EXPECT().PositioningAtx(vatx.PositioningATX, cdb, goldenAtx, vatx.PublishEpoch) @@ -287,11 +286,7 @@ func validateAndPreserveData( mvalidator.EXPECT().IsVerifyingFullPost().AnyTimes().Return(true) mreceiver.EXPECT().OnAtx(gomock.Any()) mtrtl.EXPECT().OnAtx(gomock.Any(), gomock.Any(), gomock.Any()) - require.NoError(tb, atxHandler.HandleSyncedAtx(context.Background(), vatx.ID().Hash32(), "self", encoded)) - err = poetDb.ValidateAndStore(context.Background(), proofs[i]) - require.ErrorContains(tb, err, fmt.Sprintf("failed to validate poet proof for poetID %s round 1337", - hex.EncodeToString(proofs[i].PoetServiceID[:5])), - ) + require.NoError(tb, atxHandler.HandleSyncedAtx(context.Background(), vatx.ID().Hash32(), "self", dep.Blob)) } } @@ -299,10 +294,11 @@ func newChainedAtx( tb testing.TB, prev, pos types.ATXID, commitAtx *types.ATXID, + poetProofRef types.PoetProofRef, epoch uint32, seq, vrfNonce uint64, sig *signing.EdSigner, -) *types.VerifiedActivationTx { +) *checkpoint.AtxDep { watx := &wire.ActivationTxV1{ InnerActivationTxV1: wire.InnerActivationTxV1{ NIPostChallengeV1: wire.NIPostChallengeV1{ @@ -314,7 +310,7 @@ func newChainedAtx( }, NIPost: &wire.NIPostV1{ PostMetadata: &wire.PostMetadataV1{ - Challenge: types.RandomBytes(5), + Challenge: poetProofRef[:], }, }, NumUnits: 2, @@ -336,35 +332,64 @@ func newChainedAtx( atx.SetEffectiveNumUnits(atx.NumUnits) atx.SetReceived(time.Now().Local()) - return newvAtx(tb, atx) + return &checkpoint.AtxDep{ + ID: atx.ID(), + PublishEpoch: atx.PublishEpoch, + Blob: codec.MustEncode(watx), + } +} + +func randomPoetProof(tb testing.TB) (*types.PoetProofMessage, types.PoetProofRef) { + proof := &types.PoetProofMessage{ + PoetProof: types.PoetProof{ + MerkleProof: shared.MerkleProof{ + Root: types.RandomBytes(32), + ProvenLeaves: [][]byte{{1}, {2}}, + ProofNodes: [][]byte{{1}, {2}}, + }, + LeafCount: 1234, + }, + PoetServiceID: types.RandomBytes(32), + RoundID: "1337", + } + ref, err := proof.Ref() + require.NoError(tb, err) + return proof, ref } func createInterlinkedAtxChain( tb testing.TB, sig1 *signing.EdSigner, sig2 *signing.EdSigner, -) ([]*types.VerifiedActivationTx, []*types.PoetProofMessage) { +) ([]*checkpoint.AtxDep, []*types.PoetProofMessage) { + var proofs []*types.PoetProofMessage + poetRef := func() types.PoetProofRef { + proof, ref := randomPoetProof(tb) + proofs = append(proofs, proof) + return ref + } + // epoch 2 - sig1Atx1 := newChainedAtx(tb, types.EmptyATXID, goldenAtx, &goldenAtx, 2, 0, 113, sig1) + sig1Atx1 := newChainedAtx(tb, types.EmptyATXID, goldenAtx, &goldenAtx, poetRef(), 2, 0, 113, sig1) // epoch 3 - sig1Atx2 := newChainedAtx(tb, sig1Atx1.ID(), sig1Atx1.ID(), nil, 3, 1, 0, sig1) + sig1Atx2 := newChainedAtx(tb, sig1Atx1.ID, sig1Atx1.ID, nil, poetRef(), 3, 1, 0, sig1) // epoch 4 - sig1Atx3 := newChainedAtx(tb, sig1Atx2.ID(), sig1Atx2.ID(), nil, 4, 2, 0, sig1) - commitAtxID := sig1Atx2.ID() - sig2Atx1 := newChainedAtx(tb, types.EmptyATXID, sig1Atx2.ID(), &commitAtxID, 4, 0, 513, sig2) + sig1Atx3 := newChainedAtx(tb, sig1Atx2.ID, sig1Atx2.ID, nil, poetRef(), 4, 2, 0, sig1) + commitAtxID := sig1Atx2.ID + sig2Atx1 := newChainedAtx(tb, types.EmptyATXID, sig1Atx2.ID, &commitAtxID, poetRef(), 4, 0, 513, sig2) // epoch 5 - sig1Atx4 := newChainedAtx(tb, sig1Atx3.ID(), sig2Atx1.ID(), nil, 5, 3, 0, sig1) + sig1Atx4 := newChainedAtx(tb, sig1Atx3.ID, sig2Atx1.ID, nil, poetRef(), 5, 3, 0, sig1) // epoch 6 - sig1Atx5 := newChainedAtx(tb, sig1Atx4.ID(), sig1Atx4.ID(), nil, 6, 4, 0, sig1) - sig2Atx2 := newChainedAtx(tb, sig2Atx1.ID(), sig1Atx4.ID(), nil, 6, 1, 0, sig2) + sig1Atx5 := newChainedAtx(tb, sig1Atx4.ID, sig1Atx4.ID, nil, poetRef(), 6, 4, 0, sig1) + sig2Atx2 := newChainedAtx(tb, sig2Atx1.ID, sig1Atx4.ID, nil, poetRef(), 6, 1, 0, sig2) // epoch 7 - sig1Atx6 := newChainedAtx(tb, sig1Atx5.ID(), sig2Atx2.ID(), nil, 7, 5, 0, sig1) + sig1Atx6 := newChainedAtx(tb, sig1Atx5.ID, sig2Atx2.ID, nil, poetRef(), 7, 5, 0, sig1) // epoch 8 - sig2Atx3 := newChainedAtx(tb, sig2Atx2.ID(), sig1Atx6.ID(), nil, 8, 2, 0, sig2) + sig2Atx3 := newChainedAtx(tb, sig2Atx2.ID, sig1Atx6.ID, nil, poetRef(), 8, 2, 0, sig2) // epoch 9 - sig1Atx7 := newChainedAtx(tb, sig1Atx6.ID(), sig2Atx3.ID(), nil, 9, 6, 0, sig1) + sig1Atx7 := newChainedAtx(tb, sig1Atx6.ID, sig2Atx3.ID, nil, poetRef(), 9, 6, 0, sig1) - vAtxs := []*types.VerifiedActivationTx{ + vAtxs := []*checkpoint.AtxDep{ sig1Atx1, sig1Atx2, sig1Atx3, @@ -376,64 +401,42 @@ func createInterlinkedAtxChain( sig2Atx3, sig1Atx7, } - var proofs []*types.PoetProofMessage - for range vAtxs { - proofMessage := &types.PoetProofMessage{ - PoetProof: types.PoetProof{ - MerkleProof: shared.MerkleProof{ - Root: types.RandomBytes(32), - ProvenLeaves: [][]byte{{1}, {2}}, - ProofNodes: [][]byte{{1}, {2}}, - }, - LeafCount: 1234, - }, - PoetServiceID: types.RandomBytes(32), - RoundID: "1337", - } - proofs = append(proofs, proofMessage) - } + return vAtxs, proofs } -func createAtxChain(tb testing.TB, sig *signing.EdSigner) ([]*types.VerifiedActivationTx, []*types.PoetProofMessage) { +func createAtxChain(tb testing.TB, sig *signing.EdSigner) ([]*checkpoint.AtxDep, []*types.PoetProofMessage) { other, err := signing.NewEdSigner() require.NoError(tb, err) return createInterlinkedAtxChain(tb, other, sig) } -func createAtxChainDepsOnly(tb testing.TB) ([]*types.VerifiedActivationTx, []*types.PoetProofMessage) { +func createAtxChainDepsOnly(tb testing.TB) ([]*checkpoint.AtxDep, []*types.PoetProofMessage) { other, err := signing.NewEdSigner() require.NoError(tb, err) + + var proofs []*types.PoetProofMessage + poetRef := func() types.PoetProofRef { + proof, ref := randomPoetProof(tb) + proofs = append(proofs, proof) + return ref + } + // epoch 2 - othAtx1 := newChainedAtx(tb, types.EmptyATXID, goldenAtx, &goldenAtx, 2, 0, 113, other) + othAtx1 := newChainedAtx(tb, types.EmptyATXID, goldenAtx, &goldenAtx, poetRef(), 2, 0, 113, other) // epoch 3 - othAtx2 := newChainedAtx(tb, othAtx1.ID(), othAtx1.ID(), nil, 3, 1, 0, other) + othAtx2 := newChainedAtx(tb, othAtx1.ID, othAtx1.ID, nil, poetRef(), 3, 1, 0, other) // epoch 4 - othAtx3 := newChainedAtx(tb, othAtx2.ID(), othAtx2.ID(), nil, 4, 2, 0, other) - vAtxs := []*types.VerifiedActivationTx{othAtx1, othAtx2, othAtx3} - var proofs []*types.PoetProofMessage - for range vAtxs { - proofMessage := &types.PoetProofMessage{ - PoetProof: types.PoetProof{ - MerkleProof: shared.MerkleProof{ - Root: []byte{1, 2, 3}, - ProvenLeaves: [][]byte{{1}, {2}}, - ProofNodes: [][]byte{{1}, {2}}, - }, - LeafCount: 1234, - }, - PoetServiceID: []byte("poet_id_123456"), - RoundID: "1337", - } - proofs = append(proofs, proofMessage) - } - return vAtxs, proofs + othAtx3 := newChainedAtx(tb, othAtx2.ID, othAtx2.ID, nil, poetRef(), 4, 2, 0, other) + atxDeps := []*checkpoint.AtxDep{othAtx1, othAtx2, othAtx3} + + return atxDeps, proofs } -func atxIDs(atxs []*types.VerifiedActivationTx) []types.ATXID { +func atxIDs(atxs []*checkpoint.AtxDep) []types.ATXID { ids := make([]types.ATXID, 0, len(atxs)) for _, atx := range atxs { - ids = append(ids, atx.ID()) + ids = append(ids, atx.ID) } return ids } @@ -489,18 +492,21 @@ func TestRecover_OwnAtxNotInCheckpoint_Preserve(t *testing.T) { vAtxs3, proofs3 := createInterlinkedAtxChain(t, sig3, sig4) vAtxs = append(vAtxs, vAtxs3...) proofs = append(proofs, proofs3...) - validateAndPreserveData(t, oldDB, vAtxs, proofs) + + validateAndPreserveData(t, oldDB, vAtxs) // the proofs are not valid, but save them anyway for the purpose of testing - for i, vatx := range vAtxs { - encoded, err := codec.Encode(proofs[i]) + for _, proof := range proofs { + encoded, err := codec.Encode(proof) + require.NoError(t, err) + ref, err := proof.Ref() require.NoError(t, err) err = poets.Add( oldDB, - types.PoetProofRef(vatx.GetPoetProofRef()), + ref, encoded, - proofs[i].PoetServiceID, - proofs[i].RoundID, + proof.PoetServiceID, + proof.RoundID, ) require.NoError(t, err) } @@ -524,8 +530,7 @@ func TestRecover_OwnAtxNotInCheckpoint_Preserve(t *testing.T) { require.NotNil(t, newDB) t.Cleanup(func() { assert.NoError(t, newDB.Close()) }) verifyDbContent(t, newDB) - validateAndPreserveData(t, newDB, preserve.Deps, preserve.Proofs) - // note that poet proofs are not saved to newDB due to verification errors + validateAndPreserveData(t, newDB, preserve.Deps) restore, err := recovery.CheckpointInfo(newDB) require.NoError(t, err) @@ -574,30 +579,38 @@ func TestRecover_OwnAtxNotInCheckpoint_Preserve_IncludePending(t *testing.T) { vAtxs2, proofs2 := createInterlinkedAtxChain(t, sig2, sig3) vAtxs := append(vAtxs1, vAtxs2...) proofs := append(proofs1, proofs2...) - validateAndPreserveData(t, oldDB, vAtxs, proofs) + validateAndPreserveData(t, oldDB, vAtxs) // the proofs are not valid, but save them anyway for the purpose of testing - for i, vatx := range vAtxs { - encoded, err := codec.Encode(proofs[i]) + for _, proof := range proofs { + encoded, err := codec.Encode(proof) + require.NoError(t, err) + ref, err := proof.Ref() require.NoError(t, err) + require.NoError( t, poets.Add( oldDB, - types.PoetProofRef(vatx.GetPoetProofRef()), + ref, encoded, - proofs[i].PoetServiceID, - proofs[i].RoundID, + proof.PoetServiceID, + proof.RoundID, ), ) } require.NoError(t, oldDB.Close()) // write pending nipost challenge to simulate a pending atx still waiting for poet proof. - prevAtx1 := vAtxs1[len(vAtxs1)-2] - posAtx1 := vAtxs1[len(vAtxs1)-1] + var atx wire.ActivationTxV1 + require.NoError(t, codec.Decode(vAtxs1[len(vAtxs1)-2].Blob, &atx)) + prevAtx1 := wire.ActivationTxFromWireV1(&atx) + require.NoError(t, codec.Decode(vAtxs1[len(vAtxs1)-1].Blob, &atx)) + posAtx1 := wire.ActivationTxFromWireV1(&atx) - prevAtx2 := vAtxs2[len(vAtxs2)-2] - posAtx2 := vAtxs2[len(vAtxs2)-1] + require.NoError(t, codec.Decode(vAtxs2[len(vAtxs1)-2].Blob, &atx)) + prevAtx2 := wire.ActivationTxFromWireV1(&atx) + require.NoError(t, codec.Decode(vAtxs2[len(vAtxs1)-1].Blob, &atx)) + posAtx2 := wire.ActivationTxFromWireV1(&atx) localDB, err := localsql.Open("file:" + filepath.Join(cfg.DataDir, cfg.LocalDbFile)) require.NoError(t, err) @@ -635,8 +648,7 @@ func TestRecover_OwnAtxNotInCheckpoint_Preserve_IncludePending(t *testing.T) { require.NotNil(t, newDB) t.Cleanup(func() { assert.NoError(t, newDB.Close()) }) verifyDbContent(t, newDB) - validateAndPreserveData(t, newDB, preserve.Deps, preserve.Proofs) - // note that poet proofs are not saved to newDB due to verification errors + validateAndPreserveData(t, newDB, preserve.Deps) restore, err := recovery.CheckpointInfo(newDB) require.NoError(t, err) @@ -680,19 +692,19 @@ func TestRecover_OwnAtxNotInCheckpoint_Preserve_Still_Initializing(t *testing.T) require.NotNil(t, oldDB) vAtxs, proofs := createAtxChainDepsOnly(t) - validateAndPreserveData(t, oldDB, vAtxs, proofs) + validateAndPreserveData(t, oldDB, vAtxs) // the proofs are not valid, but save them anyway for the purpose of testing - for i, vatx := range vAtxs { - encoded, err := codec.Encode(proofs[i]) + for _, proof := range proofs { + encoded, err := codec.Encode(proof) require.NoError(t, err) require.NoError( t, poets.Add( oldDB, - types.PoetProofRef(vatx.GetPoetProofRef()), + types.PoetProofRef(types.CalcHash32(encoded)), encoded, - proofs[i].PoetServiceID, - proofs[i].RoundID, + proof.PoetServiceID, + proof.RoundID, ), ) } @@ -776,20 +788,20 @@ func TestRecover_OwnAtxNotInCheckpoint_Preserve_DepIsGolden(t *testing.T) { require.NotNil(t, oldDB) vAtxs, proofs := createAtxChain(t, sig) // make the first one from the previous snapshot - golden := vAtxs[0] + var atx wire.ActivationTxV1 + require.NoError(t, codec.Decode(vAtxs[0].Blob, &atx)) + golden := wire.ActivationTxFromWireV1(&atx) require.NoError(t, atxs.AddCheckpointed(oldDB, &atxs.CheckpointAtx{ - ID: golden.ID(), - Epoch: golden.PublishEpoch, - CommitmentATX: *golden.CommitmentATX, - VRFNonce: *golden.VRFNonce, - NumUnits: golden.NumUnits, - BaseTickHeight: golden.BaseTickHeight(), - TickCount: golden.TickCount(), - SmesherID: golden.SmesherID, - Sequence: golden.Sequence, - Coinbase: golden.Coinbase, + ID: golden.ID(), + Epoch: golden.PublishEpoch, + CommitmentATX: *golden.CommitmentATX, + VRFNonce: *golden.VRFNonce, + NumUnits: golden.NumUnits, + SmesherID: golden.SmesherID, + Sequence: golden.Sequence, + Coinbase: golden.Coinbase, })) - validateAndPreserveData(t, oldDB, vAtxs[1:], proofs[1:]) + validateAndPreserveData(t, oldDB, vAtxs[1:]) // the proofs are not valid, but save them anyway for the purpose of testing for i, proof := range proofs { if i == 0 { @@ -801,7 +813,7 @@ func TestRecover_OwnAtxNotInCheckpoint_Preserve_DepIsGolden(t *testing.T) { t, poets.Add( oldDB, - types.PoetProofRef(vAtxs[i].GetPoetProofRef()), + types.PoetProofRef(types.CalcHash32(encoded)), encoded, proof.PoetServiceID, proof.RoundID, @@ -857,19 +869,19 @@ func TestRecover_OwnAtxNotInCheckpoint_DontPreserve(t *testing.T) { require.NoError(t, err) require.NotNil(t, oldDB) vAtxs, proofs := createAtxChain(t, sig) - validateAndPreserveData(t, oldDB, vAtxs, proofs) + validateAndPreserveData(t, oldDB, vAtxs) // the proofs are not valid, but save them anyway for the purpose of testing - for i, vatx := range vAtxs { - encoded, err := codec.Encode(proofs[i]) + for _, proof := range proofs { + encoded, err := codec.Encode(proof) require.NoError(t, err) require.NoError( t, poets.Add( oldDB, - types.PoetProofRef(vatx.GetPoetProofRef()), + types.PoetProofRef(types.CalcHash32(encoded)), encoded, - proofs[i].PoetServiceID, - proofs[i].RoundID, + proof.PoetServiceID, + proof.RoundID, ), ) } diff --git a/node/node.go b/node/node.go index f97ff6a343..356323c2ac 100644 --- a/node/node.go +++ b/node/node.go @@ -2134,45 +2134,40 @@ func (app *App) preserveAfterRecovery(ctx context.Context) { encoded, err := codec.Encode(poetProof) if err != nil { app.log.With().Error("failed to encode poet proof after checkpoint", - log.Stringer("atx id", app.preserve.Deps[i].ID()), log.Object("poet proof", poetProof), log.Err(err), ) continue } - hash := app.preserve.Deps[i].GetPoetProofRef() + ref, err := poetProof.Ref() + if err != nil { + app.log.With().Error("failed to get poet proof ref after checkpoint", log.Inline(poetProof), log.Err(err)) + continue + } + hash := types.Hash32(ref) if err := app.poetDb.ValidateAndStoreMsg(ctx, hash, p2p.NoPeer, encoded); err != nil { app.log.With().Error("failed to preserve poet proof after checkpoint", - log.Stringer("atx id", app.preserve.Deps[i].ID()), + log.Stringer("atx id", app.preserve.Deps[i].ID), log.String("poet proof ref", hash.ShortString()), log.Err(err), ) continue } app.log.With().Info("preserved poet proof after checkpoint", - log.Stringer("atx id", app.preserve.Deps[i].ID()), + log.Stringer("atx id", app.preserve.Deps[i].ID), log.String("poet proof ref", hash.ShortString()), ) } - for _, vatx := range app.preserve.Deps { - encoded, err := codec.Encode(wire.ActivationTxToWireV1(vatx.ActivationTx)) - if err != nil { - app.log.With().Error("failed to encode atx after checkpoint", - log.Inline(vatx), + for _, atx := range app.preserve.Deps { + if err := app.atxHandler.HandleSyncedAtx(ctx, atx.ID.Hash32(), p2p.NoPeer, atx.Blob); err != nil { + app.log.With().Error( + "failed to preserve atx after checkpoint", + log.ShortStringer("id", atx.ID), log.Err(err), ) continue } - if err := app.atxHandler.HandleSyncedAtx(ctx, vatx.ID().Hash32(), p2p.NoPeer, encoded); err != nil { - app.log.With().Error("failed to preserve atx after checkpoint", - log.Inline(vatx), - log.Err(err), - ) - continue - } - app.log.With().Info("preserved atx after checkpoint", - log.Inline(vatx), - ) + app.log.With().Info("preserved atx after checkpoint", log.ShortStringer("id", atx.ID)) } } diff --git a/sql/atxs/atxs.go b/sql/atxs/atxs.go index b6dc1109a3..d46501932d 100644 --- a/sql/atxs/atxs.go +++ b/sql/atxs/atxs.go @@ -794,3 +794,18 @@ func IterateForGrading( } return nil } + +func PoetProofRef(ctx context.Context, db sql.Executor, id types.ATXID) (types.PoetProofRef, error) { + var blob sql.Blob + if err := LoadBlob(ctx, db, id.Bytes(), &blob); err != nil { + return types.PoetProofRef{}, fmt.Errorf("getting blob for %s: %w", id, err) + } + + // TODO: decide about version based on publish epoch + var atx wire.ActivationTxV1 + if err := codec.Decode(blob.Bytes, &atx); err != nil { + return types.PoetProofRef{}, fmt.Errorf("decoding ATX blob: %w", err) + } + + return types.PoetProofRef(atx.NIPost.PostMetadata.Challenge), nil +}