2

On Ethereum, I'm able to run and test a Fibonacci function by writing a Solidity smart-contract with a fib method, compiling to the EVM, deploying and running offline by using any EVM implementation, such as Sputnick:

function fib(uint n) public constant returns(uint result) {
  if (n <= 1) {
    return 1;
  } else {
    return Fib.fib(n - 1) + Fib.fib(n - 2);
  }
}

On Cardano, I'm aware I can use Haskell to create contracts, which is then compiled to Plutus. But, despite having spent some time googling, I'm still not sure how to achieve that. How do I run and test a "fib" function offline on Cardano?

2 Answers 2

1

That is indeed possible. The following will compile a fib function to plutus and execute it given the default cost model.

{-# LANGUAGE DataKinds             #-}
{-# LANGUAGE DeriveAnyClass        #-}
{-# LANGUAGE DeriveGeneric         #-}
{-# LANGUAGE FlexibleContexts      #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE NamedFieldPuns        #-}
{-# LANGUAGE NoImplicitPrelude     #-}
{-# LANGUAGE OverloadedStrings     #-}
{-# LANGUAGE RankNTypes            #-}
{-# LANGUAGE RecordWildCards       #-}
{-# LANGUAGE ScopedTypeVariables   #-}
{-# LANGUAGE TemplateHaskell       #-}
{-# LANGUAGE TypeApplications      #-}
{-# LANGUAGE TypeFamilies          #-}
{-# LANGUAGE TypeOperators         #-}

module Onchain where

import           Cardano.Api                          (PlutusScriptV2,
                                                       writeFileTextEnvelope)
import           Cardano.Api.Shelley                  (PlutusScript (..))
import           Codec.Serialise
import qualified Data.ByteString.Lazy                 as LBS
import qualified Data.ByteString.Short                as SBS
import           Data.Functor                         (void)
import qualified Ledger.Typed.Scripts                 as Scripts
import           Plutus.V1.Ledger.Api                 as PlutusV1
import           Plutus.V1.Ledger.Value               as PlutusV1
import qualified Plutus.V2.Ledger.Api                 as PlutusV2
import qualified Plutus.V2.Ledger.Contexts            as PlutusV2
import qualified PlutusTx
import           PlutusTx.Prelude                     as P 
import           Prelude                              (IO, print, map) 

import           PlutusCore.Evaluation.Machine.ExBudgetingDefaults (defaultCostModelParams)

{-# INLINEABLE fib #-}
fib :: Integer -> Integer
fib n = go n (0,1)
  where
    go x (a, b) | x==0      = a
                | otherwise = go (x-1) (b, a+b)

fibScript :: Script
fibScript = PlutusV2.fromCompiledCode ($$(PlutusTx.compile [|| fib ||]))

serialisedScript :: PlutusV2.SerializedScript
serialisedScript = SBS.toShort . LBS.toStrict $ serialise fibScript

getEvalCtx :: MonadError CostModelApplyError m => m EvaluationContext
getEvalCtx = case defaultCostModelParams of
              Nothing -> Err.throwError CMInternalReadError
              Just n  -> mkEvaluationContext n

runScript :: MonadError CostModelApplyError m => Integer -> m (LogOutput, Either EvaluationError ExBudget)
runScript n = getEvalCtx >>= (\c -> return (evaluateScriptCounting a b c serialisedScript [I n]))
              where
                a :: PlutusV1.ProtocolVersion
                a = ProtocolVersion 7 0
                b :: PlutusV1.VerboseMode
                b = Quiet
2
  • 1
    Thank you very much. Makes a lot of sense to me now. I think these evaluation functions should be documented somewhere.
    – MaiaVictor
    Dec 20, 2022 at 0:39
  • What I always do, git clone the latest release plutus app repo (not main), then enter a nix-shell and execute build-and-serve-docs. This will run a local haddock server with all functions, really handy if you are searching for something! It's an up-to-date version of playground.plutus.iohkdev.io/doc/haddock
    – Fermat
    Dec 20, 2022 at 7:57
1

What do you want to do with the output of your fib-function ? Also with "offline" you probably mean off-chain. If you want to compute the Fibonacci sequence off-chain you can use pure Haskell. There are many examples like this one:

fib 0 = 0
fib 1 = 1
fib n = fib (n-1) + fib (n-2)

This is just an inefficient naive way. Other solutions can be found here: https://wiki.haskell.org/The_Fibonacci_sequence

And then in your contract:

fibonacci :: forall w s. FibParams -> Contract w s Text ()
fibonacci param = do
    pkh <- ownPaymentPubKeyHash
    ... --do stuff with f
    unbalTx <- ...
    ledgerTx <- submitUnbalancedTx $ Constraints.adjustUnbalancedTx unbalTx
    awaitTxConfirmed $ getCardanoTxId ledgerTx
    Contract.logInfo @String $ "submitted tx"
  where
    f = fib (param num)

For testing you need to look into runEmulatorTraceIO.

1
  • I want to run fib in the context of Cardano's VM (is there one?) in order to benchmark its performance. Cardano doesn't run Haskell directly on nodes, right? It compiles to Plutus, and then interprets. I want to go through it to benchmark the Plutus interpreter versus native GHC Fib.
    – MaiaVictor
    Dec 15, 2022 at 18:49

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.