From 4151bd1c309acbb9f726ba6e2232d7dff0b187c3 Mon Sep 17 00:00:00 2001 From: shum Date: Thu, 22 Oct 2026 05:11:42 +0000 Subject: [PATCH] store: refuse a reveal deadline before the window --- .../src/StoreService/Names/Reveal.hs | 21 +++++++++++-------- .../StoreService/Web/Names/Registration.hs | 2 +- tests/Bots/StoreService/NamesRevealTests.hs | 4 +++- 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/apps/simplex-store-service/src/StoreService/Names/Reveal.hs b/apps/simplex-store-service/src/StoreService/Names/Reveal.hs index 97a66e397f..3036913bed 100644 --- a/apps/simplex-store-service/src/StoreService/Names/Reveal.hs +++ b/apps/simplex-store-service/src/StoreService/Names/Reveal.hs @@ -108,15 +108,18 @@ committedRegistration NamesConfig {nTLD, nChain = ChainConfig {cResolver}} NameR [link] -> Right $ Just link _ -> Left RVInvalidLink --- | The deadline not past and the owner's signature first, so a request nobody signed reads nothing from the chain. -checkIntent :: NamesConfig -> Contracts -> UTCTime -> RegisterIntent -> IO (Either RevealRefusal ()) -checkIntent NamesConfig {nChain = ChainConfig {cChainId, cSponsor}} ct now intent@RegisterIntent {riRegistration = Registration {rOwner}, riNonce} = - verifyRegisterIntent (sponsorDomain cChainId cSponsor) (floor $ utcTimeToPOSIXSeconds now) intent >>= \case - Left _ -> pure $ Left RVBadRequest - Right () -> - sponsorNonce ct rOwner >>= \case - Left e -> Left RVChainUnavailable <$ logWarn ("names: a reveal's nonce read failed: " <> tshow e) - Right nonce -> pure $ if nonce == riNonce then Right () else Left RVBadRequest +-- | The deadline and the owner's signature first, so a request nobody signed reads nothing from the chain. +-- A deadline before revealBefore would expire while the commitment can still register. +checkIntent :: NamesConfig -> Contracts -> UTCTime -> Maybe Word64 -> RegisterIntent -> IO (Either RevealRefusal ()) +checkIntent NamesConfig {nChain = ChainConfig {cChainId, cSponsor}} ct now revealBefore intent@RegisterIntent {riRegistration = Registration {rOwner}, riNonce, riDeadline} + | not (any (riDeadline >=) revealBefore) = pure $ Left RVBadRequest + | otherwise = + verifyRegisterIntent (sponsorDomain cChainId cSponsor) (floor $ utcTimeToPOSIXSeconds now) intent >>= \case + Left _ -> pure $ Left RVBadRequest + Right () -> + sponsorNonce ct rOwner >>= \case + Left e -> Left RVChainUnavailable <$ logWarn ("names: a reveal's nonce read failed: " <> tshow e) + Right nonce -> pure $ if nonce == riNonce then Right () else Left RVBadRequest linksValid :: Registration -> Bool linksValid Registration {rContactLink, rChannelLink} = all (ofKind LKContact) rContactLink && all (ofKind LKChannel) rChannelLink diff --git a/apps/simplex-store-service/src/StoreService/Web/Names/Registration.hs b/apps/simplex-store-service/src/StoreService/Web/Names/Registration.hs index ee7a406684..413e3f6ea1 100644 --- a/apps/simplex-store-service/src/StoreService/Web/Names/Registration.hs +++ b/apps/simplex-store-service/src/StoreService/Web/Names/Registration.hs @@ -88,7 +88,7 @@ revealHandler env@OrdersEnv {oeStore, oeConfig, oeContracts, oeTurns, oePhases, Just fields -> either (\refusal -> pure $ RDRefused refusal Nothing) (signedReveal now r signed fields) $ committedRegistration oeConfig r signed fields signedReveal now r SignedLabel {slSignature} RevealFields {rfNonce, rfDeadline} registration = do let intent = RegisterIntent {riRegistration = registration, riNonce = rfNonce, riDeadline = rfDeadline, riSignature = slSignature} - checkIntent oeConfig oeContracts now intent >>= \case + checkIntent oeConfig oeContracts now (nrRevealBefore r) intent >>= \case Left refusal -> pure $ RDRefused refusal Nothing Right () | linksValid registration -> write now (RDAccepted . Just) r {nrReveal = Just $ revealOf intent} diff --git a/tests/Bots/StoreService/NamesRevealTests.hs b/tests/Bots/StoreService/NamesRevealTests.hs index 3da8816c39..aa827fdbf2 100644 --- a/tests/Bots/StoreService/NamesRevealTests.hs +++ b/tests/Bots/StoreService/NamesRevealTests.hs @@ -69,7 +69,7 @@ namesRevealTests = describe "StoreService names: reveal" $ do it "answers an identical repeat in a later phase, and refuses a different one" testRepeats it "answers after the name's turn, so a slow reader of one answer holds no other reveal" testAnswersAfterTurn describe "refused" $ do - it "refuses a high-s intent, a wrong signer, a stale nonce and a past deadline, writing nothing" testIntentRefusals + it "refuses a high-s intent, a wrong signer, a stale nonce, and a deadline past or before revealBefore, writing nothing" testIntentRefusals it "refuses a label not of the order, another secret or other links, writing nothing" testCommitmentRefusals it "refuses a commitment of another duration than the order's" testWrongDuration it "refuses a second link for one key with invalid_link, recording nothing" testSecondLink @@ -342,10 +342,12 @@ testIntentRefusals = withReveals $ \Fx {fCtx = ctx@Ctx {cNode}} -> do unchanged <- unwritten <$> stored ctx rid sig <- signIntent ctx keyA alice 0 revealBefore other <- signIntent ctx fakeRegistrarKey alice 0 revealBefore + early <- signIntent ctx keyA alice 0 (revealBefore - 1) past <- signIntent ctx keyA alice 0 (orderSeconds - 1) forM_ [ ("high s", bodyOf (highS sig) alice 0 revealBefore), ("another signer", bodyOf other alice 0 revealBefore), + ("a deadline a second before revealBefore", bodyOf early alice 0 (revealBefore - 1)), ("a past deadline", bodyOf past alice 0 (orderSeconds - 1)) ] $ \(what, body) -> (what,) <$> reveal ctx rid body `shouldReturn` (what :: String, badRequest)