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

Toml #30

Merged
merged 14 commits into from
Apr 9, 2019
Merged

Toml #30

Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
/.purs*
/.psa*
/abis/**/*.json
./plasma.toml
/purs/src/Plasma/Contracts
*.swp
chanterelle.js
Expand Down
3 changes: 2 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ install:

# install plasma
- plasmad init
- make prepare-plasma
- make write-plasma-toml
- cp plasma.toml $HOME/.plasmad/config/plasma.toml
- mkdir -p $HOME/.plasmacli
- ln -s $HOME/.plasmad/config/plasma.toml $HOME/.plasmacli/plasma.toml
- plasmad start &
Expand Down
10 changes: 8 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ export
FINALIZED_PERIOD ?= 18
NODE_URL ?= http://localhost:8545

# plasma config vars, need to supply operator private key
PLASMA_CONFIG_DESTINATION ?= ./plasma.toml
IS_OPERATOR ?= false
COMMITMENT_RATE ?= 2
PLASMA_ARTIFACT ?= ./abis/PlasmaMVP.json

help: ## Ask for help!
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'

Expand All @@ -24,8 +30,8 @@ compile-contracts: build-purs ## Compile all contracts from dapp/contracts and w
generate-genesis: ## Generate a cliquebait.json file
chanterelle genesis --input ./cliquebait.json --output cliquebait-generated.json

prepare-plasma:
sed -i "/ethereum_plasma_contract_address = /c\ethereum_plasma_contract_address = `cat abis/PlasmaMVP.json | jq \".networks[].address\"`" "$(HOME)/.plasmad/config/plasma.toml"
write-plasma-toml: ## write the plasma config to the plasma.toml file
pulp run --jobs 8 --src-path purs/src -m Plasma.Config.TOMLMain

test-plasma: ## Run the plasma e2e
NODE_URL=$(NODE_URL) pulp test --src-path purs/src --test-path purs/test -m Spec.Main
Expand Down
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,11 @@ We now provide a super simple way to run the test-suite. It only depends on dock

```
> make install
> docker-compose up -d
> docker-compose up -d cliquebait
> make deploy-contracts
> make write-plasma-toml
> sleep 5
> docker-compose up -d plasma
> make deploy-and-test
> docker-compose down
```
Expand Down Expand Up @@ -108,6 +111,9 @@ I[2046-04-02|14:05:54.388] binding to contract address 0xe545eaf693277ead76f5d9b

```

## Generating a plasma.toml config file
You can generate a plasma.toml config file by running `make write-plasma-toml`. If you would like to create a config for an `Operator`, set `IS_OPERATOR=true OPERATOR_PRIVATE_KEY=<whatever-it-is> make write-plasma-toml`.

## WARNINGS
We are still trying to figure out how to automate config/setup so that it's the same everywhere (test config vs $HOME/.plasmad/config/plasma.toml etc). Until that's done, you should check this warnings list/ update it with new warnings when you find them.
1. When running `make plasma-test` you need to supply the root chain plasma contract with environment variable `PLASMA_ADDRESS`.
Expand Down
3 changes: 2 additions & 1 deletion bower.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
"purescript-web3": "^1.0.0",
"purescript-web3-generator": "^1.0.0",
"purescript-chanterelle": "f-o-a-m/chanterelle#v2.0.2",
"purescript-servant": "f-o-a-m/purescript-servant#master"
"purescript-servant": "f-o-a-m/purescript-servant#master",
"purescript-heterogeneous": "0.2.0"
},
"devDependencies": {
"purescript-psci-support": "^4.0.0",
Expand Down
2 changes: 2 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,5 @@ services:
image: docker.kube-system.svc.cluster.local/foam/plasma-mvp-sidechain:latest
ports:
- "1317:1317"
volumes:
- ./plasma.toml:/root/.plasmad/config/plasma.toml
219 changes: 219 additions & 0 deletions purs/src/Plasma/Config/TOML.purs
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
module Plasma.Config.TOML
( PlasmaConfig
, TimeInterval
, writePlasmaConfig
, makeConfigFromEnvironment
) where

import Prelude

import Chanterelle.Internal.Deploy (readDeployAddress)
import Chanterelle.Internal.Logging (logDeployError)
import Control.Error.Util (note)
import Control.Monad.Except (runExceptT)
import Data.Array ((:))
import Data.Bifunctor (lmap)
import Data.Either (Either(..))
import Data.Int (fromString)
import Data.Maybe (Maybe(..), maybe)
import Data.String (joinWith)
import Data.Symbol (class IsSymbol, SProxy, reflectSymbol)
import Data.Tuple (Tuple(..))
import Effect.Aff (Aff)
import Effect.Class (class MonadEffect, liftEffect)
import Effect.Class.Console as C
import Effect.Exception (throw)
import Heterogeneous.Folding (class FoldingWithIndex, hfoldlWithIndex)
import Network.Ethereum.Core.BigNumber (parseBigNumber, decimal)
import Network.Ethereum.Core.HexString (unHex)
import Network.Ethereum.Core.Signatures (PrivateKey, unPrivateKey, mkPrivateKey)
import Network.Ethereum.Web3 (Address, Provider, runWeb3, httpProvider, mkHexString, mkAddress)
import Network.Ethereum.Web3.Api (net_version)
import Node.Encoding (Encoding(UTF8))
import Node.FS.Aff (writeTextFile)
import Node.Process as NP
import Partial.Unsafe (unsafeCrashWith)

{-

EXAMPLE

# This is a TOML config file.
# For more information, see https://github.com/toml-lang/toml

##### ethereum config options #####
# Boolean specifying if this node is the operator of the plasma contract
is_operator = "true"

# Hex encoded private key
# Used to sign eth transactions interacting with the contract
ethereum_operator_privatekey = "ac4d28b45202507d3475761c1e9277d3efc5a8ae8cfd690d979083ce241dd6d8"

# Ethereum plasma contract address
ethereum_plasma_contract_address = "0x05df3e846693afefce3dd9b6701787ad1d6cd9b1"

# Plasma block commitment rate. i.e 1m30s, 1m, 1h, etc.
plasma_block_commitment_rate = "2s"

# Node URL for eth client
ethereum_nodeurl = "http://localhost:8545"

# Number of Ethereum blocks until a submitted block header is considered final
ethereum_finality = "2"


-}

withQuotes :: String -> String
withQuotes s = "\""<> s <> "\""

newtype TimeInterval = TimeInterval Int

class TomlValue a where
toTomlValue :: a -> String

instance tomlValueInt :: TomlValue Int where
toTomlValue = show >>> withQuotes
martyall marked this conversation as resolved.
Show resolved Hide resolved

instance tomlValueString :: TomlValue String where
toTomlValue = show

instance tomlValueTimeInterval :: TomlValue TimeInterval where
toTomlValue (TimeInterval n) = withQuotes $ show n <> "s"
martyall marked this conversation as resolved.
Show resolved Hide resolved

instance tomlValuePrivateKey :: TomlValue PrivateKey where
toTomlValue = unPrivateKey >>> unHex >>> withQuotes

instance tomlValueAddress :: TomlValue Address where
toTomlValue = show >>> withQuotes

instance tomlValueBool :: TomlValue Boolean where
toTomlValue = show >>> withQuotes
martyall marked this conversation as resolved.
Show resolved Hide resolved

instance tomlValueMaybe :: TomlValue a => TomlValue (Maybe a) where
toTomlValue = maybe (toTomlValue "") toTomlValue

type PlasmaConfig =
{ is_operator :: Boolean
, ethereum_operator_privatekey :: Maybe PrivateKey
, ethereum_plasma_contract_address :: Address
, plasma_block_commitment_rate :: TimeInterval
, ethereum_nodeurl :: String
, ethereum_finality :: Int
}

--------------------------------------------------------------------------------
-- Generate toml file
--------------------------------------------------------------------------------

type TomlFile = Array (Tuple String String)

data TomlEntry = TomlEntry

instance tomlEntry :: (TomlValue a, IsSymbol sym) => FoldingWithIndex TomlEntry (SProxy sym) (Array (Tuple String String)) a (Array (Tuple String String)) where
foldingWithIndex TomlEntry prop acc a = Tuple (reflectSymbol prop) (toTomlValue a) : acc

-- | Using the toTomlValue instances, fold over the PlasmaConfig record to build the toml file as a string
templateTomlFile :: PlasmaConfig -> String
templateTomlFile = joinWith "\n" <<< map (\(Tuple k v) -> k <> " = " <> v) <<< toTomlFile
where
toTomlFile :: PlasmaConfig -> TomlFile
toTomlFile = hfoldlWithIndex TomlEntry ([] :: TomlFile)

-- | read a bunch env vars and dynamically try to figure out where the plasma contract address is coming from.
makeConfigFromEnvironment :: Aff PlasmaConfig
makeConfigFromEnvironment = do
isOperator <- requireEnvVar "IS_OPERATOR" asBoolean
operatorKey <- if isOperator
then Just <$> requireEnvVar "OPERATOR_PRIVATE_KEY" asPrivateKey
else pure Nothing
commitmentRate <- requireEnvVar "COMMITMENT_RATE" asInt
nodeURL <- requireEnvVar "NODE_URL" asString
finality <- requireEnvVar "FINALIZED_PERIOD" asInt
plasmaAddress <- discoverPlasmaContractAddress
martyall marked this conversation as resolved.
Show resolved Hide resolved
pure { is_operator: isOperator
, ethereum_operator_privatekey: operatorKey
, ethereum_plasma_contract_address: plasmaAddress
, plasma_block_commitment_rate: TimeInterval commitmentRate
, ethereum_nodeurl: nodeURL
, ethereum_finality: finality
}

-- | write the plasma config to a file.
writePlasmaConfig :: PlasmaConfig -> Aff Unit
writePlasmaConfig cfg = do
configDest <- requireEnvVar "PLASMA_CONFIG_DESTINATION" asString
let content = templateTomlFile cfg
C.log ("Writing plasma config to " <> configDest)
writeTextFile UTF8 configDest content

--------------------------------------------------------------------------------
-- | ConfigUtils
--------------------------------------------------------------------------------

data AddressSource =
FromChanterelleArtifactFile Provider String
| AddressLiteral Address


discoverPlasmaContractAddress :: Aff Address
discoverPlasmaContractAddress = do
mAddr <- liftEffect (NP.lookupEnv "PLASMA_ADDRESS")
case mAddr of
Nothing -> do
C.log "PLASMA_ADDRESS env var not found, trying to read from file..."
fp <- requireEnvVar "PLASMA_ARTIFACT" asString
provider <- requireEnvVar "NODE_URL" asString >>= liftEffect <<< httpProvider
getPlasmaContractAddress (FromChanterelleArtifactFile provider fp)
Just addr -> case mkHexString addr >>= mkAddress of
Nothing -> liftEffect $ throw ("Error parsing PLASMA_ADDRESS: " <> show addr)
Just addr' -> getPlasmaContractAddress (AddressLiteral addr')
Copy link
Contributor

Choose a reason for hiding this comment

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

Passing AddressLiteral to getPlasmaContractAddress is besically no op why not just do that log here and pure addr', and remove the AddressSource data type.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I will decide whether or not to do this after we have a more standard setup script. My hope is that we can somehow do a fresh contract deployment / cliquebait for a test suite, but I also want to be able to run it from an existing deployment.

I did it yesterday for a reason I can't recall right now, but will only change it if I'm sure I don't want it. It works for now


getPlasmaContractAddress :: AddressSource -> Aff Address
getPlasmaContractAddress (AddressLiteral addr) = do
C.log "Plasma Contract Address given as literal"
pure addr
getPlasmaContractAddress (FromChanterelleArtifactFile provider filepath) = do
C.log $ "Taking Plasma Contract Address from file: " <> filepath
enId <- runWeb3 provider $ net_version
case lmap show enId >>= \nid -> note ("Couldn't parse network version: " <> nid) $ parseBigNumber decimal nid of
Left e -> liftEffect $ throw e
Right nId -> do
eRes <- runExceptT $ readDeployAddress filepath nId
case eRes of
Left e -> do
logDeployError e
liftEffect $ throw "error"
Right res -> pure res


type FromStringReader a = String -> Either String a

requireEnvVar :: forall m a. MonadEffect m => String -> FromStringReader a -> m a
requireEnvVar var parse = liftEffect $ do
mval <- NP.lookupEnv var
case mval of
Nothing -> unsafeCrashWith $ "Must specify " <> show var <> " env var"
Just val -> case parse val of
Left err -> unsafeCrashWith
$ "Failed to parse env var: " <> show var
<> ", containing: " <> show val
<> ", with error: " <> err
Right a -> pure a

asBoolean :: FromStringReader Boolean
asBoolean "true" = Right true
asBoolean "false" = Right false
asBoolean _ = Left "Invalid Boolean"

asInt :: FromStringReader Int
asInt = fromString >>> note "invalid Int"

asString :: FromStringReader String
asString = pure

asPrivateKey :: FromStringReader PrivateKey
asPrivateKey = (mkHexString >=> mkPrivateKey) >>> note "invalid PrivateKey"

asAddress :: FromStringReader Address
asAddress = (mkHexString >=> mkAddress) >>> note "invalid Address"
11 changes: 11 additions & 0 deletions purs/src/Plasma/Config/TOMLMain.purs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module Plasma.Config.TOMLMain (main) where

import Prelude
import Plasma.Config.TOML (makeConfigFromEnvironment, writePlasmaConfig)
import Effect (Effect)
import Effect.Aff (launchAff_)

main :: Effect Unit
main = launchAff_ do
cfg <- makeConfigFromEnvironment
writePlasmaConfig cfg