Integrate zcashd-based zaddr validation.
[?]
Sep 15, 2020, 1:47 PM
5IDB3IWSB6LFW4U772Y7BH5Y3FQOQ7IFWLVXDZE5XS6SKJITFV4QCDependencies
- [2]
U256ZALIAdd captcha check to register route. - [3]
I2KHGVD4Require project permissions for access to most data. - [4]
O2BZOX7MAdd signup form, captcha check. - [5]
2XQD6KKKAdd invitation logic and clean up DBProg error handling. - [6]
BXGLKYRXAdded primitive user registration handler. - [7]
IPG33FAWAdd billing daemon - [8]
4U7F3CPITHE GREAT RENAMING OF THINGS! - [9]
IZEVQF62Work in progress replacing sqlite with postgres. - [10]
HMDM3B55Implement core of payments/billing infrastructure. - [11]
KEP5WUFJConvert project to stack-based build. - [12]
NEDDHXUKReformat via stylish-haskell - [13]
GCVQD44VCreate amends endpoint, switch to UUID primary keys - [14]
HALRDT2FAdded initial auction create route. - [15]
LTSVBVA2Update to a recent haskoin-core. Fix Stack build. - [16]
O722AOKEAdd route to allow crediting of events to users/projects. - [17]
EFSXYZPOAutoformat everything with brittany. - [18]
BROSTG5KBeginning of modularization of server. - [19]
PT4276XCAdd logout functionality. - [20]
UWMGUJOWAutoformat sources. - [21]
ASF3UPJLAdd auction creation and bid handlers - [22]
B6HWAPDPModularize & update to recent haskoin. - [*]
64C6AWH6Rename Ananke -> Quixotic, project reboot. - [*]
UUR6SMCAAdd start of specs for auctions. - [*]
QMRKFEPGRefactor QDB to use a free monad algebra instead. - [*]
V2VDN77HEnable postgres configuration via environment variable for Heroku. - [*]
6L5BK5EHUse generic SMTP rather than Sendmail-specific mail client. - [*]
MB5SHULBAdd route for accepting an invitation with an existing account - [*]
ADMKQQGCInitial empty Snap project. - [*]
W35DDBFYFactor common JSON conversions up into client lib module. - [*]
RFYEVKZQAdd nix-shell based build environment.
Change contents
- replacement in aftok.cabal at line 35
Aftok.Currency.ZCashAftok.Currency.Zcash - edit in aftok.cabal at line 47
Aftok.Users - edit in aftok.cabal at line 71
, http-client, http-types - replacement in client/src/Aftok/Api/Account.purs at line 107
let signupJSON =let signupJSON = encodeJson $ - edit in client/src/Aftok/Api/Account.purs at line 119
, captchaToken: req.captchaToken - edit in client/src/Aftok/Api/Account.purs at line 121
result <- post RF.ignore "/api/register" (Just <<< RB.Json $ signupJson)case result ofLeft err -> log ("Registration failed: " <> printError err)Right r -> log ("Registration status: " <> show r.status) - file deletion: ZCash.hs
{-# LANGUAGE TemplateHaskell #-}module Aftok.Currency.ZCash whereimport Control.Lens ( makePrisms)newtype ZAddr = ZAddr { zaddrText :: Text }deriving (Eq, Ord, Show)makePrisms ''ZAddr - file addition: Zcash.hs[3.4250]
{-# LANGUAGE TemplateHaskell #-}module Aftok.Currency.Zcash( ZAddr(..), _ZAddr, ZAddrError(..), ZcashdConfig(..), rpcValidateZAddr) whereimport Control.Lens ( makePrisms )import qualified Data.Aeson as Aimport Data.Aeson ( Value, (.=), (.:), (.:?), object, encode )import Data.Aeson.Types ( Parser )import qualified Data.Text.Encoding as Timport Network.HTTP.Client ( Manager, RequestBody(..), defaultRequest, responseBody, responseStatus, httpLbs, host, port, method, requestBody)import Network.HTTP.Types ( Status, statusCode )newtype ZAddr = ZAddr { zaddrText :: Text }deriving (Eq, Ord, Show)makePrisms ''ZAddrdata ZAddrType= Sprout| Saplingdata ZcashdConfig = ZcashdConfig{ zcashdHost :: Text, zcashdPort :: Int}data ZAddrError= ServiceError Status| ParseError String| ZAddrInvalid| SproutAddress| DataMissingvalidateZAddrRequest :: Text -> ValuevalidateZAddrRequest addr = object[ "jsonrpc" .= ("1.0" :: Text), "id" .= ("aftok-z_validateaddress" :: Text), "method" .= ("z_validateaddress" :: Text), "params" .= [addr]]data ValidateZAddrResponse = ValidateZAddrResponse{ isValid :: Bool, _address :: Maybe Text, addrType :: Maybe ZAddrType}instance A.FromJSON ValidateZAddrResponse whereparseJSON = parseValidateZAddrResponseparseAddrType :: Text -> Maybe ZAddrTypeparseAddrType = \case"sprout" -> Just Sprout"sapling" -> Just Sapling_ -> NothingparseValidateZAddrResponse :: Value -> Parser ValidateZAddrResponseparseValidateZAddrResponse = \case(A.Object v) ->ValidateZAddrResponse <$> v .: "isvalid"<*> v .:? "address"<*> ((traverse (maybe (fail "Not a recognized zaddr type") pure) . fmap parseAddrType) =<< v .:? "type")_ ->fail "ZAddr validation response body was not a valid JSON object"rpcValidateZAddr :: Manager -> ZcashdConfig -> Text -> IO (Either ZAddrError ZAddr)rpcValidateZAddr mgr cfg addr = dolet req = defaultRequest { host = T.encodeUtf8 $ zcashdHost cfg, port = zcashdPort cfg, method = "POST", requestBody = RequestBodyLBS $ encode (validateZAddrRequest addr)}response <- httpLbs req mgrlet status = responseStatus responsepure $ case statusCode status of200 ->case A.eitherDecode (responseBody response) ofLeft err -> Left (ParseError err)Right resp ->if isValid respthencase addrType resp ofJust Sprout -> Left SproutAddressJust Sapling -> Right (ZAddr addr)_ -> Left DataMissingelseLeft ZAddrInvalid_ ->Left (ServiceError status) - replacement in lib/Aftok/Database/PostgreSQL.hs at line 44
import Aftok.Currency.ZCash (ZAddr(..), _ZAddr)import Aftok.Currency.Zcash (ZAddr(..), _ZAddr) - replacement in lib/Aftok/Types.hs at line 16
import Aftok.Currency.ZCash ( ZAddr )import Aftok.Currency.Zcash ( ZAddr ) - replacement in lib/Aftok/Types.hs at line 28
data AccountRecoverydata AccountRecovery z - replacement in lib/Aftok/Types.hs at line 30
| RecoverByZAddr ZAddr| RecoverByZAddr z - replacement in lib/Aftok/Types.hs at line 35
, _userAccountRecovery :: !AccountRecovery, _userAccountRecovery :: !(AccountRecovery ZAddr) - file addition: Users.hs[3.679]
module Aftok.Users( RegisterOps(..), RegisterError(..))whereimport Aftok.Types (Email(..))import Aftok.Currency.Zcash (ZAddr, ZAddrError)data RegisterError= ZAddrParseError ZAddrErrordata RegisterOps m = RegisterOps{ parseZAddr :: Text -> m (Either RegisterError ZAddr), sendConfirmationEmail :: Email -> m ()} - edit in server/Aftok/QConfig.hs at line 24
import Aftok.Currency.Zcash (ZcashdConfig(..)) - edit in server/Aftok/QConfig.hs at line 39
, _zcashdConfig :: ZcashdConfig - edit in server/Aftok/QConfig.hs at line 69
<*> (readZcashdConfig $ C.subconfig "zcashd" cfg)readZcashdConfig :: CT.Config -> IO ZcashdConfigreadZcashdConfig cfg =ZcashdConfig <$> C.require cfg "zcashdHost"<*> C.require cfg "zcashdPort" - edit in server/Aftok/Snaplet/Users.hs at line 11
, RegisterOps(..) - replacement in server/Aftok/Snaplet/Users.hs at line 40[3.10432]→[2.5197:5259](∅→∅),[2.5259]→[3.8814:8846](∅→∅),[3.34920]→[3.8814:8846](∅→∅),[3.55897]→[3.8814:8846](∅→∅),[3.8814]→[3.8814:8846](∅→∅),[3.8846]→[3.4056:4087](∅→∅),[3.4087]→[2.5260:5289](∅→∅)
import Aftok.Currency.ZCash ( ZAddr(..) )import Aftok.Databaseimport Aftok.Projectimport Aftok.Typesimport Aftok.Database ( createUser, acceptInvitation )import Aftok.Project ( InvitationCode, parseInvCode )import Aftok.Users ( RegisterOps(..) )import Aftok.Types ( UserId, User(..), AccountRecovery(..), Email(..), UserName(..), _UserName) - edit in server/Aftok/Snaplet/Users.hs at line 55
data RegUser = RegUser{ _username :: !UserName, _userAccountRecovery :: !(AccountRecovery Text)}makeLenses ''RegUser - replacement in server/Aftok/Snaplet/Users.hs at line 63
{ _cuser :: User{ _regUser :: RegUser - replacement in server/Aftok/Snaplet/Users.hs at line 75
"zaddr" -> RecoverByZAddr . ZAddr <$> v .: "zaddr""zaddr" -> RecoverByZAddr <$> v .: "zaddr" - replacement in server/Aftok/Snaplet/Users.hs at line 77
user <- User <$> (UserName <$> v .: "username")<*> pure recoveryuser <- RegUser <$> (UserName <$> v .: "username")<*> pure recovery - replacement in server/Aftok/Snaplet/Users.hs at line 81
<$> (fromString <$> v .: "password")<*> (v .: "captchaToken")<*> (parseInvitationCodes =<< v .: "invitation_codes")<$> (fromString <$> v .: "password")<*> (v .: "captchaToken")<*> (parseInvitationCodes =<< v .: "invitation_codes") - replacement in server/Aftok/Snaplet/Users.hs at line 92
registerHandler :: CaptchaConfig -> S.Handler App App UserIdregisterHandler cfg = doregisterHandler :: RegisterOps IO -> CaptchaConfig -> S.Handler App App UserIdregisterHandler ops cfg = do - replacement in server/Aftok/Snaplet/Users.hs at line 97
void . either (const . throwDenied $ AU.AuthError "Captcha check failed, please try again.") pure $ captchaResultlet captchaFailed = throwDenied $ AU.AuthError "Captcha check failed, please try again."void . either (const captchaFailed) pure $ captchaResult - replacement in server/Aftok/Snaplet/Users.hs at line 100
now <- liftIO C.getCurrentTimeacctRecovery <- case (userData ^. regUser . userAccountRecovery) ofRecoverByEmail e -> doliftIO $ sendConfirmationEmail ops epure $ RecoverByEmail eRecoverByZAddr z -> dozaddrValid <- liftIO $ parseZAddr ops zcase zaddrValid ofLeft _ -> snapError 400 "The Z-Address provided for account recovery was invalid."Right r -> pure $ RecoverByZAddr rnow <- liftIO C.getCurrentTime - replacement in server/Aftok/Snaplet/Users.hs at line 112
createSUser = AU.createUser (userData ^. (cuser . username . _UserName))(userData ^. password)uname = userData ^. (regUser . username)createSUser = AU.createUser (uname ^. _UserName) (userData ^. password) - replacement in server/Aftok/Snaplet/Users.hs at line 115
userId <- createUser (userData ^. cuser)userId <- createUser $ User uname acctRecovery - edit in server/Aftok/Snaplet/Users.hs at line 121
- edit in server/Main.hs at line 15
import Network.HTTP.Client ( Manager, newManager, defaultManagerSettings ) - edit in server/Main.hs at line 19
import Aftok.Currency.Zcash ( rpcValidateZAddr ) - edit in server/Main.hs at line 22
import Aftok.Users ( RegisterError(..) ) - edit in server/Main.hs at line 50
registerOps :: Manager -> QConfig -> RegisterOps IOregisterOps mgr cfg = RegisterOps{ parseZAddr =(pure . first ZAddrParseError)<=< rpcValidateZAddr mgr (_zcashdConfig cfg), sendConfirmationEmail = const $ pure ()} - edit in server/Main.hs at line 60
mgr <- liftIO $ newManager defaultManagerSettings - edit in server/Main.hs at line 62
rops = registerOps mgr cfg - replacement in server/Main.hs at line 80
registerRoute = void $ method POST (registerHandler $ cfg ^. recaptchaSecret)registerRoute = void $ method POST (registerHandler rops (cfg ^. recaptchaSecret)) - edit in shell.nix at line 25[32.99189][32.99189]
haskellPackages.ormolu