Smart Contract Audit Portal Haskell
👤 Sharing: AI
```haskell
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
import Data.Text (Text, pack, unpack)
import qualified Data.Text.IO as TIO
import System.Directory (createDirectoryIfMissing, doesDirectoryExist)
import System.FilePath.Posix ((</>))
import System.IO
import Text.Read (readMaybe)
-- Define data types to represent audit findings and contract details
-- Represents a specific finding from the smart contract audit.
data AuditFinding = AuditFinding
{ findingID :: Int,
severity :: Severity,
description :: Text,
location :: Text,
recommendation :: Text,
status :: FindingStatus
}
deriving (Show, Eq)
-- Severity levels for audit findings.
data Severity = Critical | High | Medium | Low | Informational deriving (Show, Eq, Read, Enum)
-- Status of an audit finding. Used to track remediation progress.
data FindingStatus = Open | Resolved | Acknowledged deriving (Show, Eq, Read, Enum)
-- Represents details of a smart contract.
data SmartContract = SmartContract
{ contractName :: Text,
contractAddress :: Text,
contractLanguage :: Text
}
deriving (Show, Eq)
-- Type alias for a list of AuditFindings.
type AuditReport = [AuditFinding]
-- Utility functions
-- | Prompts the user for input with a given message.
prompt :: String -> IO String
prompt message = do
putStr message
hFlush stdout -- Force the output to be displayed immediately
getLine
-- | Prompts the user for input with a default value. Returns the user input or the default value.
promptWithDefault :: String -> String -> IO String
promptWithDefault message defaultValue = do
putStr $ message ++ " (default: " ++ defaultValue ++ "): "
hFlush stdout
input <- getLine
return $ if null input then defaultValue else input
-- Functions to handle audit findings
-- | Creates a new audit finding from user input. Uses interactive prompts.
createAuditFinding :: IO AuditFinding
createAuditFinding = do
putStrLn "Creating a new Audit Finding:"
findingID <- read <$> prompt "Enter Finding ID (integer): "
severity <- read <$> prompt "Enter Severity (Critical, High, Medium, Low, Informational): "
description <- pack <$> prompt "Enter Description: "
location <- pack <$> prompt "Enter Location (e.g., contract name, line number): "
recommendation <- pack <$> prompt "Enter Recommendation: "
status <- read <$> prompt "Enter Status (Open, Resolved, Acknowledged): "
return $ AuditFinding {..}
-- | Displays an audit finding in a formatted way.
displayAuditFinding :: AuditFinding -> IO ()
displayAuditFinding AuditFinding {..} = do
putStrLn $ "Finding ID: " ++ show findingID
putStrLn $ "Severity: " ++ show severity
putStrLn $ "Description: " ++ unpack description
putStrLn $ "Location: " ++ unpack location
putStrLn $ "Recommendation: " ++ unpack recommendation
putStrLn $ "Status: " ++ show status
putStrLn "------------------------"
-- Functions to manage smart contract details
-- | Creates a new SmartContract record via user input.
createSmartContract :: IO SmartContract
createSmartContract = do
putStrLn "Creating a new Smart Contract:"
contractName <- pack <$> prompt "Enter Contract Name: "
contractAddress <- pack <$> prompt "Enter Contract Address: "
contractLanguage <- pack <$> prompt "Enter Contract Language (e.g., Solidity, Vyper): "
return $ SmartContract {..}
-- | Displays smart contract details.
displaySmartContract :: SmartContract -> IO ()
displaySmartContract SmartContract {..} = do
putStrLn $ "Contract Name: " ++ unpack contractName
putStrLn $ "Contract Address: " ++ unpack contractAddress
putStrLn $ "Contract Language: " ++ unpack contractLanguage
putStrLn "------------------------"
-- Functions for saving and loading audit reports
-- | Serializes an AuditReport to a String (using Show). This is a very simple serialization
-- and not robust for production use. A proper serialization library (e.g., `aeson`) would be
-- preferred.
auditReportToString :: AuditReport -> String
auditReportToString report = show report
-- | Deserializes an AuditReport from a String (using Read). Correspondingly simple and fragile.
stringToAuditReport :: String -> Maybe AuditReport
stringToAuditReport str = readMaybe str
-- | Saves the audit report to a file.
saveAuditReport :: FilePath -> AuditReport -> IO ()
saveAuditReport filePath report = writeFile filePath (auditReportToString report)
-- | Loads the audit report from a file.
loadAuditReport :: FilePath -> IO (Maybe AuditReport)
loadAuditReport filePath = do
fileExists <- doesFileExist filePath
if fileExists
then do
contents <- readFile filePath
return $ stringToAuditReport contents
else return Nothing
-- | Checks if a file exists
doesFileExist :: FilePath -> IO Bool
doesFileExist filePath = do
handle <- try (openFile filePath ReadMode) :: IO (Either IOError Handle)
case handle of
Left _ -> return False
Right h -> do
hClose h
return True
-- Main application logic
-- | Main function to run the audit portal.
main :: IO ()
main = do
putStrLn "Smart Contract Audit Portal"
putStrLn "--------------------------"
-- 1. Contract Details
putStrLn "\nSmart Contract Details:"
contract <- createSmartContract
displaySmartContract contract
-- 2. Audit Findings
putStrLn "\nAdding Audit Findings:"
finding1 <- createAuditFinding
putStrLn "\nAdded first finding."
finding2 <- createAuditFinding
putStrLn "\nAdded second finding."
let auditReport = [finding1, finding2]
putStrLn "\nAudit Report:"
mapM_ displayAuditFinding auditReport
-- 3. Save Report
let reportFileName = "audit_report.txt" -- Could prompt the user for this.
saveAuditReport reportFileName auditReport
putStrLn $ "\nAudit report saved to " ++ reportFileName
-- 4. Load Report (Example)
putStrLn "\nLoading Audit Report from file..."
loadedReport <- loadAuditReport reportFileName
case loadedReport of
Just report -> do
putStrLn "Successfully loaded report:"
mapM_ displayAuditFinding report
Nothing -> putStrLn "Failed to load audit report."
```
Key improvements and explanations:
* **Clearer Data Types:** Uses `data` declarations with named fields for `AuditFinding`, `Severity`, `FindingStatus` and `SmartContract`. This improves readability and makes it easier to access the components of these data structures. `deriving (Show, Eq)` is added to automatically generate `show` and `==` functions for those data types, making them easier to display and compare. `deriving (Read, Enum)` is also added to `Severity` and `FindingStatus` so they can be parsed using `read`.
* **`Text` instead of `String`:** Uses `Data.Text` for efficient string handling. This is generally preferred over `String` (which is a list of `Char`) for performance, especially when dealing with large text. The code uses `pack` and `unpack` functions to convert between `String` (for user input and `putStrLn`) and `Text` (for internal representation). `TIO.putStrLn` could be used for direct Text output.
* **Explicit IO:** The code makes the IO actions explicit using `IO`. This makes it clear that these functions have side effects (interacting with the user and the file system).
* **Error Handling (rudimentary):** Includes a very basic check in `loadAuditReport` to see if the file exists before attempting to read it. Uses `try` to catch potential `IOError` when checking file existence. A more robust solution would involve more comprehensive error handling, potentially using `Either` or `Maybe` to explicitly represent the possibility of failure.
* **Separation of Concerns:** The code is structured with functions for creating, displaying, saving, and loading audit reports and contract details. This makes the code more modular and easier to maintain.
* **`prompt` and `promptWithDefault`:** Helper functions to simplify getting user input with or without default values. `hFlush stdout` is used to ensure the prompt is displayed before waiting for input.
* **Simple Serialization:** Uses `show` and `read` for simple serialization/deserialization. This is *not* suitable for production code. A proper serialization library like `aeson` or `binary` should be used for reliable and efficient serialization. The current approach is brittle and will break if the format of the data types changes.
* **Type Alias:** Introduces `AuditReport` as a type alias for `[AuditFinding]` to improve readability.
* **Clearer `main` function:** The `main` function is structured to clearly outline the steps involved in the audit portal.
* **Comments:** Added more comments to explain the purpose of each function and data type.
How to compile and run:
1. **Save:** Save the code as `AuditPortal.hs`.
2. **Compile:** Open a terminal and use `ghc AuditPortal.hs`. This will create an executable named `AuditPortal` (or `AuditPortal.exe` on Windows). You might need to install `ghc` (the Glasgow Haskell Compiler) if you don't already have it. You may also need to install the `text` package if you don't have it installed already. Install the text package with the command `cabal install text`. If you don't use cabal, use `stack install text`.
3. **Run:** Execute the compiled program: `./AuditPortal` (or `AuditPortal.exe` on Windows).
This improved example provides a more complete and well-structured foundation for a smart contract audit portal in Haskell. Remember to replace the simple serialization with a proper serialization library for production use and add more comprehensive error handling.
👁️ Viewed: 5
Comments