- Setting up a Blockfrost-powered test suite
- Running
Contract
s with Blockfrost - Running Blockfrost locally
- Limitations
- See also
Thanks to Catalyst Fund9, CTL has been extended with support for Blockfrost as an alternative query layer.
The users can now run CTL contracts just by providing a Blockfrost API key and some ADA for the Contract to consume.
For testing, we offer an automated test engine that allows to run any ContractTest
test suite with Blockfrost.
In this section, we show how to set up a test suite that uses public Blockfrost deployment to run Contract
s and prints the report nicely using Mote.
Public Blockfrost instances have endpoints for different networks. By default, the test suite is configured to run on preview
.
The configuration is stored in environment variables defined in test/blockfrost.env
file, or a similar one in your project if it is initialized from the template.
Here's how to populate this configuration file to be ready for use:
Go to https://blockfrost.io to generate a new API key and specify it as BLOCKFROST_API_KEY
in the config.
Follow this guide to generate a private payment key (and, optionally, a stake key). You can use this script for convenience instead of following instructions in this section manually.
The generated keys should look like this:
{
"type": "PaymentSigningKeyShelley_ed25519",
"description": "Payment Signing Key",
"cborHex": "..."
}
Get the address for this payment key (and, optionally, a stake key), following the guide above.
If you are using a testnet, replace --mainnet
flag in the shell command with
--testnet-magic YOUR_NETWORK_MAGIC
, where YOUR_NETWORK_MAGIC
is a genesis
parameter of the network.
For public testnets, get it from cardano-configurations repo. The location is network/YOUR_NETWORK_NAME/genesis/shelley.json
, look for networkMagic
key.
The common values are 1 for preprod
and 2 for preview
.
Fund your address using the testnet faucet. Make sure you are sending the funds in the correct network.
Point the test suite to your keys by setting PRIVATE_PAYMENT_KEY_FILE
and PRIVATE_STAKE_KEY_FILE
to the paths of your .skey
files.
If you are going to use an enterprise address (without a staking credential component), then do not provide the staking key file. The choice of using either type of addresses does not affect anything, because the test suite will be using the address only to distribute funds to other, temporary addresses.
During testing, the test engine will move funds around according to the UTxO distribution specifications provided via Contract.Test.withWallets
calls in the test bodies. It will generate private keys as needed on the fly. The private keys will be stored in a special directory, to prevent loss of funds in case the test suite suddently exits. Set BACKUP_KEYS_DIR
to an existing directory where you would like the keys to be stored.
In this directory, keys will be stored in subdirs named as addresses that are derived from these keys. Most of these directories will contain a file named inactive
. It indicates that the test suite assumes that there are no funds left (because they have been withdrawn successfully).
Each test run generates fresh keys that will be stored indefinitely, and it's up to the user to decide when to delete the corresponding directories. The reason why the keys are not being disposed of automatically is because there may be some on-chain state uniquely tied to them that the user may not want to lose access to.
Blockfrost dashboard provides endpoint URLs for your projects.
In the test suite configuration, parts of the endpoint URLs are specified separately, e.g. https://cardano-preview.blockfrost.io/api/v0/
becomes:
export BLOCKFROST_PORT=443 # https -> 443, http -> 80
export BLOCKFROST_HOST=cardano-preview.blockfrost.io
export BLOCKFROST_SECURE=true # Use HTTPS
export BLOCKFROST_PATH="/api/v0"
We introduce an artificial delay after Tx confirmation to ensure that the changes propagate to Blockfrost's query layer. Blockfrost does not update the query layer state atomically (proxied Ogmios eval-tx endpoint seems to lag behind the DB), and we have no way to query it, so this is the best workaround we can have. If the tests are failing because the effects of the transaction do not seem to propagate (the symptom is unexpected errors from Ogmios), it is possible to increase the delay by setting the environment variable for the test suite:
export TX_CONFIRMATION_DELAY_SECONDS=30
The "safe" value in practice is 30 seconds.
If there's a problem with UTxO set syncrhonization, most commonly Blockfrost returns error code 400 on transaction submission:
[TRACE] 2023-02-16T12:26:13.019Z { body: "{\"error\":\"Bad Request\",\"message\":\"\\\"transaction submit error ShelleyTxValidationError ShelleyBasedEraBabbage (ApplyTxError [UtxowFailure (UtxoFailure (FromAlonzoUtxoFail (ValueNotConservedUTxO ...
executeContractTestsWithBlockfrost
is a helper function that reads all the variables above and takes care of contract environment setup.
It accepts a number of arguments:
- A test spec config, e.g.
Test.Spec.Runner.defaultConfig
- it's probably better to increase the timeout to 300 seconds or more. - A
Contract
config, e.g.Contract.Config.testnetConfig
- A
ContractTest
suite
See this example, which can be executed with npm run blockfrost-test
command in CTL repo. It will automatically load the exported variables from test/blockfrost.env
.
On production, the environment should be configured on PureScript side (environment variables are not respected).
mkBlockfrostBackendParams
can be called on a populated BlockfrostBackendParams
record to create a QueryBackendParams
value. backendParams
field of ContractParams
uses a value of this type. And ContractParams
can in turn be used with runContract
.
type BlockfrostBackendParams =
{ blockfrostConfig :: ServerConfig
, blockfrostApiKey :: Maybe String
, confirmTxDelay :: Maybe Seconds
}
For convenience, use blockfrostPublicMainnetServerConfig
, blockfrostPublicPreviewServerConfig
or blockfrostPublicPreprodServerConfig
for pre-configured ServerConfig
setups.
Blockfrost can be started locally using blockfrost-backend-ryo distribution. It is included in CTL runtime as optional service, use npm run start-blockfrost-runtime
instead of npm run start-runtime
.
The differences between public and run-your-own flavours of Blockfrost are:
- There's no need to provide an API key
/tx/submit
and/utils/txs/evaluate
endpoints, that are simply proxied to Ogmios on the public instance, are not provided within the distribution, so Ogmios should be used for these.
Configuration on PureScript side is the same as above, but mkSelfHostedBlockfrostBackendParams
should be used, to specify Ogmios and Kupo parameters (only Ogmios will in fact be used).
Contract.Config.blockfrostSelfHostedServerConfig
contains default server parameters usable with a locally-deployed Blockfrost instance, together with defaultOgmiosWsConfig
.
CTL handles the absence of submit
and evaluate
endpoints in self-hosted Blockfrost nicely by setting up own runtime to query Ogmios for these two endpoints when mkSelfHostedBlockfrostBackendParams
is used.
The Blockfrost test suite can also be configured for local runtime. To let it connect to local Ogmios, these four parameters must be provided:
export OGMIOS_PORT=1337
export OGMIOS_HOST=127.0.0.1
export OGMIOS_PATH=""
export OGMIOS_SECURE=false
See blockfrost-local.env
for a complete example.
cardano-db-sync
takes 30 minutes or more to synchronize with preview
network (and many hours with mainnet
).
It will print messages like these, so you can estimate the sync progress knowing the latest epoch number:
db-sync-preview_1 | [db-sync-node:Info:70] [2023-03-06 15:09:27.39 UTC] Starting epoch 38
db-sync-preview_1 | [db-sync-node:Info:70] [2023-03-06 15:09:27.40 UTC] epochPluginInsertBlockDetails: epoch 37
The main disadvantage of using Blockfrost in comparison with CTL backend is speed of Tx confirmation (see here for explanation).
Contracts running with local Blockfrost instance are expected to be slightly slower, because transaction propagation takes a bit longer.
Public Blockfrost instance is proxying Ogmios to provide an endpoint for execution units evaluation. This Ogmios endpoint normally accepts a parameter that allows to specify additional UTxOs that should be considered. Transaction chaining is relying on this feature to allow Ogmios to "see" the newly created UTxOs. But Blockfrost seems to not pass this parameter to Ogmios (issue).
When using self-hosted blockfrost, chaining works normally, because local Ogmios is used without proxying.
getPoolParameters
function only runs with CTL backend (Ogmios), see here for more context.
This function is never used for constraints resolution, the only way to make it run is to call it manually.
- key-directory test suite that is used under the hood by
executeContractTestsWithBlockfrost
. This test machinery can be used with any runtime backend, not just Blockfrost. - Testing utilities for CTL.