Skip to content

Commit aa382a4

Browse files
committed
wip: time slot <-> time conversions
- v1 working variant, checked on testnet
1 parent e50c779 commit aa382a4

File tree

12 files changed

+786
-26
lines changed

12 files changed

+786
-26
lines changed

cabal.project

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ packages: ./bot-plutus-interface.cabal
44
./examples/plutus-game/plutus-game.cabal
55
./examples/plutus-transfer/plutus-transfer.cabal
66
./examples/plutus-nft/plutus-nft.cabal
7+
./examples/ex-units/ex-units.cabal
78

89
tests: true
910
benchmarks: true

examples/ex-units/app/Main.hs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
module Main (main) where
2+
3+
import PlutipRun qualified as Plutip
4+
import TestnetRun qualified as Testnet
5+
import Prelude
6+
7+
{- | For running fast live tests using Plutip's local cluster,
8+
needed only for debugging period
9+
-}
10+
main :: IO ()
11+
main = do
12+
-- Plutip.plutipRun
13+
Testnet.testnetRun

examples/ex-units/comparison.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# Comparison of slot <-> time conversion made by Ledger with SlotConfig and custom ones with node queries
2+
3+
```
4+
Starting tip: Right (Tip {epoch = 1, hash = "fca4108ac2356c479ca21621bd4531a88760ac02e9a9e748301b62710865a5f1", slot = 100, block = 13, era = "Alonzo", syncProgress = "100.00"})
5+
Converting POSIXTime {getPOSIXTime = 1652945917999} to slot:
6+
- with node queries: Slot {getSlot = 105}
7+
- with ledger stuff: Slot {getSlot = 105}
8+
Converting Slot {getSlot = 108} to time:
9+
- with node queries: POSIXTime {getPOSIXTime = 1652945920999}
10+
- with ledger stuff: POSIXTime {getPOSIXTime = 1652945920999}
11+
Current tip: Right (Tip {epoch = 1, hash = "50e92ee38c01745eb0862fd7e1938faab6d11f9b86405da948a385f950152547", slot = 108, block = 14, era = "Alonzo", syncProgress = "100.00"})
12+
Ledger SlotRange: Interval {ivFrom = LowerBound (Finite (Slot {getSlot = 100})) False, ivTo = UpperBound (Finite (Slot {getSlot = 102})) True}
13+
Query SlotRange: Interval {ivFrom = LowerBound (Finite (Slot {getSlot = 100})) False, ivTo = UpperBound (Finite (Slot {getSlot = 102})) True}
14+
```
15+
16+
17+
## Querries result
18+
19+
```
20+
Slot length: POSIXTime {getPOSIXTime = 1000}
21+
Ledger SlotRange: Interval {ivFrom = LowerBound (Finite (Slot {getSlot = 148})) True, ivTo = UpperBound (Finite (Slot {getSlot = 158})) False}
22+
Query SlotRange: Interval {ivFrom = LowerBound (Finite (Slot {getSlot = 148})) True, ivTo = UpperBound (Finite (Slot {getSlot = 158})) True}
23+
24+
[\\\"Upper bounds not same\\\",\\\"Ranges not equal\\\",\\\"PT5\\\"]
25+
```
26+
27+
## Ledger result
28+
29+
```
30+
Interval {ivFrom = LowerBound (Finite (Slot {getSlot = 80})) True, ivTo = UpperBound (Finite (Slot {getSlot = 90})) False}
31+
32+
33+
[\\\"End not in range\\\",\\\"Upper bounds not same\\\",\\\"Ranges not equal\\\",\\\"PT5\\\"]
34+
```
35+
36+
37+
Query SlotRange: Interval {ivFrom = LowerBound (Finite (Slot {getSlot = 416})) False, ivTo = UpperBound (Finite (Slot {getSlot = 426})) False}
38+
Query SlotRange: Interval {ivFrom = LowerBound (Finite (Slot {getSlot = 480})) True, ivTo = UpperBound (Finite (Slot {getSlot = 490})) True}

examples/ex-units/ex-units.cabal

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
cabal-version: 3.0
2+
name: ex-units
3+
version: 0.1.0.0
4+
synopsis: NFT example
5+
description: NFT example running on the fake pab
6+
homepage: https://github.com/mlabs-haskell/bot-plutus-interface
7+
bug-reports: https://github.com/mlabs-haskell/bot-plutus-interface
8+
license:
9+
license-file:
10+
author: MLabs
11+
maintainer: gergely@mlabs.city
12+
copyright: TODO
13+
build-type: Simple
14+
tested-with: GHC ==8.10.4
15+
extra-source-files: README.md
16+
17+
source-repository head
18+
type: git
19+
location: https://github.com/mlabs-haskell/bot-plutus-interface
20+
21+
-- Common sections
22+
23+
common common-lang
24+
ghc-options:
25+
-Wall -Wcompat -Wincomplete-record-updates
26+
-Wincomplete-uni-patterns -Wredundant-constraints
27+
-- -Werror
28+
-fobject-code -fno-ignore-interface-pragmas
29+
-fno-omit-interface-pragmas -Wunused-packages
30+
-fplugin-opt PlutusTx.Plugin:defer-errors
31+
32+
build-depends:
33+
, base ^>=4.14
34+
35+
default-extensions:
36+
NoImplicitPrelude
37+
BangPatterns
38+
BinaryLiterals
39+
ConstraintKinds
40+
DataKinds
41+
DeriveFunctor
42+
DeriveGeneric
43+
DeriveTraversable
44+
DerivingStrategies
45+
DerivingVia
46+
DuplicateRecordFields
47+
EmptyCase
48+
FlexibleContexts
49+
FlexibleInstances
50+
GADTs
51+
GeneralizedNewtypeDeriving
52+
HexFloatLiterals
53+
ImportQualifiedPost
54+
InstanceSigs
55+
KindSignatures
56+
LambdaCase
57+
MultiParamTypeClasses
58+
NumericUnderscores
59+
OverloadedStrings
60+
ScopedTypeVariables
61+
StandaloneDeriving
62+
TupleSections
63+
TypeApplications
64+
TypeOperators
65+
TypeSynonymInstances
66+
UndecidableInstances
67+
TemplateHaskell
68+
TypeFamilies
69+
70+
default-language: Haskell2010
71+
72+
-- Libraries
73+
74+
75+
library
76+
import: common-lang
77+
exposed-modules:
78+
TimeDebugContract
79+
PlutipRun
80+
TestnetRun
81+
82+
build-depends:
83+
plutus-ledger
84+
, containers
85+
, plutus-contract
86+
, plutus-ledger-api
87+
, plutus-ledger-constraints
88+
, plutus-pab
89+
, plutus-tx
90+
, text
91+
, plutus-tx-plugin
92+
, aeson
93+
, bot-plutus-interface
94+
, cardano-api
95+
, data-default
96+
, servant-client
97+
, filepath
98+
, text
99+
, directory
100+
, plutus-ledger
101+
, uuid
102+
, stm
103+
, plutus-pab
104+
, plutus-contract
105+
106+
107+
108+
hs-source-dirs: src
109+
110+
executable ex-units-run
111+
import: common-lang
112+
build-depends:
113+
ex-units
114+
115+
116+
main-is: Main.hs
117+
hs-source-dirs: app

examples/ex-units/hie.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
cradle:
2+
cabal:
3+
- path: "./src"
4+
component: "lib:ex-units"
5+
6+
- path: "./app"
7+
component: "exe:ex-units-run"

examples/ex-units/src/PlutipRun.hs

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
module PlutipRun (plutipRun) where
2+
3+
import BotPlutusInterface.Contract qualified as BPI
4+
import BotPlutusInterface.QueryNode qualified as BPI
5+
import BotPlutusInterface.Types
6+
import Cardano.Api (NetworkId (Mainnet))
7+
import Cardano.Api.Shelley (ProtocolParameters)
8+
import Control.Concurrent.STM (newTVarIO, readTVarIO)
9+
import Data.Aeson ((.=))
10+
import Data.Aeson qualified as JSON
11+
import Data.Default (def)
12+
import Data.Text (Text)
13+
import Data.Text qualified as Text
14+
import Data.UUID.V4 qualified as UUID
15+
import Ledger (POSIXTime (POSIXTime), PubKeyHash)
16+
import TimeDebugContract qualified
17+
18+
import GHC.IO.Encoding (setLocaleEncoding, utf8)
19+
import Ledger.TimeSlot (SlotConfig (scSlotZeroTime))
20+
import Plutus.PAB.Core.ContractInstance.STM (Activity (Active))
21+
import Servant.Client (BaseUrl (BaseUrl), Scheme (Http))
22+
import System.Directory (listDirectory)
23+
import System.Environment (getArgs, getEnv, setEnv)
24+
import System.FilePath ((</>))
25+
import Wallet.Types (ContractInstanceId (ContractInstanceId))
26+
import Prelude
27+
28+
plutipRun :: IO ()
29+
plutipRun = do
30+
setLocaleEncoding utf8
31+
[sockPath, clusterDir, cliDir] <- getArgs
32+
setEnv "CARDANO_NODE_SOCKET_PATH" sockPath
33+
getEnv "PATH" >>= \p -> setEnv "PATH" (p ++ ":" ++ cliDir)
34+
let nodeInfo = BPI.NodeInfo Mainnet sockPath
35+
36+
cEnv <- mkContractEnv nodeInfo clusterDir
37+
putStrLn "Running contract"
38+
res <- BPI.runContract cEnv TimeDebugContract.unlockWithTimeCheck
39+
putStrLn $ case res of
40+
Right r -> "=== OK ===\n" ++ show r
41+
Left e -> "=== FAILED ===\n" ++ show e
42+
43+
stats <- readTVarIO (ceContractStats cEnv)
44+
putStrLn $ "=== Stats ===\n" ++ show stats
45+
46+
mkContractEnv :: Monoid w => BPI.NodeInfo -> FilePath -> IO (ContractEnvironment w)
47+
mkContractEnv nodeInfo clusterDir = do
48+
(pparams, paramsFile) <- getPparams nodeInfo clusterDir
49+
contractInstanceID <- ContractInstanceId <$> UUID.nextRandom
50+
contractState <- newTVarIO (ContractState Active mempty)
51+
contractStats <- newTVarIO (ContractStats mempty)
52+
pkhs <- getPkhs clusterDir
53+
return $
54+
ContractEnvironment
55+
{ cePABConfig = mkPabConf pparams (Text.pack paramsFile) clusterDir (head pkhs)
56+
, ceContractState = contractState
57+
, ceContractInstanceId = contractInstanceID
58+
, ceContractStats = contractStats
59+
}
60+
61+
getPparams :: BPI.NodeInfo -> FilePath -> IO (ProtocolParameters, FilePath)
62+
getPparams nodeInfo clusterDir = do
63+
pparams :: ProtocolParameters <- getOrFailM $ BPI.queryProtocolParams nodeInfo
64+
let ppath = clusterDir </> "pparams.json"
65+
JSON.encodeFile ppath pparams
66+
return (pparams, ppath)
67+
68+
mkPabConf :: ProtocolParameters -> Text -> FilePath -> PubKeyHash -> PABConfig
69+
mkPabConf pparams pparamsFile clusterDir ownPkh =
70+
PABConfig
71+
{ pcCliLocation = Local
72+
, pcNetwork = Mainnet
73+
, pcChainIndexUrl = BaseUrl Http "localhost" 9083 ""
74+
, pcPort = 9080
75+
, pcProtocolParams = pparams
76+
, pcTipPollingInterval = 1_000_000
77+
, pcSlotConfig = def {scSlotZeroTime = POSIXTime $ 1652956123 * 1000}
78+
, pcOwnPubKeyHash = ownPkh
79+
, pcOwnStakePubKeyHash = Nothing
80+
, pcScriptFileDir = Text.pack $ clusterDir </> "bot-plutus-interface/scripts"
81+
, pcSigningKeyFileDir = Text.pack $ clusterDir </> "bot-plutus-interface/signing-keys"
82+
, pcTxFileDir = Text.pack $ clusterDir </> "bot-plutus-interface/txs"
83+
, pcDryRun = False
84+
, pcLogLevel = Error
85+
, pcProtocolParamsFile = pparamsFile
86+
, pcEnableTxEndpoint = True
87+
, pcCollectStats = True
88+
, pcMetadataDir = Text.pack $ clusterDir </> "bot-plutus-interface/metadata"
89+
}
90+
91+
getPkhs :: FilePath -> IO [PubKeyHash]
92+
getPkhs bpiDir = do
93+
let dir = bpiDir </> "bot-plutus-interface/signing-keys"
94+
replace =
95+
Text.unpack
96+
. Text.replace "signing-key-" ""
97+
. Text.replace ".skey" ""
98+
. Text.pack
99+
keyNames <- listDirectory dir
100+
return $ map (parseKey . replace) keyNames
101+
where
102+
parseKey :: String -> PubKeyHash
103+
parseKey key =
104+
let res = JSON.fromJSON $ JSON.object ["getPubKeyHash" .= key]
105+
in case res of
106+
JSON.Success pkh -> pkh
107+
_ -> error "failed to parse pkh"
108+
109+
getOrFail :: Show e => Either e a -> a
110+
getOrFail = either (error . show) id
111+
112+
getOrFailM :: (Show e, Functor f) => f (Either e b) -> f b
113+
getOrFailM = (getOrFail <$>)

0 commit comments

Comments
 (0)