Skip to content

Commit

Permalink
Merge pull request #54 from mlabs-haskell/uhbif19/update-soa-examples
Browse files Browse the repository at this point in the history
Update SoA docs
  • Loading branch information
uhbif19 authored Apr 22, 2024
2 parents 7d2623a + 8b719ed commit 8e75a29
Showing 1 changed file with 54 additions and 24 deletions.
78 changes: 54 additions & 24 deletions docs/goals_and_soa.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ what we use to demonstrate problems in following:
* CNS
* Hydra
* Plutonomicon patterns
* Plutus tutorial
* [Game Model](https://github.com/IntersectMBO/plutus-apps/blob/dbafa0ffdc1babcf8e9143ca5a7adde78d021a9a/doc/plutus/tutorials/GameModel.hs)
* plutus-usecases

@todo #3: Add more links to specific bugs and code size blowups in existing DApps.
Expand Down Expand Up @@ -98,6 +100,9 @@ eliminate some constraint following from others.
Such kind of manual SMT solving exercises are
known source for security bugs and complicated code.

Checking such constraints leads to code bloat
in form of bunch of utility functions.

Making Plutus contract invariant to `TxIn` ordering
and participation in multi-script scenarios lead to
similar kind of complications.
Expand All @@ -107,6 +112,8 @@ Examples:
* Non-security bug: https://github.com/mlabs-haskell/hydra-auction/issues/129
* Non-security bug: https://github.com/mlabs-haskell/hydra-auction/commit/8152720c43732f8fb74181b7509de503b8371997
* Multiple kind of code complication was observed in CNS audit.
* Utilities [from Indigo](https://github.com/IndigoProtocol/indigo-smart-contracts/blob/main/src/Indigo/Utils/Spooky/Helpers.hs)


### Single script safety and liveliness

Expand Down Expand Up @@ -172,6 +179,9 @@ Our script stages abstraction cover all those kind of problems.

## Logic duplication and spec subtleness

There is a bunch of very common tasks shared by multiple DApps,
which could be tackled generically in our framework.

### Human-readable specification

Designing, understanding and auditing any non-trivial DApp
Expand All @@ -195,31 +205,46 @@ is much less obvious to implement,
and out of scope of current Catalyst project,
but it is very much possible feature as well.

### On-/Off-chain and spec code duplication
Examples of this done by hand:

@todo #3: Add more off-chain code duplication examples from existing PABs.
Include problems with querying coin-selection, tests, retrying and errors.
* [State graph for Agora](https://github.com/Liqwid-Labs/agora/blob/staging/docs/diagrams/ProposalStateMachine.png)

### Computer readable spec and hashes

Script hashes and sizes summary is essential
for DApp users and testers to check on-chain script are matching.
We provide generic implementation showing all DApp hashes via CIP.

### Indexing and querying code duplication

Our framework simplifies generation of common queries
and custom indexing.

Querying of current script state is required for almost any DApp,
and they are usually implemented with bunch of boilerplate.

Examples of boilerplate:

* [Querying available vestings](https://github.com/geniusyield/atlas-examples/blob/main/vesting/offchain/Vesting/Api.hs#L27)

Customized transaction indexing is important for providing
data to almost any kind of custom web-backend.
Customized indexing may reduce storage space or SaaS dependence.

Indexing transactions and parsing it back to state-machine transition
is required for delegated architectures, including many DApps and Hydra L2.

Examples of boilerplate:

* https://github.com/MELD-labs/cardano-defi-public/tree/eacaa527823031105eba1730f730e1b32f1470bc/lending-index/src/Lending/Index

### Correct off-chain Tx construction logic

A lot of on-chain problems, like timing and coin change issues
have their counterpart on Tx submission side.

### Common backend features

There is a list of common tasks shared by multiple backends,
which could be tackled generically in our framework.

* Parsing transaction back to state-machine transition
is required for delegated architectures,
including almost any DApp on Hydra L2.
* Customized transaction indexing is important for providing
data to almost any kind of custom web-backend.
Also usage of customized indexing may reduce storage space or SaaS dependence for DApp dependent on old transactions being recorded.
Our framework simplifies generation of custom indexing solution,
based on transition parsing feature.
* Script hashes and sizes summary is essential
for DApp users and testers to check on-chain script are matching.
@todo #3: Add more off-chain code duplication examples from existing PABs.
Include problems with coin-selection, tests, retrying and errors.

# Existing solutions

Expand Down Expand Up @@ -364,12 +389,17 @@ Any kind of on-chain PL can only cover goals
As far as we aware, none of them are trying
to solve other goals.

Known examples:

* Aiken
* Helios
* https://github.com/OpShin/opshin
* PureScript Backend project
Known examples of PLs:

* [Marlowe](https://github.com/input-output-hk/marlowe-plutus)
- Finance contracts DSL
* [Aiken](https://aiken-lang.org/)
- OnChain PL with IDE support and testing framework
* [Helios](https://www.hyperion-bt.org/helios-book/api/index.html)
- Onchain PL and frontend Tx sending library
* [OpShin](https://github.com/OpShin/opshin)
- Onchain PL
* Purs - PureScript Onchain PL

Same stands for any known kind of frontend framework:

Expand Down

1 comment on commit 8e75a29

@0pdd
Copy link
Collaborator

@0pdd 0pdd commented on 8e75a29 Apr 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wasn't able to retrieve PDD puzzles from the code base and submit them to github. If you think that it's a bug on our side, please submit it to yegor256/0pdd:

set -x && set -e && set -o pipefail && cd /tmp/0pdd20240422-2-e0nqe3/Z2l0QGdpdGh1Yi5jb206bWxhYnMtaGFza2VsbC9jZW0tc2NyaXB0LmdpdA && pdd -v -f /tmp/20240422-4397-ijc1hr [1]: + set -e + set -o pipefail + cd /tmp/0pdd20240422-2-e0nqe3/Z2l0QGdpdGh1Yi5jb206bWxhYnMtaGFza2VsbC9jZW0tc2NyaXB0LmdpdA + pdd...

Please, copy and paste this stack trace to GitHub:

UserError
set -x && set -e && set -o pipefail && cd /tmp/0pdd20240422-2-e0nqe3/Z2l0QGdpdGh1Yi5jb206bWxhYnMtaGFza2VsbC9jZW0tc2NyaXB0LmdpdA && pdd -v -f /tmp/20240422-4397-ijc1hr [1]:
+ set -e
+ set -o pipefail
+ cd /tmp/0pdd20240422-2-e0nqe3/Z2l0QGdpdGh1Yi5jb206bWxhYnMtaGFza2VsbC9jZW0tc2NyaXB0LmdpdA
+ pdd -v -f /tmp/20240422-4397-ijc1hr

My version is 0.24.0
Ruby version is 3.1.4 at x86_64-linux
Reading from root dir /tmp/0pdd20240422-2-e0nqe3/Z2l0QGdpdGh1Yi5jb206bWxhYnMtaGFza2VsbC9jZW0tc2NyaXB0LmdpdA
/tmp/0pdd20240422-2-e0nqe3/Z2l0QGdpdGh1Yi5jb206bWxhYnMtaGFza2VsbC9jZW0tc2NyaXB0LmdpdA/.vscode/settings.json is a binary file (89 bytes)
/tmp/0pdd20240422-2-e0nqe3/Z2l0QGdpdGh1Yi5jb206bWxhYnMtaGFza2VsbC9jZW0tc2NyaXB0LmdpdA/devnet/byron-delegate.key is a binary file (130 bytes)
/tmp/0pdd20240422-2-e0nqe3/Z2l0QGdpdGh1Yi5jb206bWxhYnMtaGFza2VsbC9jZW0tc2NyaXB0LmdpdA/devnet/byron-delegation.cert is a binary file (379 bytes)
/tmp/0pdd20240422-2-e0nqe3/Z2l0QGdpdGh1Yi5jb206bWxhYnMtaGFza2VsbC9jZW0tc2NyaXB0LmdpdA/devnet/cardano-node.json is a binary file (2178 bytes)
/tmp/0pdd20240422-2-e0nqe3/Z2l0QGdpdGh1Yi5jb206bWxhYnMtaGFza2VsbC9jZW0tc2NyaXB0LmdpdA/devnet/credentials/alice.sk is a binary file (180 bytes)
/tmp/0pdd20240422-2-e0nqe3/Z2l0QGdpdGh1Yi5jb206bWxhYnMtaGFza2VsbC9jZW0tc2NyaXB0LmdpdA/devnet/credentials/alice.vk is a binary file (166 bytes)
/tmp/0pdd20240422-2-e0nqe3/Z2l0QGdpdGh1Yi5jb206bWxhYnMtaGFza2VsbC9jZW0tc2NyaXB0LmdpdA/devnet/credentials/bob.sk is a binary file (180 bytes)
/tmp/0pdd20240422-2-e0nqe3/Z2l0QGdpdGh1Yi5jb206bWxhYnMtaGFza2VsbC9jZW0tc2NyaXB0LmdpdA/devnet/credentials/bob.vk is a binary file (166 bytes)
/tmp/0pdd20240422-2-e0nqe3/Z2l0QGdpdGh1Yi5jb206bWxhYnMtaGFza2VsbC9jZW0tc2NyaXB0LmdpdA/devnet/credentials/carol.sk is a binary file (180 bytes)
/tmp/0pdd20240422-2-e0nqe3/Z2l0QGdpdGh1Yi5jb206bWxhYnMtaGFza2VsbC9jZW0tc2NyaXB0LmdpdA/devnet/credentials/carol.vk is a binary file (190 bytes)
/tmp/0pdd20240422-2-e0nqe3/Z2l0QGdpdGh1Yi5jb206bWxhYnMtaGFza2VsbC9jZW0tc2NyaXB0LmdpdA/devnet/credentials/dave.sk is a binary file (180 bytes)
/tmp/0pdd20240422-2-e0nqe3/Z2l0QGdpdGh1Yi5jb206bWxhYnMtaGFza2VsbC9jZW0tc2NyaXB0LmdpdA/devnet/credentials/dave.vk is a binary file (190 bytes)
/tmp/0pdd20240422-2-e0nqe3/Z2l0QGdpdGh1Yi5jb206bWxhYnMtaGFza2VsbC9jZW0tc2NyaXB0LmdpdA/devnet/credentials/eve.sk is a binary file (180 bytes)
/tmp/0pdd20240422-2-e0nqe3/Z2l0QGdpdGh1Yi5jb206bWxhYnMtaGFza2VsbC9jZW0tc2NyaXB0LmdpdA/devnet/credentials/eve.vk is a binary file (190 bytes)
/tmp/0pdd20240422-2-e0nqe3/Z2l0QGdpdGh1Yi5jb206bWxhYnMtaGFza2VsbC9jZW0tc2NyaXB0LmdpdA/devnet/credentials/faucet.sk is a binary file (180 bytes)
/tmp/0pdd20240422-2-e0nqe3/Z2l0QGdpdGh1Yi5jb206bWxhYnMtaGFza2VsbC9jZW0tc2NyaXB0LmdpdA/devnet/credentials/faucet.vk is a binary file (190 bytes)
/tmp/0pdd20240422-2-e0nqe3/Z2l0QGdpdGh1Yi5jb206bWxhYnMtaGFza2VsbC9jZW0tc2NyaXB0LmdpdA/devnet/credentials/frank.sk is a binary file (180 bytes)
/tmp/0pdd20240422-2-e0nqe3/Z2l0QGdpdGh1Yi5jb206bWxhYnMtaGFza2VsbC9jZW0tc2NyaXB0LmdpdA/devnet/credentials/frank.vk is a binary file (190 bytes)
/tmp/0pdd20240422-2-e0nqe3/Z2l0QGdpdGh1Yi5jb206bWxhYnMtaGFza2VsbC9jZW0tc2NyaXB0LmdpdA/devnet/credentials/grace.sk is a binary file (180 bytes)
/tmp/0pdd20240422-2-e0nqe3/Z2l0QGdpdGh1Yi5jb206bWxhYnMtaGFza2VsbC9jZW0tc2NyaXB0LmdpdA/devnet/credentials/grace.vk is a binary file (190 bytes)
/tmp/0pdd20240422-2-e0nqe3/Z2l0QGdpdGh1Yi5jb206bWxhYnMtaGFza2VsbC9jZW0tc2NyaXB0LmdpdA/devnet/credentials/hans.sk is a binary file (180 bytes)
/tmp/0pdd20240422-2-e0nqe3/Z2l0QGdpdGh1Yi5jb206bWxhYnMtaGFza2VsbC9jZW0tc2NyaXB0LmdpdA/devnet/credentials/hans.vk is a binary file (190 bytes)
/tmp/0pdd20240422-2-e0nqe3/Z2l0QGdpdGh1Yi5jb206bWxhYnMtaGFza2VsbC9jZW0tc2NyaXB0LmdpdA/devnet/credentials/oscar.sk is a binary file (180 bytes)
/tmp/0pdd20240422-2-e0nqe3/Z2l0QGdpdGh1Yi5jb206bWxhYnMtaGFza2VsbC9jZW0tc2NyaXB0LmdpdA/devnet/credentials/oscar.vk is a binary file (190 bytes)
/tmp/0pdd20240422-2-e0nqe3/Z2l0QGdpdGh1Yi5jb206bWxhYnMtaGFza2VsbC9jZW0tc2NyaXB0LmdpdA/devnet/credentials/patricia.sk is a binary file (180 bytes)
/tmp/0pdd20240422-2-e0nqe3/Z2l0QGdpdGh1Yi5jb206bWxhYnMtaGFza2VsbC9jZW0tc2NyaXB0LmdpdA/devnet/credentials/patricia.vk is a binary file (190 bytes)
/tmp/0pdd20240422-2-e0nqe3/Z2l0QGdpdGh1Yi5jb206bWxhYnMtaGFza2VsbC9jZW0tc2NyaXB0LmdpdA/devnet/credentials/rupert.sk is a binary file (180 bytes)
/tmp/0pdd20240422-2-e0nqe3/Z2l0QGdpdGh1Yi5jb206bWxhYnMtaGFza2VsbC9jZW0tc2NyaXB0LmdpdA/devnet/credentials/rupert.vk is a binary file (190 bytes)
/tmp/0pdd20240422-2-e0nqe3/Z2l0QGdpdGh1Yi5jb206bWxhYnMtaGFza2VsbC9jZW0tc2NyaXB0LmdpdA/devnet/genesis-alonzo.json is a binary file (6424 bytes)
/tmp/0pdd20240422-2-e0nqe3/Z2l0QGdpdGh1Yi5jb206bWxhYnMtaGFza2VsbC9jZW0tc2NyaXB0LmdpdA/devnet/genesis-conway.json is a binary file (939 bytes)
/tmp/0pdd20240422-2-e0nqe3/Z2l0QGdpdGh1Yi5jb206bWxhYnMtaGFza2VsbC9jZW0tc2NyaXB0LmdpdA/devnet/kes.skey is a binary file (1327 bytes)
/tmp/0pdd20240422-2-e0nqe3/Z2l0QGdpdGh1Yi5jb206bWxhYnMtaGFza2VsbC9jZW0tc2NyaXB0LmdpdA/devnet/opcert.cert is a binary file (363 bytes)
/tmp/0pdd20240422-2-e0nqe3/Z2l0QGdpdGh1Yi5jb206bWxhYnMtaGFza2VsbC9jZW0tc2NyaXB0LmdpdA/devnet/protocol-parameters.json is a binary file (1495 bytes)
/tmp/0pdd20240422-2-e0nqe3/Z2l0QGdpdGh1Yi5jb206bWxhYnMtaGFza2VsbC9jZW0tc2NyaXB0LmdpdA/devnet/topology.json is a binary file (18 bytes)
/tmp/0pdd20240422-2-e0nqe3/Z2l0QGdpdGh1Yi5jb206bWxhYnMtaGFza2VsbC9jZW0tc2NyaXB0LmdpdA/devnet/vrf.skey is a binary file (230 bytes)
/tmp/0pdd20240422-2-e0nqe3/Z2l0QGdpdGh1Yi5jb206bWxhYnMtaGFza2VsbC9jZW0tc2NyaXB0LmdpdA/src/Cardano/CEM/Examples.hs is a binary file (0 bytes)
Reading .0pdd.yml ...
Reading .envrc ...
Reading .ghci ...
Reading .ghcid ...
Reading .github/workflows/blocking-issues.yml ...
Reading .gitignore ...
Reading .hlint.yaml ...
Reading README.md ...
Reading cabal.project ...
Reading cabal.project.freeze ...
Reading cem-script.cabal ...
Reading docker-compose.devnet.yaml ...
Reading docs/arch_principles.md ...
Reading docs/backends_comparsion.md ...
Reading docs/catalyst_milestone_reports.md ...
Reading docs/examples.md ...
Reading docs/goals_and_soa.md ...
Puzzle 3-564fd35d 0/DEV at docs/goals_and_soa.md
Puzzle 3-3d54eede 0/DEV at docs/goals_and_soa.md
Puzzle 3-66696a61 0/DEV at docs/goals_and_soa.md
Puzzle 3-28c563ae 0/DEV at docs/goals_and_soa.md
Puzzle 3-548c6789 0/DEV at docs/goals_and_soa.md
Reading docs/non_cardano_soa.md ...
Reading fourmolu.yaml ...
Reading hie.yaml ...
Reading prepare-devnet.sh ...
Reading src/Cardano/CEM/Constraints.hs ...
ERROR: ERROR: src/Cardano/CEM/Constraints.hs; PDD::Error at src/Cardano/CEM/Constraints.hs:33: TODO found, but puzzle can't be parsed, most probably because TODO is not followed by a puzzle marker, as this page explains: https://github.com/cqfn/pdd#how-to-format
If you can't understand the cause of this issue or you don't know how to fix it, please submit a GitHub issue, we will try to help you: https://github.com/cqfn/pdd/issues. This tool is still in its beta version and we will appreciate your feedback. Here is where you can find more documentation: https://github.com/cqfn/pdd/blob/master/README.md.
Exit code is 1

/app/objects/git_repo.rb:74:in `rescue in block in xml'
/app/objects/git_repo.rb:71:in `block in xml'
/app/vendor/ruby-3.1.4/lib/ruby/3.1.0/tempfile.rb:317:in `open'
/app/objects/git_repo.rb:70:in `xml'
/app/objects/puzzles.rb:46:in `deploy'
/app/objects/jobs/job.rb:38:in `proceed'
/app/objects/jobs/job_starred.rb:32:in `proceed'
/app/objects/jobs/job_recorded.rb:31:in `proceed'
/app/objects/jobs/job_emailed.rb:33:in `proceed'
/app/objects/jobs/job_commiterrors.rb:33:in `proceed'
/app/objects/jobs/job_detached.rb:48:in `exclusive'
/app/objects/jobs/job_detached.rb:36:in `block in proceed'
/app/objects/jobs/job_detached.rb:36:in `fork'
/app/objects/jobs/job_detached.rb:36:in `proceed'
/app/0pdd.rb:549:in `process_request'
/app/0pdd.rb:380:in `block in <top (required)>'
/app/vendor/bundle/ruby/3.1.0/gems/sinatra-4.0.0/lib/sinatra/base.rb:1804:in `call'
/app/vendor/bundle/ruby/3.1.0/gems/sinatra-4.0.0/lib/sinatra/base.rb:1804:in `block in compile!'
/app/vendor/bundle/ruby/3.1.0/gems/sinatra-4.0.0/lib/sinatra/base.rb:1071:in `block (3 levels) in route!'
/app/vendor/bundle/ruby/3.1.0/gems/sinatra-4.0.0/lib/sinatra/base.rb:1089:in `route_eval'
/app/vendor/bundle/ruby/3.1.0/gems/sinatra-4.0.0/lib/sinatra/base.rb:1071:in `block (2 levels) in route!'
/app/vendor/bundle/ruby/3.1.0/gems/sinatra-4.0.0/lib/sinatra/base.rb:1120:in `block in process_route'
/app/vendor/bundle/ruby/3.1.0/gems/sinatra-4.0.0/lib/sinatra/base.rb:1118:in `catch'
/app/vendor/bundle/ruby/3.1.0/gems/sinatra-4.0.0/lib/sinatra/base.rb:1118:in `process_route'
/app/vendor/bundle/ruby/3.1.0/gems/sinatra-4.0.0/lib/sinatra/base.rb:1069:in `block in route!'
/app/vendor/bundle/ruby/3.1.0/gems/sinatra-4.0.0/lib/sinatra/base.rb:1066:in `each'
/app/vendor/bundle/ruby/3.1.0/gems/sinatra-4.0.0/lib/sinatra/base.rb:1066:in `route!'
/app/vendor/bundle/ruby/3.1.0/gems/sinatra-4.0.0/lib/sinatra/base.rb:1190:in `block in dispatch!'
/app/vendor/bundle/ruby/3.1.0/gems/sinatra-4.0.0/lib/sinatra/base.rb:1161:in `catch'
/app/vendor/bundle/ruby/3.1.0/gems/sinatra-4.0.0/lib/sinatra/base.rb:1161:in `invoke'
/app/vendor/bundle/ruby/3.1.0/gems/sinatra-4.0.0/lib/sinatra/base.rb:1185:in `dispatch!'
/app/vendor/bundle/ruby/3.1.0/gems/sinatra-4.0.0/lib/sinatra/base.rb:1001:in `block in call!'
/app/vendor/bundle/ruby/3.1.0/gems/sinatra-4.0.0/lib/sinatra/base.rb:1161:in `catch'
/app/vendor/bundle/ruby/3.1.0/gems/sinatra-4.0.0/lib/sinatra/base.rb:1161:in `invoke'
/app/vendor/bundle/ruby/3.1.0/gems/sinatra-4.0.0/lib/sinatra/base.rb:1001:in `call!'
/app/vendor/bundle/ruby/3.1.0/gems/sinatra-4.0.0/lib/sinatra/base.rb:990:in `call'
/app/vendor/bundle/ruby/3.1.0/gems/rack-3.0.9/lib/rack/rewindable_input.rb:25:in `call'
/app/vendor/bundle/ruby/3.1.0/gems/rack-3.0.9/lib/rack/deflater.rb:47:in `call'
/app/vendor/bundle/ruby/3.1.0/gems/rack-protection-4.0.0/lib/rack/protection/xss_header.rb:20:in `call'
/app/vendor/bundle/ruby/3.1.0/gems/rack-protection-4.0.0/lib/rack/protection/path_traversal.rb:18:in `call'
/app/vendor/bundle/ruby/3.1.0/gems/rack-protection-4.0.0/lib/rack/protection/json_csrf.rb:28:in `call'
/app/vendor/bundle/ruby/3.1.0/gems/rack-protection-4.0.0/lib/rack/protection/base.rb:53:in `call'
/app/vendor/bundle/ruby/3.1.0/gems/rack-protection-4.0.0/lib/rack/protection/base.rb:53:in `call'
/app/vendor/bundle/ruby/3.1.0/gems/rack-protection-4.0.0/lib/rack/protection/frame_options.rb:33:in `call'
/app/vendor/bundle/ruby/3.1.0/gems/rack-3.0.9/lib/rack/logger.rb:19:in `call'
/app/vendor/bundle/ruby/3.1.0/gems/rack-3.0.9/lib/rack/common_logger.rb:43:in `call'
/app/vendor/bundle/ruby/3.1.0/gems/sinatra-4.0.0/lib/sinatra/base.rb:266:in `call'
/app/vendor/bundle/ruby/3.1.0/gems/sinatra-4.0.0/lib/sinatra/base.rb:259:in `call'
/app/vendor/bundle/ruby/3.1.0/gems/rack-3.0.9/lib/rack/head.rb:15:in `call'
/app/vendor/bundle/ruby/3.1.0/gems/rack-3.0.9/lib/rack/method_override.rb:28:in `call'
/app/vendor/bundle/ruby/3.1.0/gems/sinatra-4.0.0/lib/sinatra/base.rb:224:in `call'
/app/vendor/bundle/ruby/3.1.0/gems/sinatra-4.0.0/lib/sinatra/base.rb:2115:in `call'
/app/vendor/bundle/ruby/3.1.0/gems/sinatra-4.0.0/lib/sinatra/base.rb:1674:in `block in call'
/app/vendor/bundle/ruby/3.1.0/gems/sinatra-4.0.0/lib/sinatra/base.rb:1890:in `synchronize'
/app/vendor/bundle/ruby/3.1.0/gems/sinatra-4.0.0/lib/sinatra/base.rb:1674:in `call'
/app/vendor/bundle/ruby/3.1.0/gems/rackup-2.1.0/lib/rackup/handler/webrick.rb:111:in `service'
/app/vendor/bundle/ruby/3.1.0/gems/webrick-1.8.1/lib/webrick/httpserver.rb:140:in `service'
/app/vendor/bundle/ruby/3.1.0/gems/webrick-1.8.1/lib/webrick/httpserver.rb:96:in `run'
/app/vendor/bundle/ruby/3.1.0/gems/webrick-1.8.1/lib/webrick/server.rb:310:in `block in start_thread'

Please sign in to comment.