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

docs: update in sync with latest atlas changes (v0.5.0) #80

Merged
merged 5 commits into from
May 10, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/pages/getting-started/_meta.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"how-to-build": "How to build Atlas?",
"how-to-build": "How to build?",
"smart-contract-intro": "Smart Contract",
"operations": "Operations over Contract",
"unit-tests": "Unit Tests",
Expand Down
72 changes: 48 additions & 24 deletions src/pages/getting-started/endpoints.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ Now that we are confident with our smart contract, it's time that we make it acc

The approach here would be

1. Front-end asks to construct transaction body for the concerned operation.
2. It then receives this transaction body, which is complete besides missing for signature for spending inputs belonging to browser wallet. It calls wallet api's `signTx` method upon this body to get this signature (key witness).
3. Frontend now passes this unsigned transaction body along with the witness it received to our backend endpoint which will add this witness to the transaction body, making it complete and would then submit it.
1. Front-end asks to construct transaction for the concerned operation.
2. It then receives this transaction, which is complete besides missing for signature for spending inputs belonging to browser wallet. It calls wallet api's `signTx` method upon this body to get this signature (key witness).
3. Frontend now passes this unsigned transaction along with the witness it received to our backend endpoint which will add this witness to the transaction, making it complete and would then submit it.

We'll use [Servant](https://docs.servant.dev/en/stable/) to create our endpoints and one may understand it by following their easy to understand tutorial [here](https://docs.servant.dev/en/stable/tutorial/index.html).

<Callout>
Do note that we can also sign the transactions in server using the `signTx` function defined in [`TxBody.hs`](https://github.com/geniusyield/atlas/tree/main/src/GeniusYield/Types/TxBody.hs)
Do note that we can also sign the transactions in server using the `signTx` function defined in [`GeniusYield.Types.TxBody`](https://haddock.atlas-app.io/GeniusYield-Types-TxBody.html) module.
</Callout>

## Providing Data Provider
Expand All @@ -26,44 +26,68 @@ As noted earlier, building transaction bodies require gathering suitable informa
Currently Atlas supports the following providers (& it would be highly appreciated if community enriches this by contributing to [Atlas](https://github.com/geniusyield/atlas/tree/main)):

* [Maestro](https://www.gomaestro.org/).
* Locally ran node.
* Cardano DB Sync (but only for private network tests).
* Locally ran node along with [Kupo](https://cardanosolutions.github.io/kupo/). We have tested with version 8.1.2 of `cardano-node` and 2.7.2 of Kupo.
* [Blockfrost](https://blockfrost.io/).

<Callout type="warning">
Following API functions don't have an optimal implementation for Blockfrost:

* `utxosAtTxOutRefs`
* `utxosAtTxOutRefsWithDatums`
* `utxosAtAddressWithDatums`
* `utxosAtAddresses`
* `utxosAtAddressesWithDatums`
* `utxosAtPaymentCredentialWithDatums`
* `utxosAtPaymentCredentials`
* `utxosAtPaymentCredentialsWithDatums`

In general, we recommend either Maestro or local node with Kupo as provider.
</Callout>



To provide information about the provider, we will create a `config.json` file whose contents could be as follows:

<Callout>
We have given a sample `config.json` file [here](https://github.com/geniusyield/atlas-examples/tree/main/bet-ref/config.sample.json).
</Callout>

<Tabs items={['Maestro', 'Local Node']}>
```json
{
"coreProvider": ...,
"networkId": "preprod",
"logging": [{ "type": { "tag": "stderr" }, "severity": "Debug", "verbosity": "V2" }]
}
```

where `coreProvider` field can have one of following possible values:

<Tabs items={['Maestro', 'Local Node with Kupo', 'Blockfrost']}>
<Tab>
```json
"coreProvider": { "maestroToken": "<Your-API-Key>", "turboSubmit": false },
```
</Tab>
<Tab>
```json
{
"coreProvider": { "maestroToken": "<Your-API-Key>" },
"networkId": "testnet-preprod",
"logging": [{ "type": { "tag": "stderr" }, "severity": "Debug", "verbosity": "V2" }],
"utxoCacheEnable": false
}
"coreProvider": { "socketPath": "path-to-node-socket", "kupoUrl": "http://localhost:1442" },
```
</Tab>
<Tab>
```json
{
"coreProvider": { "socketPath": "<Path-To-node.socket-File>", "maestroToken": "<Your-API-Key>" },
"networkId": "testnet-preprod",
"logging": [{ "type": { "tag": "stderr" }, "severity": "Debug", "verbosity": "V2" }],
"utxoCacheEnable": false
}
"coreProvider": { "blockfrostKey": "<Your-API-Key>" },
```
</Tab>
</Tabs>

Here is the explaination for each of the JSON keys above:
Here is the explanation for each of the JSON keys above:

* **`coreProvider`**: This field is the differentiating factor between different providers. Above we have given how it would look like for locally ran node & Maestro. Note that local node option still requires Maestro key for `lookupDatum` query.
* **`networkId`**: Specifies your network and must be one of `mainnet`, `testnet-preprod`, `testnet-preview`, `testnet` (for legacy testnet) & `privnet` (for local private network).
* **`logging`**: It's a list of [scribes](https://hackage.haskell.org/package/katip-0.8.7.3/docs/Katip.html#t:Scribe) to register. Its parameters (like `severity`, `verbosity`) and its general usage can be understood by going over their official haddock documentation [here](https://hackage.haskell.org/package/katip-0.8.7.2/docs/Katip.html). Katip is also explained in [this](https://link.springer.com/book/10.1007/978-1-4842-3739-7) book on web development in Haskell.
* **`utxoCacheEnable`**: Enabling this boolean will enable cache (using [`Data.Cache`](https://hackage.haskell.org/package/cache-0.1.3.0/docs/Data-Cache.html)) whereby queries related to fetching UTxOs won't generate call to provider if the entry exists in cache (& has not yet expired).
* **`coreProvider`**: This field is the differentiating factor between different providers.
* For Maestro, `maestroToken` holds the api key and `turboSubmit` field dictates whether the transactions are to be submitted via their [turbo submit](https://docs.gomaestro.org/Cardano/Transaction-Manager/tx-manager-turbo-submit) endpoint.
* For Local Node with Kupo provider, `socketPath` is the path towards node socket (usually named `node.socket`) file and `kupoUrl` is the url where endpoints are made available by Kupo, it is usually `http://localhost:1442`.
* For Blockfrost, `blockfrostKey` holds the required api key.
* **`networkId`**: Specifies your network and must be one of `mainnet`, `preprod`, `preview`, `testnet` (for legacy testnet) & `privnet` (for local private network).
* **`logging`**: It's a list of [scribes](https://hackage.haskell.org/package/katip-0.8.7.3/docs/Katip.html#t:Scribe) to register. Its parameters (like `severity`, `verbosity`) and its general usage can be understood by going over their official haddock documentation [here](https://hackage.haskell.org/package/katip-0.8.7.2/docs/Katip.html). Katip is also explained in [this](https://link.springer.com/book/10.1007/978-1-4842-3739-7) book on web development in Haskell. Please have a look at haddock for `FromJSON` and `ToJSON` instances of [`GYLogScriptType`](https://haddock.atlas-app.io/GeniusYield-Types-Logging.html#t:GYLogScribeType) to see sample usage.

### Parsing Given Configuration

Expand Down
4 changes: 2 additions & 2 deletions src/pages/getting-started/how-to-build.mdx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# How to build Atlas?
# How to build?

We currently support GHC version 9.2.8 and compilation is tested with cabal version 3.10.2.0.

Expand All @@ -13,4 +13,4 @@ To verify if environment is configured properly, one can clone Atlas repository

## Building with Nix

Alternatively, we provide a nix shell with all dependencies baked in. Please refer to [this](https://github.com/input-output-hk/iogx/blob/main/doc/nix-setup-guide.md) guide on how to configure nix and later one can enter development shell via `nix develop`.
Alternatively, we provide a nix shell with all dependencies baked in. Please refer to [this](https://github.com/input-output-hk/iogx/blob/c6ce7f034717ed0c0e9c6dd8fa2f898a15439627/doc/nix-setup-guide.md) guide on how to configure nix and later one can enter development shell via `nix develop`.
45 changes: 31 additions & 14 deletions src/pages/getting-started/integration-tests.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,16 @@ We already saw how we can conveniently write tests for our smart contract using
|--------------------------------------------------------------------|-------------------------------------------------------------------------------|
| Runs against mock ledger | Runs against real node |
| Each unit test gets fresh set of wallets (having original balance) | Each subsequent unit test continues upon the effects caused by previous ones |
| Fast, purer (no `IO`) & convenient | Slow as each slot is 0.1 second |
| Fast, purer (no `IO`) & convenient | Slow as each slot is configured to be 0.1 second |

Thus these tests are suitable for integration testing.

## Spinning up private network

<Callout>
To access our private network in Atlas, we'll be using "Local node with Kupo" provider and so, `cardano-node` & Kupo version is expected to be 8.1.2 & 2.7.2 respectively.
</Callout>

Our private network is adapted from WoofPool's [`cardano-private-testnet-setup`](https://github.com/woofpool/cardano-private-testnet-setup) repository.

To spin up it up:
Expand All @@ -23,9 +27,21 @@ To spin up it up:
2. Enter it & checkout `geniusyield` branch.
3. Enter the following in terminal: `./scripts/automate.sh` (you would need to have `cardano-node` & `cardano-cli` available in your `PATH`).

Once it says, "_Congrats! Your network is ready for use!_" you can attempt to run the tests (in another terminal).
Once it says, "_Congrats! Your network is ready for use!_" we are ready to move forward and setup Kupo.

Assuming `TESTNET` environment variable points to the directory of clone private testnet repository, we can start Kupo with following command:

```
kupo \
--node-socket $TESTNET/private-testnet/node-spo1/node.sock \
--node-config $TESTNET/private-testnet/configuration.yaml \
--since origin \
--match "*" \
--prune-utxo \
--in-memory
```

First, let's say the path to `private-testnet-simple` is `X`, then being inside your example project folder, you can execute the tests by running `GENIUSYIELD_PRIVNET_DIR=$X/private-testnet cabal run betref-privnet-tests -- -j1`
We are now complete with our setup. To run tests, execute `KUPO_URL=http://localhost:1442 GENIUSYIELD_PRIVNET_DIR=$TESTNET/private-testnet cabal run betref-privnet-tests -- -j1` inside our example project folder.

The `-j1` is needed so that the tests run sequentially.

Expand All @@ -43,7 +59,7 @@ First user is called "_funder_" as it has far more ada (couple of 100 thousands)
We'll also see how to create a new user soon, if required.

<Callout type="warning">
Unless you kill & restart the private network, running your privnet tests again, would have them run in the modified network state. So in general, if you wish to reexecute the command mentioned before, viz. `ATLAS_PRIVNET_DIR=$(pwd)/private-testnet-simple/private-testnet cabal run privnet-tests -- -j1`, you should first restart the privnet[^1].
Unless you kill & restart the private network, running your privnet tests again, would have them run in the modified network state[^1].
</Callout>

## Understanding our first test
Expand All @@ -52,15 +68,15 @@ We'll also see how to create a new user soon, if required.
The tests are written in [this](https://github.com/geniusyield/atlas-examples/tree/main/bet-ref/tests-privnet/BetRef/Tests/Privnet/Tests.hs) file and are being called [here](https://github.com/geniusyield/atlas-examples/tree/main/bet-ref/tests-privnet/betref-privnet-tests.hs).
</Callout>

Here is the code (& explaination follows after it):
Here is the code (& explanation follows after it):

```haskell
testCaseSteps "Balance checks & taking pot by closest guesser should pass" $ \info -> withSetup setup info $ \ctx -> do

-- First step: Construct the parameters and obtain validator from it.
--
-- Let's define a new User to represent Oracle (not necessary though)
oracleUser <- newTempUserCtx ctx (ctxUserF ctx) (valueFromLovelace 20_000_000) False
oracleUser <- newTempUserCtx ctx (ctxUserF ctx) (valueFromLovelace 20_000_000) def
(currentSlot, slotConfig) <- getSlotAndConfig ctx
let betUntilSlotDelta = 100
betRevealSlotDelta = 200
Expand Down Expand Up @@ -110,24 +126,25 @@ Here is the code (& explaination follows after it):

The first line `testCaseSteps "test description" $ \info -> withSetup setup info $ \ctx -> do` can be seen as a boilerplate for all of your tests.

`ctx` denotes the so called context (of type `Ctx`) and contains information about our users, additional tokens, etc. It is defined in [`Ctx.hs`](https://github.com/geniusyield/atlas/tree/main/src/GeniusYield/Test/Privnet/Ctx.hs) file and it is essential to go over that file if you intend to write these tests.
`ctx` denotes the so called context (of type `Ctx`) and contains information about our users, additional tokens, etc. It is defined in [`GeniusYield.Test.Privnet.Ctx`](https://haddock.atlas-app.io/GeniusYield-Test-Privnet-Ctx.html) module and it is essential to go over that module if you intend to write these tests.

Variable `info` is used to log messages and you can use it in your test's `do` block like `info $ printf "Hello from %s" "Atlas"`

We next see the use of `newTempUserCtx` utility function. As mentioned before, we already have nine users in our context, where they have the type `User`:

```haskell
data User = User
{ userSKey :: !GYPaymentSigningKey
, userAddr :: !GYAddress
{ userPaymentSKey :: !GYPaymentSigningKey
, userStakeSKey :: !(Maybe GYStakeSigningKey)
, userAddr :: !GYAddress
}
```

But at rare times, we might need to create a new user. Such a user would not be part of the context and thus would be local to the test creating it[^2].

We can do that with the help of `newTempUserCtx` function. It accepts the context parameter, the user which will fund this new user, the value to be given to this new user and a boolean denoting whether we want to create a 5-ada-only UTxO too for this new user.
We can do that with the help of `newTempUserCtx` function. It accepts the context parameter, the user which will fund this new user, the value to be given to this new user and a value of type [`CreateUserConfig`](https://haddock.atlas-app.io/GeniusYield-Test-Privnet-Ctx.html#t:CreateUserConfig).

Next we see the use of `getSlotAndConfig` function. Earlier when we wrote for PSM tests, we could work in absolute slots as we were always running each test from the beginning of ledger but this is not the case here. Thus, we would need to work with relative slots, i.e., we find the current slot and then add offset with respect to it. Function `getSlotAndConfig` has the folowing definition:
Next we see the use of `getSlotAndConfig` function. Earlier when we wrote for PSM tests, we could work in absolute slots as we were always running each test from the beginning of ledger but this is not the case here. Thus, we would need to work with relative slots, i.e., we find the current slot and then add offset with respect to it. Function `getSlotAndConfig` has the following definition:

```haskell
getSlotAndConfig :: Ctx -> IO (GYSlot, GYSlotConfig)
Expand Down Expand Up @@ -199,7 +216,7 @@ Now let's see another test where we slightly modify the last step (all the rest
assertThrown isTxBodyErrorAutoBalance $ ctxRunI ctx (ctxUser3 ctx) $ takeBets refScript brp lockedORef (userAddr (ctxUser3 ctx)) refInputORef
```

Notice that we try catching the error using `assertThrown` function. Here `isTxBodyErrorAutoBalance` is defined as (both this & `assertThrown` have their definitions in [`Asserts.hs`](https://github.com/geniusyield/atlas/tree/main/src/GeniusYield/Test/Privnet/Asserts.hs) file):
Notice that we try catching the error using `assertThrown` function. Here `isTxBodyErrorAutoBalance` is defined as (both this & `assertThrown` have their definitions in [`GeniusYield.Test.Privnet.Asserts`](https://haddock.atlas-app.io/GeniusYield-Test-Privnet-Asserts.html) module):

```haskell
isTxBodyErrorAutoBalance :: BuildTxException -> Bool
Expand All @@ -210,7 +227,7 @@ isTxBodyErrorAutoBalance _ = False
Thus our `assertThrown` function checks for two things:

1. Whether our action indeed raises an exception.
2. If an exception is raised, does it saitsfy our predicate? For instance, here our predicate was `isTxBodyErrorAutoBalance`.
2. If an exception is raised, does it satisfy our predicate? For instance, here our predicate was `isTxBodyErrorAutoBalance`.

<Callout>
You can also catch for `IO` error like:
Expand All @@ -231,4 +248,4 @@ With this we conclude upon writing integration tests.

[^4]: Therefore this function is intended to be used when we create only a single output for an external address.

[^5]: https://unix.stackexchange.com/q/367008
[^5]: https://unix.stackexchange.com/q/367008.
Loading
Loading