diff --git a/.github/actions/nix-cachix-setup/action.yml b/.github/actions/nix-cachix-setup/action.yml index fcf19333bb5..6c24c24a284 100644 --- a/.github/actions/nix-cachix-setup/action.yml +++ b/.github/actions/nix-cachix-setup/action.yml @@ -9,6 +9,21 @@ runs: using: composite steps: + - name: 🧹 Free disk space + if: runner.os == 'Linux' + shell: bash + run: | + echo "Disk space before cleanup:" + df -h / + # Remove unnecessary tools to free up disk space + sudo rm -rf /usr/share/dotnet + sudo rm -rf /usr/local/lib/android + sudo rm -rf /opt/ghc + sudo rm -rf /opt/hostedtoolcache/CodeQL + sudo docker image prune --all --force || true + echo "Disk space after cleanup:" + df -h / + - name: ❄ Prepare nix uses: cachix/install-nix-action@v30 with: diff --git a/hydra-node/src/Hydra/API/HTTPServer.hs b/hydra-node/src/Hydra/API/HTTPServer.hs index b5cd6e1240a..81aabac4d1a 100644 --- a/hydra-node/src/Hydra/API/HTTPServer.hs +++ b/hydra-node/src/Hydra/API/HTTPServer.hs @@ -581,8 +581,9 @@ handleSubmitL2Tx putClientInput apiTransactionTimeout responseChannel body = do go = do event <- atomically $ readTChan dupChannel case event of - Right (RejectedInput{clientInput = NewTx{}, reason}) -> do - pure $ SubmitTxRejectedResponse reason + Right (RejectedInput{clientInput = NewTx{transaction}, reason}) + | txId transaction == txid -> + pure $ SubmitTxRejectedResponse reason Left (TimedServerOutput{output}) -> case output of TxValid{transactionId} | transactionId == txid -> diff --git a/hydra-node/test/Hydra/API/HTTPServerSpec.hs b/hydra-node/test/Hydra/API/HTTPServerSpec.hs index 5dc51159a31..6cb78a9e05c 100644 --- a/hydra-node/test/Hydra/API/HTTPServerSpec.hs +++ b/hydra-node/test/Hydra/API/HTTPServerSpec.hs @@ -805,6 +805,55 @@ apiServerSpec = do $ do post "/transaction" (mkReq testTx) `shouldRespondWith` 503 + prop "ignores RejectedInput for different transaction" $ do + -- This test verifies the fix for the race condition where a RejectedInput + -- for one transaction was incorrectly matched by the handler waiting for + -- a different transaction. + responseChannel <- newTChanIO + let txA = SimpleTx 42 mempty mempty + txB = SimpleTx 99 mempty mempty + -- RejectedInput for txB (different from txA we're submitting) + rejectedB = RejectedInput{clientInput = NewTx txB, reason = "chain out of sync"} + -- SnapshotConfirmed includes txA + snapshot = + Snapshot + { headId = testHeadId + , version = 1 + , number = 7 + , confirmed = [txA] + , utxo = mempty + , utxoToCommit = mempty + , utxoToDecommit = mempty + } + confirmedEvent = + TimedServerOutput + { output = SnapshotConfirmed{snapshot = snapshot, signatures = mempty, headId = testHeadId} + , seq = 0 + , time = now + } + withApplication + ( httpApp @SimpleTx + nullTracer + dummyChainHandle + testEnvironment + dummyStatePath + defaultPParams + (pure inUnsyncedIdleState) + (pure CannotCommit) + (pure []) + -- First write RejectedInput for txB, then SnapshotConfirmed for txA + ( const $ atomically $ do + writeTChan responseChannel (Right rejectedB) + writeTChan responseChannel (Left confirmedEvent) + ) + 10 + responseChannel + ) + $ do + -- Handler for txA should ignore the RejectedInput for txB + -- and correctly return 200 when txA is confirmed + post "/transaction" (mkReq txA) `shouldRespondWith` 200 + describe "POST /decommit" $ do it "returns 202 on timeout" $ do responseChannel <- newTChanIO