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] - Add check for next poet round on mainnet to codebase. #4752

Closed
wants to merge 3 commits into from
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
97 changes: 88 additions & 9 deletions activation/nipost.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"github.com/spacemeshos/poet/shared"
"github.com/spacemeshos/post/proving"
"github.com/spacemeshos/post/verifying"
"golang.org/x/exp/slices"
"golang.org/x/sync/errgroup"

"github.com/spacemeshos/go-spacemesh/activation/metrics"
Expand All @@ -28,7 +29,10 @@
// proving clients, and thus enormously reduce the cost-per-proof for PoET since each additional proof adds only
// a small number of hash evaluations to the total cost.
type PoetProvingServiceClient interface {
Address() string

PowParams(ctx context.Context) (*PoetPowParams, error)

// Submit registers a challenge in the proving service current open round.
Submit(ctx context.Context, prefix, challenge []byte, signature types.EdSignature, nodeID types.NodeID, pow PoetPoW) (*types.PoetRound, error)

Expand Down Expand Up @@ -64,7 +68,7 @@
nodeID types.NodeID
dataDir string
postSetupProvider postSetupProvider
poetProvers []PoetProvingServiceClient
poetProvers map[string]PoetProvingServiceClient
poetDB poetDbAPI
state *types.NIPostBuilderState
log log.Log
Expand All @@ -82,6 +86,16 @@
}
}

// withPoetClients allows to pass in clients directly (for testing purposes).
func withPoetClients(clients []PoetProvingServiceClient) NIPostBuilderOption {
return func(nb *NIPostBuilder) {
nb.poetProvers = make(map[string]PoetProvingServiceClient, len(clients))
for _, client := range clients {
nb.poetProvers[client.Address()] = client
}
}
}

type poetDbAPI interface {
GetProof(types.PoetProofRef) (*types.PoetProof, *types.Hash32, error)
ValidateAndStore(ctx context.Context, proofMessage *types.PoetProofMessage) error
Expand All @@ -91,19 +105,28 @@
func NewNIPostBuilder(
nodeID types.NodeID,
postSetupProvider postSetupProvider,
poetProvers []PoetProvingServiceClient,
poetDB poetDbAPI,
poetServers []string,
fasmat marked this conversation as resolved.
Show resolved Hide resolved
dataDir string,
lg log.Log,
signer *signing.EdSigner,
poetCfg PoetConfig,
layerClock layerClock,
opts ...NIPostBuilderOption,
) *NIPostBuilder {
) (*NIPostBuilder, error) {
poetClients := make(map[string]PoetProvingServiceClient, len(poetServers))
for _, address := range poetServers {
client, err := NewHTTPPoetClient(address, poetCfg)
if err != nil {
return nil, fmt.Errorf("cannot create poet client: %w", err)
fasmat marked this conversation as resolved.
Show resolved Hide resolved
}

Check warning on line 122 in activation/nipost.go

View check run for this annotation

Codecov / codecov/patch

activation/nipost.go#L121-L122

Added lines #L121 - L122 were not covered by tests
poetClients[client.Address()] = client
}

b := &NIPostBuilder{
nodeID: nodeID,
postSetupProvider: postSetupProvider,
poetProvers: poetProvers,
poetProvers: poetClients,
poetDB: poetDB,
state: &types.NIPostBuilderState{NIPost: &types.NIPost{}},
dataDir: dataDir,
Expand All @@ -116,7 +139,7 @@
for _, opt := range opts {
opt(b)
}
return b
return b, nil
}

func (nb *NIPostBuilder) DataDir() string {
Expand All @@ -125,11 +148,17 @@

// UpdatePoETProvers updates poetProver reference. It should not be executed concurrently with BuildNIPoST.
func (nb *NIPostBuilder) UpdatePoETProvers(poetProvers []PoetProvingServiceClient) {
// TODO(mafa): this seems incorrect - this makes it impossible for the node to fetch a submitted challenge
// thereby skipping an epoch they could have published an ATX for

Check warning on line 153 in activation/nipost.go

View check run for this annotation

Codecov / codecov/patch

activation/nipost.go#L151-L153

Added lines #L151 - L153 were not covered by tests
// reset the state for safety to avoid accidental erroneous wait in Phase 1.
nb.state = &types.NIPostBuilderState{
NIPost: &types.NIPost{},
}
nb.poetProvers = poetProvers
nb.poetProvers = make(map[string]PoetProvingServiceClient, len(poetProvers))
for _, poetProver := range poetProvers {
nb.poetProvers[poetProver.Address()] = poetProver
}

Check warning on line 161 in activation/nipost.go

View check run for this annotation

Codecov / codecov/patch

activation/nipost.go#L158-L161

Added lines #L158 - L161 were not covered by tests
nb.log.With().Info("updated poet proof service clients", log.Int("count", len(nb.poetProvers)))
}

Expand Down Expand Up @@ -204,13 +233,13 @@
// Phase 1: query PoET services for proofs
if nb.state.PoetProofRef == types.EmptyPoetProofRef {
if poetProofDeadline.Before(now) {
return nil, 0, fmt.Errorf("%w: poet proof for pub epoch %d must be available by now (%s)", ErrATXChallengeExpired, challenge.PublishEpoch, now)
return nil, 0, fmt.Errorf("%w: deadline to query poet proof for pub epoch %d exceeded (deadline: %s, now: %s)", ErrATXChallengeExpired, challenge.PublishEpoch, poetProofDeadline, now)
}
getProofsCtx, cancel := context.WithDeadline(ctx, poetProofDeadline)
defer cancel()

events.EmitPoetWaitProof(challenge.PublishEpoch, challenge.TargetEpoch(), time.Until(poetRoundEnd))
poetProofRef, membership, err := nb.getBestProof(getProofsCtx, nb.state.Challenge)
poetProofRef, membership, err := nb.getBestProof(getProofsCtx, nb.state.Challenge, challenge.PublishEpoch)
if err != nil {
return nil, 0, &PoetSvcUnstableError{msg: "getBestProof failed", source: err}
}
Expand Down Expand Up @@ -349,7 +378,57 @@
return 0, fmt.Errorf("challenge is not a member of the proof")
}

func (nb *NIPostBuilder) getBestProof(ctx context.Context, challenge types.Hash32) (types.PoetProofRef, *types.MerkleProof, error) {
// TODO(mafa): remove after next poet round; https://github.com/spacemeshos/go-spacemesh/issues/4753
fasmat marked this conversation as resolved.
Show resolved Hide resolved
func (nb *NIPostBuilder) addPoet111ForPubEpoch1(ctx context.Context) error {
// because poet 111 had a hardware issue when challenges for round 0 were submitted, no node could submit to it
// 111 was recovered with the poet 110 DB, so all successful submissions to 110 should be able to be fetched from there as well

client111, ok := nb.poetProvers["https://poet-111.spacemesh.network"]
if !ok {
// poet 111 is not in the list, no action necessary
return nil
}

nb.log.Info("pub epoch 1 mitigation: poet 111 is in the list of poets, adding it to the state as well")
client110 := nb.poetProvers["https://poet-110.spacemesh.network"]
fasmat marked this conversation as resolved.
Show resolved Hide resolved

ID110, err := client110.PoetServiceID(ctx)
if err != nil {
return fmt.Errorf("failed to get poet 110 id: %w", err)
}

Check warning on line 398 in activation/nipost.go

View check run for this annotation

Codecov / codecov/patch

activation/nipost.go#L397-L398

Added lines #L397 - L398 were not covered by tests
ID111, err := client111.PoetServiceID(ctx)
if err != nil {
return fmt.Errorf("failed to get poet 111 id: %w", err)
}

Check warning on line 402 in activation/nipost.go

View check run for this annotation

Codecov / codecov/patch

activation/nipost.go#L401-L402

Added lines #L401 - L402 were not covered by tests

if slices.IndexFunc(nb.state.PoetRequests, func(r types.PoetRequest) bool { return bytes.Equal(r.PoetServiceID.ServiceID, ID111.ServiceID) }) != -1 {
nb.log.Info("poet 111 is already in the state, no action necessary")
return nil
}

Check warning on line 407 in activation/nipost.go

View check run for this annotation

Codecov / codecov/patch

activation/nipost.go#L405-L407

Added lines #L405 - L407 were not covered by tests

poet110 := slices.IndexFunc(nb.state.PoetRequests, func(r types.PoetRequest) bool {
return bytes.Equal(r.PoetServiceID.ServiceID, ID110.ServiceID)
})
if poet110 == -1 {
return fmt.Errorf("poet 110 is not in the state, cannot add poet 111")
}

Check warning on line 414 in activation/nipost.go

View check run for this annotation

Codecov / codecov/patch

activation/nipost.go#L413-L414

Added lines #L413 - L414 were not covered by tests
poet111 := nb.state.PoetRequests[poet110]
poet111.PoetServiceID.ServiceID = ID111.ServiceID
nb.state.PoetRequests = append(nb.state.PoetRequests, poet111)
nb.persistState()
nb.log.Info("pub epoch 1 mitigation: poet 111 added to the state")
return nil
}
fasmat marked this conversation as resolved.
Show resolved Hide resolved

func (nb *NIPostBuilder) getBestProof(ctx context.Context, challenge types.Hash32, publishEpoch types.EpochID) (types.PoetProofRef, *types.MerkleProof, error) {
// TODO(mafa): remove after next poet round; https://github.com/spacemeshos/go-spacemesh/issues/4753
if publishEpoch == 1 {
err := nb.addPoet111ForPubEpoch1(ctx)
if err != nil {
nb.log.With().Error("pub epoch 1 mitigation: failed to add poet 111 to state for pub epoch 1", log.Err(err))
}

Check warning on line 429 in activation/nipost.go

View check run for this annotation

Codecov / codecov/patch

activation/nipost.go#L428-L429

Added lines #L428 - L429 were not covered by tests
}

type poetProof struct {
poet *types.PoetProofMessage
membership *types.MerkleProof
Expand Down
14 changes: 14 additions & 0 deletions activation/nipost_mocks.go

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