Comprehensive framework to build network actors and distributed applications
Haskell
44
2,468 commits
updated Mar 14, 2019
Enecuum Node Framework allows to build network actors and blockchain protocols, console applications, work with KV database and cryptography. Current features include:
The Node project contains:
The framework represents a set of embedded monadic languages organized hierarchically. The languages are divided to core languages responsible for common subsystems and framework languages responsible for network and actors behavior.
curl -sSL https://get.haskellstack.org/ | sh
sudo nano ~/.profile and append export PATH=$PATH:$HOME/.local/bin at the end.
sudo apt update && sudo apt install librocksdb-dev libgd-dev libtinfo-dev -y
git clone https://github.com/Enecuum/Node.git && cd Node
stack build
Run all tests:
stack test
Run fast tests:
stack build --test --test-arguments "-m Fast"
Run slow and unreliable tests:
stack build --test --test-arguments "-m Slow"
enq-test-node-haskell is a single executable for sample nodes.
GraphNode Transmitter
stack exec enq-test-node-haskell singlenode ./configs/tst_graph_node_transmitter.json
GraphNode Receiver
stack exec enq-test-node-haskell singlenode ./configs/tst_graph_node_receiver.json
Gen PoW
stack exec enq-test-node-haskell singlenode ./configs/tst_gen_pow.json
Gen PoA
stack exec enq-test-node-haskell singlenode ./configs/tst_gen_poa.json
Console client
stack exec enq-test-node-haskell singlenode ./configs/tst_client.json
In this sample, two nodes interact via network sending UDP messages.
Ping server node
Ping messages.Pons message back to the client.stack exec enq-test-node-haskell singlenode ./configs/tst_ping_server.json
Pong client node
Ping messages to the server periodically.Pong messages from the server.stack exec enq-test-node-haskell singlenode ./configs/tst_pong_client1.json
stack exec enq-test-node-haskell singlenode ./configs/tst_pong_client2.json
-- Messages
newtype Ping = Ping Text deriving (Generic, ToJSON, FromJSON)
newtype Pong = Pong Int deriving (Generic, ToJSON, FromJSON)
-- Ping server node unique tag.
data PingServerNode = PingServerNode
-- Ping server node config.
data instance NodeConfig PingServerNode = PingServerNodeConfig
{ stopOnPing :: Int
, servingPort :: PortNumber
}
-- Ping server node definition type.
instance Node PingServerNode where
data NodeScenario PingServerNode = PingServer
getNodeScript PingServer = pingServerNode
getNodeTag _ = PingServerNode
-- Handling Ping messages.
acceptPing
:: D.StateVar D.NodeStatus
-> D.StateVar Int
-> Int
-> Ping
-> D.Connection D.Udp
-> L.NodeL ()
acceptPing status pingsCount threshold (Ping clientName) conn = do
pings <- L.atomically $ do
L.modifyVar pingsCount (+1)
L.readVar pingsCount
let done = pings + 1 >= threshold
when done $ do
L.close conn
L.writeVarIO status D.NodeFinished
L.logInfo $ "Pings threshold reached: " +|| threshold ||+ ". Finishing."
unless done $ do
L.send conn (Pong pings)
L.logInfo $ "Ping #" +|| pings ||+ " accepted from " +|| clientName ||+ "."
-- Ping server definition node.
pingServerNode :: NodeConfig PingServerNode -> L.NodeDefinitionL ()
pingServerNode cfg = do
let threshold = _stopOnPing cfg
let port = _servingPort cfg
pingsCount <- L.newVarIO 0
status <- L.newVarIO D.NodeActing
-- Starting a separate process for serving on UDP port.
L.serving D.Udp port $
L.handler $ acceptPing status pingsCount threshold
L.awaitNodeFinished' status
-- Pong client node unique tag.
data PongClientNode = PongClientNode
deriving (Show, Generic)
-- Pong client node config.
data instance NodeConfig PongClientNode = PongClientNodeConfig
{ _clientName :: Text
, _pingDelay :: Int
, _pingServerAddress :: D.Address
}
deriving (Show, Generic)
-- Pong client node definition type.
instance Node PongClientNode where
data NodeScenario PongClientNode = PongClient
deriving (Show, Generic)
getNodeScript _ = pongClientNode'
getNodeTag _ = PongClientNode
-- Accepting pong responses from the server.
acceptPong :: Pong -> connection -> L.NodeL ()
acceptPong (Pong pingsCount) _ =
L.logInfo $ "Pong accepted from server. Pings count: " <> show pingsCount
-- Sending pings to the server.
pingSending :: D.StateVar D.NodeStatus -> NodeConfig PongClientNode -> D.Connection D.Udp -> L.NodeL ()
pingSending status cfg conn = do
L.delay $ _pingDelay cfg
L.logInfo "Sending Ping to the server."
eSent <- L.send conn (Ping $ _clientName cfg)
case eSent of
Right () -> pingSending status cfg conn
Left _ -> do
L.logInfo "Server is gone."
L.close conn
L.writeVarIO status D.NodeFinished
-- Pong client definition node.
pongClientNode :: NodeConfig PongClientNode -> L.NodeDefinitionL ()
pongClientNode cfg = do
status <- L.newVarIO D.NodeActing
-- Connecting to the server.
mbConn <- L.open D.Udp (_pingServerAddress cfg) $
L.handler acceptPong
case mbConn of
Nothing -> L.logError "Ping Server not found"
Just conn -> do
-- Forking separate process of periodical pings.
L.process (pingSending status cfg conn)
-- Waiting when the node is finished.
L.awaitNodeFinished' status
Haskell
100.0%
Comprehensive framework to build network actors and distributed applications
Haskell
44
2,468 commits
updated Mar 14, 2019
Enecuum Node Framework allows to build network actors and blockchain protocols, console applications, work with KV database and cryptography. Current features include:
The Node project contains:
The framework represents a set of embedded monadic languages organized hierarchically. The languages are divided to core languages responsible for common subsystems and framework languages responsible for network and actors behavior.
curl -sSL https://get.haskellstack.org/ | sh
sudo nano ~/.profile and append export PATH=$PATH:$HOME/.local/bin at the end.
sudo apt update && sudo apt install librocksdb-dev libgd-dev libtinfo-dev -y
git clone https://github.com/Enecuum/Node.git && cd Node
stack build
Run all tests:
stack test
Run fast tests:
stack build --test --test-arguments "-m Fast"
Run slow and unreliable tests:
stack build --test --test-arguments "-m Slow"
enq-test-node-haskell is a single executable for sample nodes.
GraphNode Transmitter
stack exec enq-test-node-haskell singlenode ./configs/tst_graph_node_transmitter.json
GraphNode Receiver
stack exec enq-test-node-haskell singlenode ./configs/tst_graph_node_receiver.json
Gen PoW
stack exec enq-test-node-haskell singlenode ./configs/tst_gen_pow.json
Gen PoA
stack exec enq-test-node-haskell singlenode ./configs/tst_gen_poa.json
Console client
stack exec enq-test-node-haskell singlenode ./configs/tst_client.json
In this sample, two nodes interact via network sending UDP messages.
Ping server node
Ping messages.Pons message back to the client.stack exec enq-test-node-haskell singlenode ./configs/tst_ping_server.json
Pong client node
Ping messages to the server periodically.Pong messages from the server.stack exec enq-test-node-haskell singlenode ./configs/tst_pong_client1.json
stack exec enq-test-node-haskell singlenode ./configs/tst_pong_client2.json
-- Messages
newtype Ping = Ping Text deriving (Generic, ToJSON, FromJSON)
newtype Pong = Pong Int deriving (Generic, ToJSON, FromJSON)
-- Ping server node unique tag.
data PingServerNode = PingServerNode
-- Ping server node config.
data instance NodeConfig PingServerNode = PingServerNodeConfig
{ stopOnPing :: Int
, servingPort :: PortNumber
}
-- Ping server node definition type.
instance Node PingServerNode where
data NodeScenario PingServerNode = PingServer
getNodeScript PingServer = pingServerNode
getNodeTag _ = PingServerNode
-- Handling Ping messages.
acceptPing
:: D.StateVar D.NodeStatus
-> D.StateVar Int
-> Int
-> Ping
-> D.Connection D.Udp
-> L.NodeL ()
acceptPing status pingsCount threshold (Ping clientName) conn = do
pings <- L.atomically $ do
L.modifyVar pingsCount (+1)
L.readVar pingsCount
let done = pings + 1 >= threshold
when done $ do
L.close conn
L.writeVarIO status D.NodeFinished
L.logInfo $ "Pings threshold reached: " +|| threshold ||+ ". Finishing."
unless done $ do
L.send conn (Pong pings)
L.logInfo $ "Ping #" +|| pings ||+ " accepted from " +|| clientName ||+ "."
-- Ping server definition node.
pingServerNode :: NodeConfig PingServerNode -> L.NodeDefinitionL ()
pingServerNode cfg = do
let threshold = _stopOnPing cfg
let port = _servingPort cfg
pingsCount <- L.newVarIO 0
status <- L.newVarIO D.NodeActing
-- Starting a separate process for serving on UDP port.
L.serving D.Udp port $
L.handler $ acceptPing status pingsCount threshold
L.awaitNodeFinished' status
-- Pong client node unique tag.
data PongClientNode = PongClientNode
deriving (Show, Generic)
-- Pong client node config.
data instance NodeConfig PongClientNode = PongClientNodeConfig
{ _clientName :: Text
, _pingDelay :: Int
, _pingServerAddress :: D.Address
}
deriving (Show, Generic)
-- Pong client node definition type.
instance Node PongClientNode where
data NodeScenario PongClientNode = PongClient
deriving (Show, Generic)
getNodeScript _ = pongClientNode'
getNodeTag _ = PongClientNode
-- Accepting pong responses from the server.
acceptPong :: Pong -> connection -> L.NodeL ()
acceptPong (Pong pingsCount) _ =
L.logInfo $ "Pong accepted from server. Pings count: " <> show pingsCount
-- Sending pings to the server.
pingSending :: D.StateVar D.NodeStatus -> NodeConfig PongClientNode -> D.Connection D.Udp -> L.NodeL ()
pingSending status cfg conn = do
L.delay $ _pingDelay cfg
L.logInfo "Sending Ping to the server."
eSent <- L.send conn (Ping $ _clientName cfg)
case eSent of
Right () -> pingSending status cfg conn
Left _ -> do
L.logInfo "Server is gone."
L.close conn
L.writeVarIO status D.NodeFinished
-- Pong client definition node.
pongClientNode :: NodeConfig PongClientNode -> L.NodeDefinitionL ()
pongClientNode cfg = do
status <- L.newVarIO D.NodeActing
-- Connecting to the server.
mbConn <- L.open D.Udp (_pingServerAddress cfg) $
L.handler acceptPong
case mbConn of
Nothing -> L.logError "Ping Server not found"
Just conn -> do
-- Forking separate process of periodical pings.
L.process (pingSending status cfg conn)
-- Waiting when the node is finished.
L.awaitNodeFinished' status
Haskell
100.0%