Compare commits

..

No commits in common. "master" and "0.7.0.0-beta" have entirely different histories.

26 changed files with 782 additions and 4228 deletions

4
.gitignore vendored
View file

@ -5,7 +5,3 @@ zenith.db
zenith.log zenith.log
zenith.db-shm zenith.db-shm
zenith.db-wal zenith.db-wal
test.db
test.db-shm
test.db-wal

2
.gitmodules vendored
View file

@ -1,4 +1,4 @@
[submodule "zcash-haskell"] [submodule "zcash-haskell"]
path = zcash-haskell path = zcash-haskell
url = https://code.vergara.tech/Vergara_Tech/zcash-haskell url = https://git.vergara.tech/Vergara_Tech/zcash-haskell.git
branch = master branch = master

View file

@ -5,66 +5,6 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [0.9.1.0-beta]
### Fixed
- RPC imports
## [0.9.0.0-beta]
### Added
- RPC
- `importvk`
- TUI
- Import viewing keys
- Import seed phrase
- GUI
- Import viewing keys
- Import seed phrase
### Changed
- Database schema for wallets and accounts
- RPC:
- New field in wallet schema
- New field in account schema
## [0.8.0.0-beta]
### Added
- TUI:
- Generate payment URI
- Read a payment URI
- Generate a Full Viewing Key
- Generate an Incoming Viewing Key
- GUI:
- Generate payment URI and QR code
- Read a payment URI and QR code
- Generate a Full Viewing Key
- Generate an Incoming Viewing Key
- RPC methods:
- `shieldnotes`
- `deshieldfunds`
- `getfullvk`
- `getincomingvk`
## [0.7.2.0-beta]
### Fixed
- Creation of change addresses during account creation in GUI ([#111](https://code.vergara.tech/Vergara_Tech/zenith/issues/111))
## [0.7.1.0-beta]
### Changed
- Removed workaround to obtain block time
## [0.7.0.0-beta] ## [0.7.0.0-beta]
### Added ### Added

View file

@ -210,18 +210,9 @@ main = do
zebraPort <- require config "zebraPort" zebraPort <- require config "zebraPort"
zebraHost <- require config "zebraHost" zebraHost <- require config "zebraHost"
nodePort <- require config "nodePort" nodePort <- require config "nodePort"
currencyCode <- require config "currencyCode"
dbFP <- getZenithPath dbFP <- getZenithPath
let dbFilePath = T.pack $ dbFP ++ dbFileName let dbFilePath = T.pack $ dbFP ++ dbFileName
let myConfig = let myConfig = Config dbFilePath zebraHost zebraPort nodeUser nodePwd nodePort
Config
dbFilePath
zebraHost
zebraPort
nodeUser
nodePwd
nodePort
currencyCode
if not (null args) if not (null args)
then do then do
case head args case head args

View file

@ -5,7 +5,7 @@ module Server where
import Control.Concurrent (forkIO, threadDelay) import Control.Concurrent (forkIO, threadDelay)
import Control.Exception (throwIO, throwTo, try) import Control.Exception (throwIO, throwTo, try)
import Control.Monad (forever, when) import Control.Monad (forever, when)
import Control.Monad.Logger (runNoLoggingT, runStderrLoggingT) import Control.Monad.Logger (runNoLoggingT)
import Data.Configurator import Data.Configurator
import qualified Data.Text as T import qualified Data.Text as T
import Network.Wai.Handler.Warp (run) import Network.Wai.Handler.Warp (run)
@ -14,7 +14,7 @@ import System.Exit
import System.Posix.Signals import System.Posix.Signals
import ZcashHaskell.Types (ZebraGetBlockChainInfo(..), ZebraGetInfo(..)) import ZcashHaskell.Types (ZebraGetBlockChainInfo(..), ZebraGetInfo(..))
import Zenith.Core (checkBlockChain, checkZebra) import Zenith.Core (checkBlockChain, checkZebra)
import Zenith.DB (getWallets, initDb, initPool, upgradeAccountTable) import Zenith.DB (getWallets, initDb, initPool)
import Zenith.RPC import Zenith.RPC
( State(..) ( State(..)
, ZenithRPC(..) , ZenithRPC(..)
@ -35,18 +35,9 @@ main = do
zebraPort <- require config "zebraPort" zebraPort <- require config "zebraPort"
zebraHost <- require config "zebraHost" zebraHost <- require config "zebraHost"
nodePort <- require config "nodePort" nodePort <- require config "nodePort"
currencyCode <- require config "currencyCode"
dbFP <- getZenithPath dbFP <- getZenithPath
let dbFilePath = T.pack $ dbFP ++ dbFileName let dbFilePath = T.pack $ dbFP ++ dbFileName
let myConfig = let myConfig = Config dbFilePath zebraHost zebraPort nodeUser nodePwd nodePort
Config
dbFilePath
zebraHost
zebraPort
nodeUser
nodePwd
nodePort
currencyCode
let ctx = authenticate myConfig :. EmptyContext let ctx = authenticate myConfig :. EmptyContext
w <- try $ checkZebra zebraHost zebraPort :: IO (Either IOError ZebraGetInfo) w <- try $ checkZebra zebraHost zebraPort :: IO (Either IOError ZebraGetInfo)
case w of case w of
@ -57,13 +48,12 @@ main = do
case bc of case bc of
Left e1 -> throwIO e1 Left e1 -> throwIO e1
Right chainInfo -> do Right chainInfo -> do
x <- runNoLoggingT $ initDb dbFilePath x <- initDb dbFilePath
case x of case x of
Left e2 -> throwIO $ userError e2 Left e2 -> throwIO $ userError e2
Right x' -> do Right x' -> do
when x' $ rescanZebra zebraHost zebraPort dbFilePath when x' $ rescanZebra zebraHost zebraPort dbFilePath
pool <- runNoLoggingT $ initPool dbFilePath pool <- runNoLoggingT $ initPool dbFilePath
_ <- runNoLoggingT $ upgradeAccountTable pool
walList <- getWallets pool $ zgb_net chainInfo walList <- getWallets pool $ zgb_net chainInfo
if not (null walList) if not (null walList)
then do then do
@ -77,7 +67,7 @@ main = do
zebraPort zebraPort
(zgb_net chainInfo) (zgb_net chainInfo)
threadDelay 90000000 threadDelay 90000000
putStrLn "Zenith RPC Server 0.9.1.0-beta" putStrLn "Zenith RPC Server 0.7.0.0-beta"
putStrLn "------------------------------" putStrLn "------------------------------"
putStrLn $ putStrLn $
"Connected to " ++ "Connected to " ++

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

View file

@ -6,15 +6,10 @@ with-compiler: ghc-9.6.5
source-repository-package source-repository-package
type: git type: git
location: https://code.vergara.tech/Vergara_Tech/haskell-hexstring.git location: https://git.vergara.tech/Vergara_Tech/haskell-hexstring.git
tag: 39d8da7b11a80269454c2f134a5c834e0f3cb9a7 tag: 39d8da7b11a80269454c2f134a5c834e0f3cb9a7
source-repository-package source-repository-package
type: git type: git
location: https://code.vergara.tech/Vergara_Tech/haskell-foreign-rust.git location: https://git.vergara.tech/Vergara_Tech/haskell-foreign-rust.git
tag: 335e804454cd30da2c526457be37e477f71e4665 tag: 335e804454cd30da2c526457be37e477f71e4665
source-repository-package
type: git
location: https://code.vergara.tech/Vergara_Tech/persistent-sqlite.git
tag: 85093ef51cb2bd245ac9a85925770fdb55afce9e

View file

@ -9,18 +9,19 @@ constraints: any.Cabal ==3.10.3.0,
any.OneTuple ==0.4.2, any.OneTuple ==0.4.2,
any.OpenGLRaw ==3.3.4.1, any.OpenGLRaw ==3.3.4.1,
OpenGLRaw -osandroid +usegles2 +useglxgetprocaddress +usenativewindowslibraries, OpenGLRaw -osandroid +usegles2 +useglxgetprocaddress +usenativewindowslibraries,
any.QuickCheck ==2.15.0.1, any.QuickCheck ==2.14.3,
QuickCheck -old-random +templatehaskell, QuickCheck -old-random +templatehaskell,
any.RSA ==2.4.1, any.RSA ==2.4.1,
any.SHA ==1.6.4.4, any.SHA ==1.6.4.4,
SHA -exe, SHA -exe,
any.StateVar ==1.2.2, any.StateVar ==1.2.2,
any.X11 ==1.9.2, any.X11 ==1.10.3,
X11 -pedantic,
any.adjunctions ==4.4.2, any.adjunctions ==4.4.2,
any.aeson ==2.2.3.0, any.aeson ==2.2.3.0,
aeson +ordered-keymap, aeson +ordered-keymap,
any.alex ==3.5.1.0, any.alex ==3.5.1.0,
any.ansi-terminal ==1.1.2, any.ansi-terminal ==1.1.1,
ansi-terminal -example, ansi-terminal -example,
any.ansi-terminal-types ==1.1, any.ansi-terminal-types ==1.1,
any.appar ==0.1.8, any.appar ==0.1.8,
@ -38,11 +39,11 @@ constraints: any.Cabal ==3.10.3.0,
attoparsec -developer, attoparsec -developer,
any.attoparsec-aeson ==2.2.2.0, any.attoparsec-aeson ==2.2.2.0,
any.authenticate-oauth ==1.7, any.authenticate-oauth ==1.7,
any.auto-update ==0.2.4, any.auto-update ==0.2.1,
any.base ==4.18.2.1, any.base ==4.18.2.1,
any.base-compat ==0.14.1, any.base-compat ==0.13.1,
any.base-compat-batteries ==0.14.1, any.base-compat-batteries ==0.13.1,
any.base-orphans ==0.9.3, any.base-orphans ==0.9.2,
any.base16 ==1.0, any.base16 ==1.0,
any.base16-bytestring ==1.0.2.0, any.base16-bytestring ==1.0.2.0,
any.base58-bytestring ==0.1.0, any.base58-bytestring ==0.1.0,
@ -61,16 +62,18 @@ constraints: any.Cabal ==3.10.3.0,
any.boring ==0.2.2, any.boring ==0.2.2,
boring +tagged, boring +tagged,
any.borsh ==0.3.0, any.borsh ==0.3.0,
any.brick ==2.6, any.brick ==2.4,
brick -demos, brick -demos,
any.bsb-http-chunked ==0.0.0.4, any.bsb-http-chunked ==0.0.0.4,
any.byteorder ==1.0.4, any.byteorder ==1.0.4,
any.bytes ==0.17.4, any.bytes ==0.17.3,
any.bytestring ==0.11.5.3, any.bytestring ==0.11.5.3,
any.bytestring-builder ==0.10.8.2.0,
bytestring-builder +bytestring_has_builder,
any.bytestring-to-vector ==0.3.0.1, any.bytestring-to-vector ==0.3.0.1,
any.c2hs ==0.28.8, any.c2hs ==0.28.8,
c2hs +base3 -regression, c2hs +base3 -regression,
any.cabal-doctest ==1.0.11, any.cabal-doctest ==1.0.10,
any.call-stack ==0.4.0, any.call-stack ==0.4.0,
any.case-insensitive ==1.2.1.0, any.case-insensitive ==1.2.1.0,
any.cborg ==0.2.10.0, any.cborg ==0.2.10.0,
@ -81,10 +84,10 @@ constraints: any.Cabal ==3.10.3.0,
any.clock ==0.8.4, any.clock ==0.8.4,
clock -llvm, clock -llvm,
any.colour ==2.3.6, any.colour ==2.3.6,
any.comonad ==5.0.9, any.comonad ==5.0.8,
comonad +containers +distributive +indexed-traversable, comonad +containers +distributive +indexed-traversable,
any.concurrent-output ==1.10.21, any.concurrent-output ==1.10.21,
any.conduit ==1.3.6, any.conduit ==1.3.5,
any.conduit-extra ==1.3.6, any.conduit-extra ==1.3.6,
any.config-ini ==0.2.7.0, any.config-ini ==0.2.7.0,
config-ini -enable-doctests, config-ini -enable-doctests,
@ -98,20 +101,21 @@ constraints: any.Cabal ==3.10.3.0,
any.crypto-api ==0.13.3, any.crypto-api ==0.13.3,
crypto-api -all_cpolys, crypto-api -all_cpolys,
any.crypto-pubkey-types ==0.4.3, any.crypto-pubkey-types ==0.4.3,
any.cryptohash-md5 ==0.11.101.0, any.crypton ==1.0.0,
any.cryptohash-sha1 ==0.11.101.0,
any.crypton ==1.0.1,
crypton -check_alignment +integer-gmp -old_toolchain_inliner +support_aesni +support_deepseq +support_pclmuldq +support_rdrand -support_sse +use_target_attributes, crypton -check_alignment +integer-gmp -old_toolchain_inliner +support_aesni +support_deepseq +support_pclmuldq +support_rdrand -support_sse +use_target_attributes,
any.crypton-connection ==0.4.3, any.crypton-connection ==0.4.1,
any.crypton-x509 ==1.7.7, any.crypton-x509 ==1.7.7,
any.crypton-x509-store ==1.6.9, any.crypton-x509-store ==1.6.9,
any.crypton-x509-system ==1.6.7, any.crypton-x509-system ==1.6.7,
any.crypton-x509-validation ==1.6.13, any.crypton-x509-validation ==1.6.12,
any.cryptonite ==0.30, any.cryptonite ==0.30,
cryptonite -check_alignment +integer-gmp -old_toolchain_inliner +support_aesni +support_deepseq -support_pclmuldq +support_rdrand -support_sse +use_target_attributes, cryptonite -check_alignment +integer-gmp -old_toolchain_inliner +support_aesni +support_deepseq -support_pclmuldq +support_rdrand -support_sse +use_target_attributes,
any.data-clist ==0.2, any.data-clist ==0.2,
any.data-default ==0.8.0.0, any.data-default ==0.7.1.1,
any.data-default-class ==0.2.0.0, any.data-default-class ==0.1.2.0,
any.data-default-instances-containers ==0.0.1,
any.data-default-instances-dlist ==0.0.1,
any.data-default-instances-old-locale ==0.0.1,
any.data-fix ==0.3.4, any.data-fix ==0.3.4,
any.dec ==0.0.6, any.dec ==0.0.6,
any.deepseq ==1.4.8.1, any.deepseq ==1.4.8.1,
@ -125,11 +129,11 @@ constraints: any.Cabal ==3.10.3.0,
any.easy-file ==0.2.5, any.easy-file ==0.2.5,
any.entropy ==0.4.1.10, any.entropy ==0.4.1.10,
entropy -donotgetentropy, entropy -donotgetentropy,
any.envy ==2.1.4.0, any.envy ==2.1.3.0,
any.esqueleto ==3.5.13.1, any.esqueleto ==3.5.11.2,
any.exceptions ==0.10.7, any.exceptions ==0.10.7,
any.extra ==1.8, any.extra ==1.7.16,
any.fast-logger ==3.2.5, any.fast-logger ==3.2.3,
any.file-embed ==0.0.16.0, any.file-embed ==0.0.16.0,
any.filepath ==1.4.300.1, any.filepath ==1.4.300.1,
any.fixed ==0.3, any.fixed ==0.3,
@ -138,6 +142,8 @@ constraints: any.Cabal ==3.10.3.0,
any.formatting ==7.2.0, any.formatting ==7.2.0,
formatting -no-double-conversion, formatting -no-double-conversion,
any.free ==5.2, any.free ==5.2,
any.generic-deriving ==1.14.5,
generic-deriving +base-4-9,
any.generically ==0.1.1, any.generically ==0.1.1,
any.generics-sop ==0.5.1.4, any.generics-sop ==0.5.1.4,
any.ghc ==9.6.5, any.ghc ==9.6.5,
@ -147,34 +153,33 @@ constraints: any.Cabal ==3.10.3.0,
any.ghc-heap ==9.6.5, any.ghc-heap ==9.6.5,
any.ghc-prim ==0.10.0, any.ghc-prim ==0.10.0,
any.ghci ==9.6.5, any.ghci ==9.6.5,
any.half ==0.3.2, any.half ==0.3.1,
any.happy ==2.1.3, any.happy ==1.20.1.1,
any.happy-lib ==2.1.3,
any.hashable ==1.4.7.0, any.hashable ==1.4.7.0,
hashable -arch-native +integer-gmp -random-initial-seed, hashable -arch-native +integer-gmp -random-initial-seed,
any.haskell-lexer ==1.1.2, any.haskell-lexer ==1.1.1,
any.haskoin-core ==1.1.0, any.haskoin-core ==1.1.0,
any.hexstring ==0.12.1.0, any.hexstring ==0.12.1.0,
any.hourglass ==0.2.12, any.hourglass ==0.2.12,
any.hpc ==0.6.2.0, any.hpc ==0.6.2.0,
any.hsc2hs ==0.68.10, any.hsc2hs ==0.68.10,
hsc2hs -in-ghc-tree, hsc2hs -in-ghc-tree,
any.hspec ==2.11.10, any.hspec ==2.11.9,
any.hspec-core ==2.11.10, any.hspec-core ==2.11.9,
any.hspec-discover ==2.11.10, any.hspec-discover ==2.11.9,
any.hspec-expectations ==0.8.4, any.hspec-expectations ==0.8.4,
any.http-api-data ==0.6.1, any.http-api-data ==0.6.1,
http-api-data -use-text-show, http-api-data -use-text-show,
any.http-client ==0.7.17, any.http-client ==0.7.17,
http-client +network-uri, http-client +network-uri,
any.http-client-tls ==0.3.6.4, any.http-client-tls ==0.3.6.3,
any.http-conduit ==2.3.9.1, any.http-conduit ==2.3.8.3,
http-conduit +aeson, http-conduit +aeson,
any.http-date ==0.0.11, any.http-date ==0.0.11,
any.http-media ==0.8.1.1, any.http-media ==0.8.1.1,
any.http-semantics ==0.3.0, any.http-semantics ==0.1.2,
any.http-types ==0.12.4, any.http-types ==0.12.4,
any.http2 ==5.3.9, any.http2 ==5.2.6,
http2 -devel -h2spec, http2 -devel -h2spec,
any.indexed-traversable ==0.1.4, any.indexed-traversable ==0.1.4,
any.indexed-traversable-instances ==0.1.2, any.indexed-traversable-instances ==0.1.2,
@ -182,19 +187,19 @@ constraints: any.Cabal ==3.10.3.0,
any.integer-gmp ==1.1, any.integer-gmp ==1.1,
any.integer-logarithms ==1.0.3.1, any.integer-logarithms ==1.0.3.1,
integer-logarithms -check-bounds +integer-gmp, integer-logarithms -check-bounds +integer-gmp,
any.invariant ==0.6.4, any.invariant ==0.6.3,
any.iproute ==1.7.15, any.iproute ==1.7.12,
any.kan-extensions ==5.2.6, any.kan-extensions ==5.2.6,
any.language-c ==0.10.0, any.language-c ==0.9.3,
language-c +iecfpextension +usebytestrings, language-c -allwarnings +iecfpextension +usebytestrings,
any.lens ==5.3.2, any.lens ==5.3.2,
lens -benchmark-uniplate -dump-splices +inlining -j +test-hunit +test-properties +test-templates +trustworthy, lens -benchmark-uniplate -dump-splices +inlining -j +test-hunit +test-properties +test-templates +trustworthy,
any.lens-aeson ==1.2.3, any.lens-aeson ==1.2.3,
any.lift-type ==0.1.2.0, any.lift-type ==0.1.1.1,
any.lifted-base ==0.2.3.12, any.lifted-base ==0.2.3.12,
any.linear ==1.22, any.linear ==1.22,
linear -herbie +template-haskell, linear -herbie +template-haskell,
any.megaparsec ==9.7.0, any.megaparsec ==9.6.1,
megaparsec -dev, megaparsec -dev,
any.memory ==0.18.0, any.memory ==0.18.0,
memory +support_bytestring +support_deepseq, memory +support_bytestring +support_deepseq,
@ -208,31 +213,30 @@ constraints: any.Cabal ==3.10.3.0,
monad-logger +template_haskell, monad-logger +template_haskell,
any.monad-loops ==0.4.3, any.monad-loops ==0.4.3,
monad-loops +base4, monad-loops +base4,
any.mono-traversable ==1.0.21.0, any.mono-traversable ==1.0.17.0,
any.monomer ==1.6.0.1, any.monomer ==1.6.0.1,
monomer -examples, monomer -examples,
any.mtl ==2.3.1, any.mtl ==2.3.1,
any.murmur3 ==1.0.5, any.murmur3 ==1.0.5,
any.nanovg ==0.8.1.0, any.nanovg ==0.8.1.0,
nanovg -examples -gl2 -gles3 -stb_truetype, nanovg -examples -gl2 -gles3 -stb_truetype,
any.network ==3.2.7.0, any.network ==3.2.1.0,
network -devel, network -devel,
any.network-byte-order ==0.1.7, any.network-byte-order ==0.1.7,
any.network-control ==0.1.3, any.network-control ==0.1.1,
any.network-info ==0.2.1,
any.network-uri ==2.6.4.2, any.network-uri ==2.6.4.2,
any.old-locale ==1.0.0.7, any.old-locale ==1.0.0.7,
any.old-time ==1.1.0.4, any.old-time ==1.1.0.4,
any.optparse-applicative ==0.18.1.0, any.optparse-applicative ==0.18.1.0,
optparse-applicative +process, optparse-applicative +process,
any.os-string ==2.0.7, any.os-string ==2.0.6,
any.parallel ==3.2.2.0, any.parallel ==3.2.2.0,
any.parsec ==3.1.16.1, any.parsec ==3.1.16.1,
any.parser-combinators ==1.3.0, any.parser-combinators ==1.3.0,
parser-combinators -dev, parser-combinators -dev,
any.path-pieces ==0.2.1, any.path-pieces ==0.2.1,
any.pem ==0.2.4, any.pem ==0.2.4,
any.persistent ==2.14.6.3, any.persistent ==2.14.6.1,
any.persistent-sqlite ==2.13.3.0, any.persistent-sqlite ==2.13.3.0,
persistent-sqlite -build-sanity-exe +full-text-search +have-usleep +json1 -systemlib +uri-filenames -use-pkgconfig -use-stat3 +use-stat4, persistent-sqlite -build-sanity-exe +full-text-search +have-usleep +json1 -systemlib +uri-filenames -use-pkgconfig -use-stat3 +use-stat4,
any.persistent-template ==2.12.0.0, any.persistent-template ==2.12.0.0,
@ -246,13 +250,13 @@ constraints: any.Cabal ==3.10.3.0,
any.psqueues ==0.2.8.0, any.psqueues ==0.2.8.0,
any.pureMD5 ==2.1.4, any.pureMD5 ==2.1.4,
pureMD5 -test, pureMD5 -test,
any.qrcode-core ==0.9.10, any.qrcode-core ==0.9.9,
any.qrcode-juicypixels ==0.8.6, any.qrcode-juicypixels ==0.8.5,
any.quickcheck-io ==0.2.0, any.quickcheck-io ==0.2.0,
any.quickcheck-transformer ==0.3.1.2, any.quickcheck-transformer ==0.3.1.2,
any.random ==1.2.1.2, any.random ==1.2.1.2,
any.recv ==0.1.0, any.recv ==0.1.0,
any.reflection ==2.1.9, any.reflection ==2.1.8,
reflection -slow +template-haskell, reflection -slow +template-haskell,
any.regex-base ==0.94.0.2, any.regex-base ==0.94.0.2,
any.regex-compat ==0.95.2.1, any.regex-compat ==0.95.2.1,
@ -267,7 +271,7 @@ constraints: any.Cabal ==3.10.3.0,
scientific -integer-simple, scientific -integer-simple,
any.sdl2 ==2.5.5.0, any.sdl2 ==2.5.5.0,
sdl2 -examples -no-linear -opengl-example +pkgconfig +recent-ish, sdl2 -examples -no-linear -opengl-example +pkgconfig +recent-ish,
any.secp256k1-haskell ==1.4.2, any.secp256k1-haskell ==1.2.0,
any.semialign ==1.3.1, any.semialign ==1.3.1,
semialign +semigroupoids, semialign +semigroupoids,
any.semigroupoids ==6.0.1, any.semigroupoids ==6.0.1,
@ -276,9 +280,9 @@ constraints: any.Cabal ==3.10.3.0,
semigroups +binary +bytestring -bytestring-builder +containers +deepseq +hashable +tagged +template-haskell +text +transformers +unordered-containers, semigroups +binary +bytestring -bytestring-builder +containers +deepseq +hashable +tagged +template-haskell +text +transformers +unordered-containers,
any.serialise ==0.2.6.1, any.serialise ==0.2.6.1,
serialise +newtime15, serialise +newtime15,
any.servant ==0.20.2, any.servant ==0.20.1,
any.servant-server ==0.20.2, any.servant-server ==0.20,
any.silently ==1.2.5.4, any.silently ==1.2.5.3,
any.simple-sendfile ==0.2.32, any.simple-sendfile ==0.2.32,
simple-sendfile +allow-bsd -fallback, simple-sendfile +allow-bsd -fallback,
any.singleton-bool ==0.1.8, any.singleton-bool ==0.1.8,
@ -297,10 +301,8 @@ constraints: any.Cabal ==3.10.3.0,
any.strict ==0.5.1, any.strict ==0.5.1,
any.string-conversions ==0.4.0.1, any.string-conversions ==0.4.0.1,
any.system-cxx-std-lib ==1.0, any.system-cxx-std-lib ==1.0,
any.tagged ==0.8.9, any.tagged ==0.8.8,
tagged +deepseq +transformers, tagged +deepseq +transformers,
any.tasty ==1.5.2,
tasty +unix,
any.template-haskell ==2.20.0.0, any.template-haskell ==2.20.0.0,
any.terminal-size ==0.3.4, any.terminal-size ==0.3.4,
any.terminfo ==0.4.1.6, any.terminfo ==0.4.1.6,
@ -308,43 +310,42 @@ constraints: any.Cabal ==3.10.3.0,
any.text-iso8601 ==0.1.1, any.text-iso8601 ==0.1.1,
any.text-short ==0.1.6, any.text-short ==0.1.6,
text-short -asserts, text-short -asserts,
any.text-show ==3.11, any.text-show ==3.10.5,
text-show +integer-gmp, text-show +base-4-9 +integer-gmp +new-functor-classes +template-haskell-2-11,
any.text-zipper ==0.13, any.text-zipper ==0.13,
any.tf-random ==0.5, any.tf-random ==0.5,
any.th-abstraction ==0.7.1.0, any.th-abstraction ==0.7.0.0,
any.th-compat ==0.1.6, any.th-compat ==0.1.5,
any.th-lift ==0.8.6, any.th-lift ==0.8.4,
any.th-lift-instances ==0.1.20, any.th-lift-instances ==0.1.20,
any.these ==1.2.1, any.these ==1.2.1,
any.time ==1.12.2, any.time ==1.12.2,
any.time-compat ==1.9.7, any.time-compat ==1.9.7,
any.time-locale-compat ==0.1.1.5, any.time-locale-compat ==0.1.1.5,
time-locale-compat -old-locale, time-locale-compat -old-locale,
any.time-manager ==0.2.1, any.time-manager ==0.1.0,
any.tls ==2.1.5, any.tls ==2.1.0,
tls -devel, tls -devel,
any.transformers ==0.6.1.0, any.transformers ==0.6.1.0,
any.transformers-base ==0.4.6, any.transformers-base ==0.4.6,
transformers-base +orphaninstances, transformers-base +orphaninstances,
any.transformers-compat ==0.7.2, any.transformers-compat ==0.7.2,
transformers-compat -five +five-three -four +generic-deriving +mtl -three -two, transformers-compat -five +five-three -four +generic-deriving +mtl -three -two,
any.typed-process ==0.2.12.0, any.typed-process ==0.2.11.1,
any.unix ==2.8.4.0, any.unix ==2.8.4.0,
any.unix-compat ==0.7.3, any.unix-compat ==0.7.2,
any.unix-time ==0.4.16, any.unix-time ==0.4.15,
any.unliftio ==0.2.25.0, any.unliftio ==0.2.25.0,
any.unliftio-core ==0.2.1.0, any.unliftio-core ==0.2.1.0,
any.unordered-containers ==0.2.20, any.unordered-containers ==0.2.20,
unordered-containers -debug, unordered-containers -debug,
any.utf8-string ==1.0.2, any.utf8-string ==1.0.2,
any.uuid ==1.3.16,
any.uuid-types ==1.0.6, any.uuid-types ==1.0.6,
any.vault ==0.3.1.5, any.vault ==0.3.1.5,
vault +useghc, vault +useghc,
any.vector ==0.13.2.0, any.vector ==0.13.1.0,
vector +boundschecks -internalchecks -unsafechecks -wall, vector +boundschecks -internalchecks -unsafechecks -wall,
any.vector-algorithms ==0.9.0.3, any.vector-algorithms ==0.9.0.2,
vector-algorithms +bench +boundschecks -internalchecks -llvm +properties -unsafechecks, vector-algorithms +bench +boundschecks -internalchecks -llvm +properties -unsafechecks,
any.vector-stream ==0.1.0.1, any.vector-stream ==0.1.0.1,
any.void ==0.7.3, any.void ==0.7.3,
@ -356,10 +357,10 @@ constraints: any.Cabal ==3.10.3.0,
any.wai ==3.2.4, any.wai ==3.2.4,
any.wai-app-static ==3.1.9, any.wai-app-static ==3.1.9,
wai-app-static +crypton -print, wai-app-static +crypton -print,
any.wai-extra ==3.1.17, any.wai-extra ==3.1.15,
wai-extra -build-example, wai-extra -build-example,
any.wai-logger ==2.5.0, any.wai-logger ==2.4.0,
any.warp ==3.4.7, any.warp ==3.4.1,
warp +allow-sendfilefd -network-bytestring -warp-debug +x509, warp +allow-sendfilefd -network-bytestring -warp-debug +x509,
any.wide-word ==0.1.6.0, any.wide-word ==0.1.6.0,
any.witherable ==0.5, any.witherable ==0.5,
@ -369,4 +370,4 @@ constraints: any.Cabal ==3.10.3.0,
wreq -aws -developer +doctest -httpbin, wreq -aws -developer +doctest -httpbin,
any.zlib ==0.7.1.0, any.zlib ==0.7.1.0,
zlib -bundled-c-zlib +non-blocking-ffi +pkg-config zlib -bundled-c-zlib +non-blocking-ffi +pkg-config
index-state: hackage.haskell.org 2024-12-14T09:52:48Z index-state: hackage.haskell.org 2024-07-10T18:40:26Z

BIN
sapling-output.params Normal file

Binary file not shown.

BIN
sapling-spend.params Normal file

Binary file not shown.

File diff suppressed because it is too large Load diff

View file

@ -3,7 +3,6 @@
-- | Core wallet functionality for Zenith -- | Core wallet functionality for Zenith
module Zenith.Core where module Zenith.Core where
import Control.Concurrent (forkIO)
import Control.Exception (throwIO, try) import Control.Exception (throwIO, try)
import Control.Monad (forM, unless, when) import Control.Monad (forM, unless, when)
import Control.Monad.IO.Class (liftIO) import Control.Monad.IO.Class (liftIO)
@ -21,13 +20,11 @@ import qualified Data.ByteString as BS
import Data.HexString (HexString, hexBytes, hexString, toBytes, toText) import Data.HexString (HexString, hexBytes, hexString, toBytes, toText)
import Data.Int (Int32, Int64) import Data.Int (Int32, Int64)
import Data.List import Data.List
import Data.Maybe (catMaybes, fromJust, fromMaybe, isJust, isNothing) import Data.Maybe (fromJust, fromMaybe)
import Data.Scientific (Scientific, scientific, toBoundedInteger) import Data.Scientific (Scientific, scientific, toBoundedInteger)
import qualified Data.Text as T import qualified Data.Text as T
import qualified Data.Text.Encoding as E import qualified Data.Text.Encoding as E
import Data.Time import Data.Time
import qualified Data.UUID as U
import Data.UUID.V4 (nextRandom)
import qualified Database.Esqueleto.Experimental as ESQ import qualified Database.Esqueleto.Experimental as ESQ
import Database.Persist import Database.Persist
import Database.Persist.Sqlite import Database.Persist.Sqlite
@ -35,39 +32,33 @@ import Haskoin.Crypto.Keys (XPrvKey(..))
import Lens.Micro ((&), (.~), (^.), set) import Lens.Micro ((&), (.~), (^.), set)
import ZcashHaskell.Keys import ZcashHaskell.Keys
import ZcashHaskell.Orchard import ZcashHaskell.Orchard
( decodeUfvk ( decryptOrchardActionSK
, decodeUivk
, decryptOrchardActionFvk
, decryptOrchardActionIvk
, decryptOrchardActionSK
, encodeUnifiedAddress , encodeUnifiedAddress
, genOrchardReceiver , genOrchardReceiver
, genOrchardReceiverFvk
, genOrchardReceiverIvk
, genOrchardSpendingKey , genOrchardSpendingKey
, getOrchardFrontier
, getOrchardNotePosition
, getOrchardTreeParts , getOrchardTreeParts
, getOrchardWitness
, isValidUnifiedAddress , isValidUnifiedAddress
, parseAddress , updateOrchardCommitmentTree
, updateOrchardWitness , updateOrchardWitness
) )
import ZcashHaskell.Sapling import ZcashHaskell.Sapling
( decodeSaplingOutputEsk ( decodeSaplingOutputEsk
, decodeSaplingOutputFvk
, decodeSaplingOutputIvk
, genSaplingInternalAddress , genSaplingInternalAddress
, genSaplingInternalAddressFvk
, genSaplingPaymentAddress , genSaplingPaymentAddress
, genSaplingReceiverFvk
, genSaplingReceiverIvk
, genSaplingSpendingKey , genSaplingSpendingKey
, getSaplingFrontier
, getSaplingNotePosition
, getSaplingTreeParts , getSaplingTreeParts
, getSaplingWitness
, updateSaplingCommitmentTree
, updateSaplingWitness , updateSaplingWitness
) )
import ZcashHaskell.Transparent import ZcashHaskell.Transparent
( genTransparentPrvKey ( genTransparentPrvKey
, genTransparentReceiver , genTransparentReceiver
, genTransparentReceiverFvk
, genTransparentReceiverIvk
, genTransparentSecretKey , genTransparentSecretKey
) )
import ZcashHaskell.Types import ZcashHaskell.Types
@ -75,8 +66,7 @@ import ZcashHaskell.Utils
import Zenith.DB import Zenith.DB
import Zenith.Tree import Zenith.Tree
import Zenith.Types import Zenith.Types
( AccountType(..) ( Config(..)
, Config(..)
, HexStringDB(..) , HexStringDB(..)
, OrchardSpendingKeyDB(..) , OrchardSpendingKeyDB(..)
, PhraseDB(..) , PhraseDB(..)
@ -87,15 +77,10 @@ import Zenith.Types
, ScopeDB(..) , ScopeDB(..)
, TransparentSpendingKeyDB(..) , TransparentSpendingKeyDB(..)
, UnifiedAddressDB(..) , UnifiedAddressDB(..)
, UnifiedFvkDB(..)
, UnifiedIvkDB(..)
, ValidAddressAPI(..) , ValidAddressAPI(..)
, ZcashNetDB(..) , ZcashNetDB(..)
, ZebraTreeInfo(..) , ZebraTreeInfo(..)
, ZenithStatus(..)
, ZenithUuid(..)
) )
import Zenith.Utils (getTransparentFromUA)
-- * Zebra Node interaction -- * Zebra Node interaction
-- | Checks the status of the `zebrad` node -- | Checks the status of the `zebrad` node
@ -205,26 +190,20 @@ createTransparentSpendingKey zw i = do
createZcashAccount :: createZcashAccount ::
T.Text -- ^ The account's name T.Text -- ^ The account's name
-> Int -- ^ The account's index -> Int -- ^ The account's index
-> ZcashNet -- ^ The network
-> Entity ZcashWallet -- ^ The Zcash wallet that this account will be attached to -> Entity ZcashWallet -- ^ The Zcash wallet that this account will be attached to
-> IO ZcashAccount -> IO ZcashAccount
createZcashAccount n i znet zw = do createZcashAccount n i zw = do
orSk <- createOrchardSpendingKey (entityVal zw) i orSk <- createOrchardSpendingKey (entityVal zw) i
sapSk <- createSaplingSpendingKey (entityVal zw) i sapSk <- createSaplingSpendingKey (entityVal zw) i
tSk <- createTransparentSpendingKey (entityVal zw) i tSk <- createTransparentSpendingKey (entityVal zw) i
FullVk fvk <- deriveUfvk znet (Just orSk) (Just sapSk) (Just tSk)
IncomingVk ivk <- deriveUivk znet (Just orSk) (Just sapSk) (Just tSk)
return $ return $
ZcashAccount ZcashAccount
i i
(entityKey zw) (entityKey zw)
n n
(Just $ OrchardSpendingKeyDB orSk) (OrchardSpendingKeyDB orSk)
(Just $ SaplingSpendingKeyDB sapSk) (SaplingSpendingKeyDB sapSk)
(Just $ TransparentSpendingKeyDB tSk) (TransparentSpendingKeyDB tSk)
(Just $ UnifiedFvkDB fvk)
(Just $ UnifiedIvkDB ivk)
Local
-- * Addresses -- * Addresses
-- | Create an external unified address for the given account and index -- | Create an external unified address for the given account and index
@ -236,70 +215,27 @@ createWalletAddress ::
-> Entity ZcashAccount -- ^ The Zcash account that the address will be attached to -> Entity ZcashAccount -- ^ The Zcash account that the address will be attached to
-> IO WalletAddress -> IO WalletAddress
createWalletAddress n i zNet scope za = do createWalletAddress n i zNet scope za = do
case zcashAccountType (entityVal za) of
Local -> do
let oRec = let oRec =
genOrchardReceiver i scope . getOrchSK =<< genOrchardReceiver i scope $
zcashAccountOrchSpendKey (entityVal za) getOrchSK $ zcashAccountOrchSpendKey $ entityVal za
let sRec = let sRec =
case scope of case scope of
External -> External ->
genSaplingPaymentAddress i . getSapSK =<< genSaplingPaymentAddress i $
zcashAccountSapSpendKey (entityVal za) getSapSK $ zcashAccountSapSpendKey $ entityVal za
Internal -> Internal ->
genSaplingInternalAddress . getSapSK =<< genSaplingInternalAddress $
zcashAccountSapSpendKey (entityVal za) getSapSK $ zcashAccountSapSpendKey $ entityVal za
let tKey = getTranSK <$> zcashAccountTPrivateKey (entityVal za)
tRec <- tRec <-
case tKey of genTransparentReceiver i scope $
Nothing -> return Nothing getTranSK $ zcashAccountTPrivateKey $ entityVal za
Just k -> do
x <- genTransparentReceiver i scope k
return $ Just x
return $ return $
WalletAddress WalletAddress
i i
(entityKey za) (entityKey za)
n n
(UnifiedAddressDB $ (UnifiedAddressDB $
encodeUnifiedAddress $ UnifiedAddress zNet oRec sRec tRec) encodeUnifiedAddress $ UnifiedAddress zNet oRec sRec (Just tRec))
(ScopeDB scope)
FullViewKey -> do
let fvk = zcashAccountFvk (entityVal za)
let oRec = genOrchardReceiverFvk i scope . o_key . getFvk =<< fvk
let sRec =
case scope of
External -> genSaplingReceiverFvk i . s_key . getFvk =<< fvk
Internal -> genSaplingInternalAddressFvk . s_key . getFvk =<< fvk
let tKey = t_key . getFvk <$> fvk
tRec <-
case tKey of
Nothing -> return Nothing
Just k -> genTransparentReceiverFvk i scope k
return $
WalletAddress
i
(entityKey za)
n
(UnifiedAddressDB $
encodeUnifiedAddress $ UnifiedAddress zNet oRec sRec tRec)
(ScopeDB scope)
IncomingViewKey -> do
let ivk = zcashAccountIvk (entityVal za)
let oRec = genOrchardReceiverIvk i . i_o_key . getIvk =<< ivk
let sRec = genSaplingReceiverIvk i . i_s_key . getIvk =<< ivk
let tKey = i_t_key . getIvk <$> ivk
tRec <-
case tKey of
Nothing -> return Nothing
Just k -> genTransparentReceiverIvk i k
return $
WalletAddress
i
(entityKey za)
n
(UnifiedAddressDB $
encodeUnifiedAddress $ UnifiedAddress zNet oRec sRec tRec)
(ScopeDB scope) (ScopeDB scope)
-- | Create an external unified address for the given account and index with custom receivers -- | Create an external unified address for the given account and index with custom receivers
@ -314,26 +250,26 @@ createCustomWalletAddress ::
-> IO WalletAddress -> IO WalletAddress
createCustomWalletAddress n i zNet scope za exSap exTr = do createCustomWalletAddress n i zNet scope za exSap exTr = do
let oRec = let oRec =
genOrchardReceiver i scope . getOrchSK =<< genOrchardReceiver i scope $
zcashAccountOrchSpendKey (entityVal za) getOrchSK $ zcashAccountOrchSpendKey $ entityVal za
let sRec = let sRec =
if exSap if exSap
then Nothing then Nothing
else case scope of else case scope of
External -> External ->
genSaplingPaymentAddress i . getSapSK =<< genSaplingPaymentAddress i $
zcashAccountSapSpendKey (entityVal za) getSapSK $ zcashAccountSapSpendKey $ entityVal za
Internal -> Internal ->
genSaplingInternalAddress . getSapSK =<< genSaplingInternalAddress $
zcashAccountSapSpendKey (entityVal za) getSapSK $ zcashAccountSapSpendKey $ entityVal za
tRec <- tRec <-
if exTr if exTr
then return Nothing then return Nothing
else do else Just <$>
let tKey = getTranSK <$> zcashAccountTPrivateKey (entityVal za) genTransparentReceiver
case tKey of i
Nothing -> return Nothing scope
Just k -> Just <$> genTransparentReceiver i scope k (getTranSK $ zcashAccountTPrivateKey $ entityVal za)
return $ return $
WalletAddress WalletAddress
i i
@ -343,133 +279,6 @@ createCustomWalletAddress n i zNet scope za exSap exTr = do
encodeUnifiedAddress $ UnifiedAddress zNet oRec sRec tRec) encodeUnifiedAddress $ UnifiedAddress zNet oRec sRec tRec)
(ScopeDB scope) (ScopeDB scope)
importViewingKey ::
ConnectionPool -- ^ database connection to use
-> T.Text -- ^ name to use
-> ZcashNet -- ^ the network the wallet is on
-> ValidVk -- ^ viewing key
-> Int -- ^ birthday height
-> IO (Either T.Text Int64)
importViewingKey pool name znet vk bh = do
minBh <- getMinBirthdayHeight pool $ ZcashNetDB znet
let checkNet =
case znet of
MainNet ->
case vk of
FullVk fvk -> net fvk == 1
IncomingVk ivk -> i_net ivk == 1
TestNet ->
case vk of
FullVk fvk -> net fvk == 2
IncomingVk ivk -> i_net ivk == 2
RegTestNet -> False
if checkNet
then do
when (bh < minBh) $ do
clearWalletTransactions pool
clearWalletData pool
vkWallet <- getVkWallet pool
wal <-
case vkWallet of
Nothing -> do
x <- saveVkWallet pool znet bh
case x of
Nothing -> fail "Couldn't create VK wallet"
Just w -> return w
Just w -> return w
maxIx <- getMaxAccount pool $ entityKey wal
case vk of
FullVk fvk -> do
acc <-
saveAccount pool $
ZcashAccount
(maxIx + 1)
(entityKey wal)
name
Nothing
Nothing
Nothing
(Just $ UnifiedFvkDB fvk)
Nothing
FullViewKey
case acc of
Nothing -> return $ Left "Failed to save VK account"
Just acc' -> do
defAddr <- createWalletAddress "Default" 0 znet External acc'
chgAddr <- createWalletAddress "Change" 0 znet Internal acc'
_ <- saveAddress pool defAddr
_ <- saveAddress pool chgAddr
return $ Right $ fromSqlKey (entityKey acc')
IncomingVk ivk -> do
acc1 <-
saveAccount pool $
ZcashAccount
(maxIx + 1)
(entityKey wal)
name
Nothing
Nothing
Nothing
Nothing
(Just $ UnifiedIvkDB ivk)
IncomingViewKey
case acc1 of
Nothing -> return $ Left "Failed to save VK account"
Just acc1' -> do
defAddr <- createWalletAddress "Default" 0 znet External acc1'
chgAddr <- createWalletAddress "Change" 0 znet Internal acc1'
_ <- saveAddress pool defAddr
_ <- saveAddress pool chgAddr
return $ Right $ fromSqlKey $ entityKey acc1'
else return $ Left "Viewing key is not for the current network"
parseVK :: T.Text -> Maybe ValidVk
parseVK input =
case decodeUfvk (E.encodeUtf8 input) of
Just fvk -> Just $ FullVk fvk
Nothing ->
case decodeUivk (E.encodeUtf8 input) of
Just ivk -> Just $ IncomingVk ivk
Nothing -> Nothing
importSeedPhrase ::
ConnectionPool -- ^ database connection to use
-> T.Text -- ^ name to use
-> ZcashNet -- ^ the network the wallet is on
-> Phrase -- ^ seed phrase
-> Int -- ^ birthday height
-> IO (Either T.Text Int64)
importSeedPhrase pool name znet ph bh = do
minBh <- getMinBirthdayHeight pool $ ZcashNetDB znet
when (bh < minBh) $ do
clearWalletTransactions pool
clearWalletData pool
case getWalletSeed ph of
Nothing -> return $ Left "Invalid seed phrase"
Just _ -> do
nw <-
saveWallet pool $
ZcashWallet name (ZcashNetDB znet) (PhraseDB ph) bh 0 True
case nw of
Nothing -> return $ Left "Wallet with that name already exists"
Just nw' -> do
aIx <- getMaxAccount pool (entityKey nw')
na <-
try $ createZcashAccount "Default" (aIx + 1) znet nw' :: IO
(Either IOError ZcashAccount)
case na of
Left e -> return $ Left $ T.pack $ show e
Right na' -> do
acc <- saveAccount pool na'
case acc of
Nothing -> return $ Left "Failed to save VK account"
Just acc' -> do
defAddr <- createWalletAddress "Default" 0 znet External acc'
chgAddr <- createWalletAddress "Change" 0 znet Internal acc'
_ <- saveAddress pool defAddr
_ <- saveAddress pool chgAddr
return $ Right $ fromSqlKey (entityKey acc')
-- * Wallet -- * Wallet
-- | Find the Sapling notes that match the given spending key -- | Find the Sapling notes that match the given spending key
findSaplingOutputs :: findSaplingOutputs ::
@ -480,6 +289,8 @@ findSaplingOutputs ::
-> NoLoggingT IO () -> NoLoggingT IO ()
findSaplingOutputs config b znet za = do findSaplingOutputs config b znet za = do
let dbPath = c_dbPath config let dbPath = c_dbPath config
let zebraHost = c_zebraHost config
let zebraPort = c_zebraPort config
let zn = getNet znet let zn = getNet znet
pool <- liftIO $ runNoLoggingT $ initPool dbPath pool <- liftIO $ runNoLoggingT $ initPool dbPath
tList <- liftIO $ getShieldedOutputs pool b znet tList <- liftIO $ getShieldedOutputs pool b znet
@ -489,45 +300,29 @@ findSaplingOutputs config b znet za = do
liftIO $ throwIO $ userError "Failed to read Sapling commitment tree" liftIO $ throwIO $ userError "Failed to read Sapling commitment tree"
Just (sT', treeSync) -> do Just (sT', treeSync) -> do
logDebugN "Sapling tree valid" logDebugN "Sapling tree valid"
let acType = zcashAccountType $ entityVal za mapM_ (decryptNotes sT' zn pool) tList
mapM_ (decryptNotes sT' zn pool acType) tList
sapNotes <- liftIO $ getWalletSapNotes pool (entityKey za) sapNotes <- liftIO $ getWalletSapNotes pool (entityKey za)
liftIO $ findSapSpends pool (entityKey za) sapNotes liftIO $ findSapSpends pool (entityKey za) sapNotes
where where
sk :: Maybe SaplingSpendingKeyDB sk :: SaplingSpendingKeyDB
sk = zcashAccountSapSpendKey $ entityVal za sk = zcashAccountSapSpendKey $ entityVal za
fvk :: Maybe UnifiedFvkDB
fvk = zcashAccountFvk $ entityVal za
ivk :: Maybe UnifiedIvkDB
ivk = zcashAccountIvk $ entityVal za
decryptNotes :: decryptNotes ::
Tree SaplingNode Tree SaplingNode
-> ZcashNet -> ZcashNet
-> ConnectionPool -> ConnectionPool
-> AccountType
-> (Entity ZcashTransaction, Entity ShieldOutput) -> (Entity ZcashTransaction, Entity ShieldOutput)
-> NoLoggingT IO () -> NoLoggingT IO ()
decryptNotes st n pool act (zt, o) = do decryptNotes st n pool (zt, o) = do
case getNotePosition st $ fromSqlKey $ entityKey o of case getNotePosition st $ fromSqlKey $ entityKey o of
Nothing -> do Nothing -> do
logErrorN "Couldn't find sapling note in commitment tree" logErrorN "Couldn't find sapling note in commitment tree"
return () return ()
Just nP -> do Just nP -> do
let externalDecode =
case act of
Local -> decodeShOut External n nP o
FullViewKey -> decodeShOutFvk External o $ fromIntegral nP
IncomingViewKey -> decodeShOutIvk o
let internalDecode =
case act of
Local -> decodeShOut Internal n nP o
FullViewKey -> decodeShOutFvk Internal o $ fromIntegral nP
IncomingViewKey -> Nothing
logDebugN "got position" logDebugN "got position"
case externalDecode of case decodeShOut External n nP o of
Nothing -> do Nothing -> do
logDebugN "couldn't decode external" logDebugN "couldn't decode external"
case internalDecode of case decodeShOut Internal n nP o of
Nothing -> do Nothing -> do
logDebugN "couldn't decode internal" logDebugN "couldn't decode internal"
Just dn1 -> do Just dn1 -> do
@ -554,12 +349,9 @@ findSaplingOutputs config b znet za = do
dn0 dn0
decodeShOut :: decodeShOut ::
Scope -> ZcashNet -> Int32 -> Entity ShieldOutput -> Maybe DecodedNote Scope -> ZcashNet -> Int32 -> Entity ShieldOutput -> Maybe DecodedNote
decodeShOut scope n pos s = decodeShOut scope n pos s = do
case sk of
Nothing -> Nothing
Just sk' -> do
decodeSaplingOutputEsk decodeSaplingOutputEsk
(getSapSK sk') (getSapSK sk)
(ShieldedOutput (ShieldedOutput
(getHex $ shieldOutputCv $ entityVal s) (getHex $ shieldOutputCv $ entityVal s)
(getHex $ shieldOutputCmu $ entityVal s) (getHex $ shieldOutputCmu $ entityVal s)
@ -570,36 +362,6 @@ findSaplingOutputs config b znet za = do
n n
scope scope
(fromIntegral pos) (fromIntegral pos)
decodeShOutFvk :: Scope -> Entity ShieldOutput -> Int64 -> Maybe DecodedNote
decodeShOutFvk scope s pos =
case fvk of
Nothing -> Nothing
Just sk' -> do
decodeSaplingOutputFvk
(s_key $ getFvk sk')
(ShieldedOutput
(getHex $ shieldOutputCv $ entityVal s)
(getHex $ shieldOutputCmu $ entityVal s)
(getHex $ shieldOutputEphKey $ entityVal s)
(getHex $ shieldOutputEncCipher $ entityVal s)
(getHex $ shieldOutputOutCipher $ entityVal s)
(getHex $ shieldOutputProof $ entityVal s))
scope
pos
decodeShOutIvk :: Entity ShieldOutput -> Maybe DecodedNote
decodeShOutIvk s =
case ivk of
Nothing -> Nothing
Just sk' -> do
decodeSaplingOutputIvk
(i_s_key $ getIvk sk')
(ShieldedOutput
(getHex $ shieldOutputCv $ entityVal s)
(getHex $ shieldOutputCmu $ entityVal s)
(getHex $ shieldOutputEphKey $ entityVal s)
(getHex $ shieldOutputEncCipher $ entityVal s)
(getHex $ shieldOutputOutCipher $ entityVal s)
(getHex $ shieldOutputProof $ entityVal s))
-- | Get Orchard actions -- | Get Orchard actions
findOrchardActions :: findOrchardActions ::
@ -610,6 +372,8 @@ findOrchardActions ::
-> IO () -> IO ()
findOrchardActions config b znet za = do findOrchardActions config b znet za = do
let dbPath = c_dbPath config let dbPath = c_dbPath config
let zebraHost = c_zebraHost config
let zebraPort = c_zebraPort config
let zn = getNet znet let zn = getNet znet
pool <- runNoLoggingT $ initPool dbPath pool <- runNoLoggingT $ initPool dbPath
tList <- getOrchardActions pool b znet tList <- getOrchardActions pool b znet
@ -617,33 +381,24 @@ findOrchardActions config b znet za = do
case sT of case sT of
Nothing -> throwIO $ userError "Failed to read Orchard commitment tree" Nothing -> throwIO $ userError "Failed to read Orchard commitment tree"
Just (sT', treeSync) -> do Just (sT', treeSync) -> do
mapM_ (decryptNotes sT' pool) tList mapM_ (decryptNotes sT' zn pool) tList
orchNotes <- getWalletOrchNotes pool (entityKey za) orchNotes <- getWalletOrchNotes pool (entityKey za)
findOrchSpends pool (entityKey za) orchNotes findOrchSpends pool (entityKey za) orchNotes
where where
decryptNotes :: decryptNotes ::
Tree OrchardNode Tree OrchardNode
-> ZcashNet
-> ConnectionPool -> ConnectionPool
-> (Entity ZcashTransaction, Entity OrchAction) -> (Entity ZcashTransaction, Entity OrchAction)
-> IO () -> IO ()
decryptNotes ot pool (zt, o) = do decryptNotes ot n pool (zt, o) = do
case getNotePosition ot (fromSqlKey $ entityKey o) of case getNotePosition ot (fromSqlKey $ entityKey o) of
Nothing -> do Nothing -> do
return () return ()
Just nP -> do Just nP ->
let externalDecode = case decodeOrchAction External nP o of
case zcashAccountType (entityVal za) of
Local -> decodeOrchAction External o
FullViewKey -> decodeOrchActionFvk External o
IncomingViewKey -> decodeOrchActionIvk o
let internalDecode =
case zcashAccountType (entityVal za) of
Local -> decodeOrchAction Internal o
FullViewKey -> decodeOrchActionFvk Internal o
IncomingViewKey -> Nothing
case externalDecode of
Nothing -> Nothing ->
case internalDecode of case decodeOrchAction Internal nP o of
Nothing -> return () Nothing -> return ()
Just dn1 -> do Just dn1 -> do
wId <- saveWalletTransaction pool (entityKey za) zt wId <- saveWalletTransaction pool (entityKey za) zt
@ -665,48 +420,11 @@ findOrchardActions config b znet za = do
(entityKey za) (entityKey za)
(entityKey o) (entityKey o)
dn dn
sk :: Maybe OrchardSpendingKeyDB sk :: OrchardSpendingKeyDB
sk = zcashAccountOrchSpendKey $ entityVal za sk = zcashAccountOrchSpendKey $ entityVal za
fvk :: Maybe UnifiedFvkDB decodeOrchAction :: Scope -> Int32 -> Entity OrchAction -> Maybe DecodedNote
fvk = zcashAccountFvk $ entityVal za decodeOrchAction scope pos o =
ivk :: Maybe UnifiedIvkDB decryptOrchardActionSK (getOrchSK sk) scope $
ivk = zcashAccountIvk $ entityVal za
decodeOrchAction :: Scope -> Entity OrchAction -> Maybe DecodedNote
decodeOrchAction scope o =
case sk of
Nothing -> Nothing
Just sk' -> do
decryptOrchardActionSK (getOrchSK sk') scope $
OrchardAction
(getHex $ orchActionNf $ entityVal o)
(getHex $ orchActionRk $ entityVal o)
(getHex $ orchActionCmx $ entityVal o)
(getHex $ orchActionEphKey $ entityVal o)
(getHex $ orchActionEncCipher $ entityVal o)
(getHex $ orchActionOutCipher $ entityVal o)
(getHex $ orchActionCv $ entityVal o)
(getHex $ orchActionAuth $ entityVal o)
decodeOrchActionFvk :: Scope -> Entity OrchAction -> Maybe DecodedNote
decodeOrchActionFvk scope o =
case fvk of
Nothing -> Nothing
Just sk' -> do
decryptOrchardActionFvk (getFvk sk') scope $
OrchardAction
(getHex $ orchActionNf $ entityVal o)
(getHex $ orchActionRk $ entityVal o)
(getHex $ orchActionCmx $ entityVal o)
(getHex $ orchActionEphKey $ entityVal o)
(getHex $ orchActionEncCipher $ entityVal o)
(getHex $ orchActionOutCipher $ entityVal o)
(getHex $ orchActionCv $ entityVal o)
(getHex $ orchActionAuth $ entityVal o)
decodeOrchActionIvk :: Entity OrchAction -> Maybe DecodedNote
decodeOrchActionIvk o =
case ivk of
Nothing -> Nothing
Just sk' -> do
decryptOrchardActionIvk (getIvk sk') $
OrchardAction OrchardAction
(getHex $ orchActionNf $ entityVal o) (getHex $ orchActionNf $ entityVal o)
(getHex $ orchActionRk $ entityVal o) (getHex $ orchActionRk $ entityVal o)
@ -1028,37 +746,14 @@ deshieldNotes ::
-> ZcashNet -> ZcashNet
-> ZcashAccountId -> ZcashAccountId
-> Int -> Int
-> Scientific -> ProposedNote
-> NoLoggingT IO (Either TxError HexString) -> NoLoggingT IO (Either TxError HexString)
deshieldNotes pool zebraHost zebraPort znet za bh pnote = do deshieldNotes pool zebraHost zebraPort znet za bh pnote = do
bal <- liftIO $ getShieldedBalance pool za bal <- liftIO $ getShieldedBalance pool za
addrs <- getAddresses pool za let zats = pn_amt pnote * scientific 1 8
let defAddr =
parseAddress $
E.encodeUtf8 $ getUA $ walletAddressUAddress $ entityVal $ head addrs
case defAddr of
Nothing -> return $ Left ZHError
Just (Unified x) -> do
case getTransparentFromUA x of
Nothing -> return $ Left ZHError
Just ta -> do
let zats = pnote * scientific 1 8
if fromInteger bal > (scientific 2 4 + zats) if fromInteger bal > (scientific 2 4 + zats)
then prepareTxV2 then prepareTxV2 pool zebraHost zebraPort znet za bh [pnote] Low
pool
zebraHost
zebraPort
znet
za
bh
[ ProposedNote
(ValidAddressAPI $ Transparent ta)
pnote
Nothing
]
Low
else return $ Left InsufficientFunds else return $ Left InsufficientFunds
_anyOther -> return $ Left ZHError
shieldTransparentNotes :: shieldTransparentNotes ::
ConnectionPool ConnectionPool
@ -1067,8 +762,8 @@ shieldTransparentNotes ::
-> ZcashNet -> ZcashNet
-> ZcashAccountId -> ZcashAccountId
-> Int -> Int
-> NoLoggingT IO [Either TxError U.UUID] -> NoLoggingT IO [Either TxError HexString]
shieldTransparentNotes pool zHost zPort znet za bh = do shieldTransparentNotes pool zebraHost zebraPort znet za bh = do
accRead <- liftIO $ getAccountById pool za accRead <- liftIO $ getAccountById pool za
logDebugN $ T.pack $ "Target block: " ++ show bh logDebugN $ T.pack $ "Target block: " ++ show bh
case accRead of case accRead of
@ -1077,36 +772,22 @@ shieldTransparentNotes pool zHost zPort znet za bh = do
return [Left ZHError] return [Left ZHError]
Just acc -> do Just acc -> do
trNotes' <- liftIO $ getWalletUnspentTrNotes pool za trNotes' <- liftIO $ getWalletUnspentTrNotes pool za
if null trNotes'
then return [Left InsufficientFunds]
else do
dRecvs <- liftIO $ getReceivers pool trNotes' dRecvs <- liftIO $ getReceivers pool trNotes'
let fNotes = let fNotes =
map map
(\x -> (\x ->
filter filter (\y -> walletTrNoteAddress (entityVal y) == x) trNotes')
(\y -> walletTrNoteAddress (entityVal y) == x)
trNotes')
dRecvs dRecvs
sTree <- liftIO $ getSaplingTree pool sTree <- liftIO $ getSaplingTree pool
oTree <- liftIO $ getOrchardTree pool oTree <- liftIO $ getOrchardTree pool
forM fNotes $ \trNotes -> do forM fNotes $ \trNotes -> do
opid <- liftIO nextRandom
startTime <- liftIO getCurrentTime
opkey <-
liftIO $
saveOperation pool $
Operation (ZenithUuid opid) startTime Nothing Processing Nothing
case opkey of
Nothing -> return $ Left ZHError
Just opkey' -> do
let noteTotal = getTotalAmount (trNotes, [], []) let noteTotal = getTotalAmount (trNotes, [], [])
tSpends <- tSpends <-
liftIO $ liftIO $
prepTSpends prepTSpends
(getTranSK <$> zcashAccountTPrivateKey (entityVal acc)) (getTranSK $ zcashAccountTPrivateKey $ entityVal acc)
trNotes trNotes
chgAddr <- liftIO $ getInternalAddresses pool $ entityKey acc chgAddr <- getInternalAddresses pool $ entityKey acc
let internalUA = let internalUA =
getUA $ walletAddressUAddress $ entityVal $ head chgAddr getUA $ walletAddressUAddress $ entityVal $ head chgAddr
let oRcvr = let oRcvr =
@ -1115,39 +796,25 @@ shieldTransparentNotes pool zHost zPort znet za bh = do
let dummy = let dummy =
OutgoingNote OutgoingNote
4 4
"deadbeef" (getBytes $ getOrchSK $ zcashAccountOrchSpendKey $ entityVal acc)
(getBytes oRcvr) (getBytes oRcvr)
(fromIntegral $ noteTotal - 500) (fromIntegral $ noteTotal - 500)
"" ""
True True
let feeAmt = calculateTxFee (trNotes, [], []) [dummy] let feeAmt = calculateTxFee (trNotes, [], []) [dummy]
let osk' =
getOrchSK <$> zcashAccountOrchSpendKey (entityVal acc)
case osk' of
Nothing -> return $ Left ZHError
Just osk -> do
let snote = let snote =
OutgoingNote OutgoingNote
4 4
(getBytes osk) (getBytes $ getOrchSK $ zcashAccountOrchSpendKey $ entityVal acc)
(getBytes oRcvr) (getBytes oRcvr)
(fromIntegral $ noteTotal - fromIntegral feeAmt) (fromIntegral $ noteTotal - fromIntegral feeAmt)
"" ""
True True
_ <-
liftIO $
forkIO $ do
tx <- tx <-
liftIO $ liftIO $
createTransaction createTransaction
(maybe (maybe (hexString "00") (getHash . value . fst) sTree)
(hexString "00") (maybe (hexString "00") (getHash . value . fst) oTree)
(getHash . value . fst)
sTree)
(maybe
(hexString "00")
(getHash . value . fst)
oTree)
tSpends tSpends
[] []
[] []
@ -1155,26 +822,8 @@ shieldTransparentNotes pool zHost zPort znet za bh = do
znet znet
(bh + 3) (bh + 3)
True True
case tx of logDebugN $ T.pack $ show tx
Left e -> return tx
finalizeOperation pool opkey' Failed $
T.pack $ show e
Right rawTx -> do
zebraRes <-
makeZebraCall
zHost
zPort
"sendrawtransaction"
[Data.Aeson.String $ toText rawTx]
case zebraRes of
Left e1 ->
finalizeOperation pool opkey' Failed $
T.pack $ show e1
Right txId ->
finalizeOperation pool opkey' Successful $
"Tx ID: " <> toText txId
logDebugN $ T.pack $ show opid
return $ Right opid
where where
getTotalAmount :: getTotalAmount ::
( [Entity WalletTrNote] ( [Entity WalletTrNote]
@ -1186,13 +835,10 @@ shieldTransparentNotes pool zHost zPort znet za bh = do
sum (map (fromIntegral . walletSapNoteValue . entityVal) s) + sum (map (fromIntegral . walletSapNoteValue . entityVal) s) +
sum (map (fromIntegral . walletOrchNoteValue . entityVal) o) sum (map (fromIntegral . walletOrchNoteValue . entityVal) o)
prepTSpends :: prepTSpends ::
Maybe TransparentSpendingKey TransparentSpendingKey
-> [Entity WalletTrNote] -> [Entity WalletTrNote]
-> IO [TransparentTxSpend] -> IO [TransparentTxSpend]
prepTSpends sk' notes = do prepTSpends sk notes = do
case sk' of
Nothing -> return []
Just sk -> do
forM notes $ \n -> do forM notes $ \n -> do
tAddRead <- getAddressById pool $ walletTrNoteAddress $ entityVal n tAddRead <- getAddressById pool $ walletTrNoteAddress $ entityVal n
case tAddRead of case tAddRead of
@ -1203,8 +849,7 @@ shieldTransparentNotes pool zHost zPort znet za bh = do
(walletAddressIndex $ entityVal tAdd) (walletAddressIndex $ entityVal tAdd)
(getScope $ walletAddressScope $ entityVal tAdd) (getScope $ walletAddressScope $ entityVal tAdd)
sk sk
mReverseTxId <- mReverseTxId <- getWalletTxId pool $ walletTrNoteTx $ entityVal n
getWalletTxId pool $ walletTrNoteTx $ entityVal n
case mReverseTxId of case mReverseTxId of
Nothing -> throwIO $ userError "failed to get tx ID" Nothing -> throwIO $ userError "failed to get tx ID"
Just (ESQ.Value reverseTxId) -> do Just (ESQ.Value reverseTxId) -> do
@ -1280,8 +925,7 @@ prepareTxV2 pool zebraHost zebraPort zn za bh pnotes policy = do
policy policy
case draft of case draft of
Left e -> return $ Left e Left e -> return $ Left e
Right draftOut' -> do Right draftOut -> do
let draftOut = catMaybes draftOut'
let fee = calculateTxFee (tList, sList, oList) draftOut let fee = calculateTxFee (tList, sList, oList) draftOut
logDebugN $ T.pack $ "calculated fee " ++ show fee logDebugN $ T.pack $ "calculated fee " ++ show fee
finalNotePlan <- finalNotePlan <-
@ -1301,19 +945,18 @@ prepareTxV2 pool zebraHost zebraPort zn za bh pnotes policy = do
tSpends1 <- tSpends1 <-
liftIO $ liftIO $
prepTSpends prepTSpends
(getTranSK <$> zcashAccountTPrivateKey (entityVal acc)) (getTranSK $ zcashAccountTPrivateKey $ entityVal acc)
tList1 tList1
sSpends1 <- sSpends1 <-
liftIO $ liftIO $
prepSSpends prepSSpends
(getSapSK <$> zcashAccountSapSpendKey (entityVal acc)) (getSapSK $ zcashAccountSapSpendKey $ entityVal acc)
(maybe InvalidTree fst sTree) (maybe InvalidTree fst sTree)
sList1 sList1
oSpends1 <- oSpends1 <-
liftIO $ liftIO $
prepOSpends prepOSpends
(getOrchSK <$> (getOrchSK $ zcashAccountOrchSpendKey $ entityVal acc)
zcashAccountOrchSpendKey (entityVal acc))
(maybe InvalidTree fst oTree) (maybe InvalidTree fst oTree)
oList1 oList1
let noteTotal1 = getTotalAmount (tList1, sList1, oList1) let noteTotal1 = getTotalAmount (tList1, sList1, oList1)
@ -1327,8 +970,7 @@ prepareTxV2 pool zebraHost zebraPort zn za bh pnotes policy = do
logDebugN $ T.pack $ show outgoing' logDebugN $ T.pack $ show outgoing'
case outgoing' of case outgoing' of
Left e -> return $ Left e Left e -> return $ Left e
Right outgoing'' -> do Right outgoing -> do
let outgoing = catMaybes outgoing''
tx <- tx <-
liftIO $ liftIO $
createTransaction createTransaction
@ -1409,14 +1051,8 @@ prepareTxV2 pool zebraHost zebraPort zn za bh pnotes policy = do
, zats , zats
, fromMaybe "" m) , fromMaybe "" m)
prepareOutgoingNote :: prepareOutgoingNote ::
ZcashAccount ZcashAccount -> (Int, BS.ByteString, Int64, T.Text) -> OutgoingNote
-> (Int, BS.ByteString, Int64, T.Text) prepareOutgoingNote zac (k, r, a, m) =
-> Maybe OutgoingNote
prepareOutgoingNote zac (k, r, a, m)
| k == 4 && isNothing (zcashAccountOrchSpendKey zac) = Nothing
| k == 3 && isNothing (zcashAccountSapSpendKey zac) = Nothing
| otherwise =
Just $
OutgoingNote OutgoingNote
(if k == 5 (if k == 5
then 1 then 1
@ -1424,8 +1060,8 @@ prepareTxV2 pool zebraHost zebraPort zn za bh pnotes policy = do
then 2 then 2
else fromIntegral k) else fromIntegral k)
(case k of (case k of
4 -> getBytes $ getOrchSK $ fromJust $ zcashAccountOrchSpendKey zac 4 -> getBytes $ getOrchSK $ zcashAccountOrchSpendKey zac
3 -> getBytes $ getSapSK $ fromJust $ zcashAccountSapSpendKey zac 3 -> getBytes $ getSapSK $ zcashAccountSapSpendKey zac
_anyOther -> BS.empty) _anyOther -> BS.empty)
r r
(fromIntegral a) (fromIntegral a)
@ -1436,11 +1072,11 @@ prepareTxV2 pool zebraHost zebraPort zn za bh pnotes policy = do
-> [(Int, BS.ByteString, Int64, T.Text)] -> [(Int, BS.ByteString, Int64, T.Text)]
-> Int64 -> Int64
-> PrivacyPolicy -> PrivacyPolicy
-> IO (Either TxError [Maybe OutgoingNote]) -> IO (Either TxError [OutgoingNote])
makeOutgoing acc recvs chg pol = do makeOutgoing acc recvs chg pol = do
let k = map (\(x, _, _, _) -> x) recvs let k = map (\(x, _, _, _) -> x) recvs
let j = map (\(_, _, x, _) -> x) recvs let j = map (\(_, _, x, _) -> x) recvs
chgAddr <- getInternalAddresses pool $ entityKey acc chgAddr <- runNoLoggingT $ getInternalAddresses pool $ entityKey acc
let internalUA = getUA $ walletAddressUAddress $ entityVal $ head chgAddr let internalUA = getUA $ walletAddressUAddress $ entityVal $ head chgAddr
case pol of case pol of
Full -> Full ->
@ -1454,8 +1090,7 @@ prepareTxV2 pool zebraHost zebraPort zn za bh pnotes policy = do
Left $ Left $
PrivacyPolicyError PrivacyPolicyError
"Multiple shielded pools not allowed for Full privacy" "Multiple shielded pools not allowed for Full privacy"
else if 3 `elem` k && else if 3 `elem` k
isJust (zcashAccountSapSpendKey $ entityVal acc)
then do then do
let chgRcvr = let chgRcvr =
fromJust $ fromJust $
@ -1463,12 +1098,10 @@ prepareTxV2 pool zebraHost zebraPort zn za bh pnotes policy = do
isValidUnifiedAddress isValidUnifiedAddress
(E.encodeUtf8 internalUA) (E.encodeUtf8 internalUA)
let cnote = let cnote =
Just $
OutgoingNote OutgoingNote
3 3
(getBytes $ (getBytes $
getSapSK $ getSapSK $
fromJust $
zcashAccountSapSpendKey $ entityVal acc) zcashAccountSapSpendKey $ entityVal acc)
(getBytes chgRcvr) (getBytes chgRcvr)
(fromIntegral chg) (fromIntegral chg)
@ -1479,9 +1112,7 @@ prepareTxV2 pool zebraHost zebraPort zn za bh pnotes policy = do
(prepareOutgoingNote (entityVal acc)) (prepareOutgoingNote (entityVal acc))
recvs recvs
return $ Right $ cnote : onotes return $ Right $ cnote : onotes
else if 4 `elem` k && else if 4 `elem` k
isJust
(zcashAccountOrchSpendKey $ entityVal acc)
then do then do
let chgRcvr = let chgRcvr =
fromJust $ fromJust $
@ -1489,12 +1120,10 @@ prepareTxV2 pool zebraHost zebraPort zn za bh pnotes policy = do
isValidUnifiedAddress isValidUnifiedAddress
(E.encodeUtf8 internalUA) (E.encodeUtf8 internalUA)
let cnote = let cnote =
Just $
OutgoingNote OutgoingNote
4 4
(getBytes $ (getBytes $
getOrchSK $ getOrchSK $
fromJust $
zcashAccountOrchSpendKey $ zcashAccountOrchSpendKey $
entityVal acc) entityVal acc)
(getBytes chgRcvr) (getBytes chgRcvr)
@ -1518,12 +1147,10 @@ prepareTxV2 pool zebraHost zebraPort zn za bh pnotes policy = do
fromJust $ fromJust $
o_rec =<< isValidUnifiedAddress (E.encodeUtf8 internalUA) o_rec =<< isValidUnifiedAddress (E.encodeUtf8 internalUA)
let cnote = let cnote =
Just $
OutgoingNote OutgoingNote
4 4
(getBytes $ (getBytes $
getOrchSK $ getOrchSK $ zcashAccountOrchSpendKey $ entityVal acc)
fromJust $ zcashAccountOrchSpendKey $ entityVal acc)
(getBytes chgRcvr) (getBytes chgRcvr)
(fromIntegral chg) (fromIntegral chg)
"" ""
@ -1541,12 +1168,10 @@ prepareTxV2 pool zebraHost zebraPort zn za bh pnotes policy = do
fromJust $ fromJust $
o_rec =<< isValidUnifiedAddress (E.encodeUtf8 internalUA) o_rec =<< isValidUnifiedAddress (E.encodeUtf8 internalUA)
let cnote = let cnote =
Just $
OutgoingNote OutgoingNote
4 4
(getBytes $ (getBytes $
getOrchSK $ getOrchSK $ zcashAccountOrchSpendKey $ entityVal acc)
fromJust $ zcashAccountOrchSpendKey $ entityVal acc)
(getBytes chgRcvr) (getBytes chgRcvr)
(fromIntegral chg) (fromIntegral chg)
"" ""
@ -1564,7 +1189,6 @@ prepareTxV2 pool zebraHost zebraPort zn za bh pnotes policy = do
fromJust $ fromJust $
t_rec =<< isValidUnifiedAddress (E.encodeUtf8 internalUA) t_rec =<< isValidUnifiedAddress (E.encodeUtf8 internalUA)
let cnote = let cnote =
Just $
OutgoingNote OutgoingNote
1 1
BS.empty BS.empty
@ -1584,13 +1208,10 @@ prepareTxV2 pool zebraHost zebraPort zn za bh pnotes policy = do
sum (map (fromIntegral . walletSapNoteValue . entityVal) s) + sum (map (fromIntegral . walletSapNoteValue . entityVal) s) +
sum (map (fromIntegral . walletOrchNoteValue . entityVal) o) sum (map (fromIntegral . walletOrchNoteValue . entityVal) o)
prepTSpends :: prepTSpends ::
Maybe TransparentSpendingKey TransparentSpendingKey
-> [Entity WalletTrNote] -> [Entity WalletTrNote]
-> IO [TransparentTxSpend] -> IO [TransparentTxSpend]
prepTSpends sk' notes = do prepTSpends sk notes = do
case sk' of
Nothing -> return []
Just sk -> do
forM notes $ \n -> do forM notes $ \n -> do
tAddRead <- getAddressById pool $ walletTrNoteAddress $ entityVal n tAddRead <- getAddressById pool $ walletTrNoteAddress $ entityVal n
case tAddRead of case tAddRead of
@ -1601,8 +1222,7 @@ prepareTxV2 pool zebraHost zebraPort zn za bh pnotes policy = do
(walletAddressIndex $ entityVal tAdd) (walletAddressIndex $ entityVal tAdd)
(getScope $ walletAddressScope $ entityVal tAdd) (getScope $ walletAddressScope $ entityVal tAdd)
sk sk
mReverseTxId <- mReverseTxId <- getWalletTxId pool $ walletTrNoteTx $ entityVal n
getWalletTxId pool $ walletTrNoteTx $ entityVal n
case mReverseTxId of case mReverseTxId of
Nothing -> throwIO $ userError "failed to get tx ID" Nothing -> throwIO $ userError "failed to get tx ID"
Just (ESQ.Value reverseTxId) -> do Just (ESQ.Value reverseTxId) -> do
@ -1617,14 +1237,11 @@ prepareTxV2 pool zebraHost zebraPort zn za bh pnotes policy = do
(fromIntegral $ walletTrNoteValue $ entityVal n) (fromIntegral $ walletTrNoteValue $ entityVal n)
(walletTrNoteScript $ entityVal n)) (walletTrNoteScript $ entityVal n))
prepSSpends :: prepSSpends ::
Maybe SaplingSpendingKey SaplingSpendingKey
-> Tree SaplingNode -> Tree SaplingNode
-> [Entity WalletSapNote] -> [Entity WalletSapNote]
-> IO [SaplingTxSpend] -> IO [SaplingTxSpend]
prepSSpends sk' tree notes = do prepSSpends sk tree notes = do
case sk' of
Nothing -> return []
Just sk -> do
forM notes $ \n -> do forM notes $ \n -> do
let notePath = let notePath =
Zenith.Tree.path Zenith.Tree.path
@ -1642,14 +1259,11 @@ prepareTxV2 pool zebraHost zebraPort zn za bh pnotes policy = do
(getRseed $ walletSapNoteRseed $ entityVal n)) (getRseed $ walletSapNoteRseed $ entityVal n))
(fromMaybe nullPath notePath) (fromMaybe nullPath notePath)
prepOSpends :: prepOSpends ::
Maybe OrchardSpendingKey OrchardSpendingKey
-> Tree OrchardNode -> Tree OrchardNode
-> [Entity WalletOrchNote] -> [Entity WalletOrchNote]
-> IO [OrchardTxSpend] -> IO [OrchardTxSpend]
prepOSpends sk' tree notes = do prepOSpends sk tree notes = do
case sk' of
Nothing -> return []
Just sk -> do
forM notes $ \n -> do forM notes $ \n -> do
let notePath = let notePath =
Zenith.Tree.path Zenith.Tree.path
@ -1684,7 +1298,8 @@ syncWallet config w = do
mapM (liftIO . runNoLoggingT . getAddresses pool . entityKey) accs mapM (liftIO . runNoLoggingT . getAddresses pool . entityKey) accs
logDebugN $ "addrs: " <> T.pack (show addrs) logDebugN $ "addrs: " <> T.pack (show addrs)
intAddrs <- intAddrs <-
concat <$> mapM (liftIO . getInternalAddresses pool . entityKey) accs concat <$>
mapM (liftIO . runNoLoggingT . getInternalAddresses pool . entityKey) accs
chainTip <- liftIO $ getMaxBlock pool znet chainTip <- liftIO $ getMaxBlock pool znet
logDebugN $ "chain tip: " <> T.pack (show chainTip) logDebugN $ "chain tip: " <> T.pack (show chainTip)
lastBlock <- liftIO $ getLastSyncBlock pool $ entityKey w lastBlock <- liftIO $ getLastSyncBlock pool $ entityKey w

View file

@ -20,14 +20,13 @@ module Zenith.DB where
import Codec.Borsh import Codec.Borsh
import Control.Exception (SomeException(..), throw, throwIO, try) import Control.Exception (SomeException(..), throw, throwIO, try)
import Control.Monad (forM_, unless, when) import Control.Monad (unless, when)
import Control.Monad.IO.Class (MonadIO, liftIO) import Control.Monad.IO.Class (MonadIO, liftIO)
import Control.Monad.Logger import Control.Monad.Logger
( LoggingT ( LoggingT
, NoLoggingT , NoLoggingT
, logDebugN , logDebugN
, logErrorN , logErrorN
, logInfoN
, runNoLoggingT , runNoLoggingT
, runStderrLoggingT , runStderrLoggingT
) )
@ -53,7 +52,6 @@ import Haskoin.Transaction.Common
) )
import System.Directory (doesFileExist, getHomeDirectory, removeFile) import System.Directory (doesFileExist, getHomeDirectory, removeFile)
import System.FilePath ((</>)) import System.FilePath ((</>))
import ZcashHaskell.Keys (deriveUfvk, deriveUivk)
import ZcashHaskell.Orchard import ZcashHaskell.Orchard
( compareAddress ( compareAddress
, getSaplingFromUA , getSaplingFromUA
@ -67,7 +65,6 @@ import ZcashHaskell.Types
, OrchardBundle(..) , OrchardBundle(..)
, OrchardReceiver(..) , OrchardReceiver(..)
, OrchardWitness(..) , OrchardWitness(..)
, Phrase(..)
, SaplingAddress(..) , SaplingAddress(..)
, SaplingBundle(..) , SaplingBundle(..)
, SaplingReceiver(..) , SaplingReceiver(..)
@ -83,13 +80,11 @@ import ZcashHaskell.Types
, TxError(..) , TxError(..)
, UnifiedAddress(..) , UnifiedAddress(..)
, ValidAddress(..) , ValidAddress(..)
, ValidVk(..)
, ZcashNet(..) , ZcashNet(..)
) )
import Zenith.Tree (OrchardNode(..), SaplingNode(..), Tree(..), truncateTree) import Zenith.Tree (OrchardNode(..), SaplingNode(..), Tree(..), truncateTree)
import Zenith.Types import Zenith.Types
( AccountBalance(..) ( AccountBalance(..)
, AccountType(..)
, HexStringDB(..) , HexStringDB(..)
, OrchardSpendingKeyDB(..) , OrchardSpendingKeyDB(..)
, PhraseDB(..) , PhraseDB(..)
@ -97,10 +92,8 @@ import Zenith.Types
, RseedDB(..) , RseedDB(..)
, SaplingSpendingKeyDB(..) , SaplingSpendingKeyDB(..)
, ScopeDB(..) , ScopeDB(..)
, TransparentSpendingKeyDB(..) , TransparentSpendingKeyDB
, UnifiedAddressDB(..) , UnifiedAddressDB(..)
, UnifiedFvkDB(..)
, UnifiedIvkDB(..)
, ZcashAccountAPI(..) , ZcashAccountAPI(..)
, ZcashAddressAPI(..) , ZcashAddressAPI(..)
, ZcashNetDB(..) , ZcashNetDB(..)
@ -111,16 +104,6 @@ import Zenith.Types
, ZenithUuid(..) , ZenithUuid(..)
) )
share
[mkPersist sqlSettings, mkMigrate "schemaMigration"]
[persistLowerCase|
ZenithSchema
version Int
action T.Text
UniqueAction version action
deriving Show Eq
|]
share share
[mkPersist sqlSettings, mkMigrate "migrateAll"] [mkPersist sqlSettings, mkMigrate "migrateAll"]
[persistLowerCase| [persistLowerCase|
@ -130,19 +113,15 @@ share
seedPhrase PhraseDB seedPhrase PhraseDB
birthdayHeight Int birthdayHeight Int
lastSync Int default=0 lastSync Int default=0
local Bool default=TRUE
UniqueWallet name network UniqueWallet name network
deriving Show Eq deriving Show Eq
ZcashAccount ZcashAccount
index Int index Int
walletId ZcashWalletId walletId ZcashWalletId
name T.Text name T.Text
orchSpendKey OrchardSpendingKeyDB Maybe default=NULL orchSpendKey OrchardSpendingKeyDB
sapSpendKey SaplingSpendingKeyDB Maybe default=NULL sapSpendKey SaplingSpendingKeyDB
tPrivateKey TransparentSpendingKeyDB Maybe default=NULL tPrivateKey TransparentSpendingKeyDB
fvk UnifiedFvkDB Maybe default=NULL
ivk UnifiedIvkDB Maybe default=NULL
type AccountType default='Local'
UniqueAccount index walletId UniqueAccount index walletId
UniqueAccName walletId name UniqueAccName walletId name
deriving Show Eq deriving Show Eq
@ -346,7 +325,6 @@ toZcashWalletAPI w =
(getNet $ zcashWalletNetwork $ entityVal w) (getNet $ zcashWalletNetwork $ entityVal w)
(zcashWalletBirthdayHeight $ entityVal w) (zcashWalletBirthdayHeight $ entityVal w)
(zcashWalletLastSync $ entityVal w) (zcashWalletLastSync $ entityVal w)
(zcashWalletLocal $ entityVal w)
-- | @ZcashAccount@ -- | @ZcashAccount@
toZcashAccountAPI :: Entity ZcashAccount -> ZcashAccountAPI toZcashAccountAPI :: Entity ZcashAccount -> ZcashAccountAPI
@ -355,7 +333,6 @@ toZcashAccountAPI a =
(fromIntegral $ fromSqlKey $ entityKey a) (fromIntegral $ fromSqlKey $ entityKey a)
(fromIntegral $ fromSqlKey $ zcashAccountWalletId $ entityVal a) (fromIntegral $ fromSqlKey $ zcashAccountWalletId $ entityVal a)
(zcashAccountName $ entityVal a) (zcashAccountName $ entityVal a)
(zcashAccountType $ entityVal a)
-- | @WalletAddress@ -- | @WalletAddress@
toZcashAddressAPI :: Entity WalletAddress -> ZcashAddressAPI toZcashAddressAPI :: Entity WalletAddress -> ZcashAddressAPI
@ -448,45 +425,14 @@ orchToZcashNoteAPI pool n = do
-- | Initializes the database -- | Initializes the database
initDb :: initDb ::
T.Text -- ^ The database path to check T.Text -- ^ The database path to check
-> NoLoggingT IO (Either String Bool) -> IO (Either String Bool)
initDb dbName = do initDb dbName = do
x <-
liftIO
(try $ PS.runSqlite dbName $ runMigrationUnsafeQuiet schemaMigration :: IO
(Either SomeException [T.Text]))
case x of
Left _ -> do
logErrorN "Failed to initiate schema table"
return $ Left "Failed to initiate schema table"
Right _ -> do
pool <- liftIO $ runNoLoggingT $ initPool dbName
j <- j <-
liftIO try $ PS.runSqlite dbName $ runMigrationQuiet migrateAll :: IO
(try $ PS.runSqlite dbName $ runMigrationQuiet migrateAll :: IO (Either SomeException [T.Text])
(Either SomeException [T.Text]))
case j of case j of
Left e1 -> do Left _e1 -> do
logDebugN "Automatic migration failed, starting manual" pool <- runNoLoggingT $ initPool dbName
versions <- liftIO $ getVersions pool
migrateTables pool versions
PS.runSqlite dbName $ printMigration migrateAll
m <-
liftIO
(try $ PS.runSqlite dbName $ runMigration migrateAll :: IO
(Either SomeException ()))
case m of
Left e2 -> do
logErrorN $ "Failed to migrate data tables " <> T.pack (show e2)
return $ Left $ "Failed to migrate data tables" ++ show e2
Right _ -> do
logInfoN "Migration of tables successful"
return $ Right False
Right _ -> do
_ <-
runNoLoggingT $
PS.retryOnBusy $
flip PS.runSqlPool pool $ do
upsert (ZenithSchema 1 "Viewing Keys") []
wallets <- wallets <-
runNoLoggingT $ runNoLoggingT $
PS.retryOnBusy $ PS.retryOnBusy $
@ -495,20 +441,16 @@ initDb dbName = do
runNoLoggingT $ runNoLoggingT $
PS.retryOnBusy $ PS.retryOnBusy $
flip PS.runSqlPool pool $ do select . from $ table @ZcashAccount flip PS.runSqlPool pool $ do select . from $ table @ZcashAccount
addresses <-
runNoLoggingT $
PS.retryOnBusy $
flip PS.runSqlPool pool $ do select . from $ table @WalletAddress
abook <- abook <-
runNoLoggingT $ runNoLoggingT $
PS.retryOnBusy $ PS.retryOnBusy $
flip PS.runSqlPool pool $ do select . from $ table @AddressBook flip PS.runSqlPool pool $ do select . from $ table @AddressBook
hDir <- liftIO getHomeDirectory hDir <- getHomeDirectory
let backupDb = hDir </> "Zenith/.backup.db" let backupDb = hDir </> "Zenith/.backup.db"
checkDbFile <- liftIO $ doesFileExist backupDb checkDbFile <- doesFileExist backupDb
when checkDbFile $ liftIO $ removeFile backupDb when checkDbFile $ removeFile backupDb
_ <- PS.runSqlite (T.pack backupDb) $ runMigrationQuiet migrateAll _ <- PS.runSqlite (T.pack backupDb) $ runMigrationQuiet migrateAll
backupPool <- liftIO $ runNoLoggingT $ initPool $ T.pack backupDb backupPool <- runNoLoggingT $ initPool $ T.pack backupDb
_ <- _ <-
runNoLoggingT $ runNoLoggingT $
PS.retryOnBusy $ PS.retryOnBusy $
@ -521,86 +463,23 @@ initDb dbName = do
runNoLoggingT $ runNoLoggingT $
PS.retryOnBusy $ PS.retryOnBusy $
flip PS.runSqlPool backupPool $ insertMany_ $ entityVal <$> abook flip PS.runSqlPool backupPool $ insertMany_ $ entityVal <$> abook
_ <- clearWalletTransactions pool
runNoLoggingT $ clearWalletData pool
PS.retryOnBusy $ m <-
flip PS.runSqlPool backupPool $ try $ PS.runSqlite dbName $ runMigrationUnsafeQuiet migrateAll :: IO
insertMany_ $ entityVal <$> addresses (Either SomeException [T.Text])
case m of
Left e2 -> return $ Left $ "Failed to migrate data tables" ++ show e2
Right _ -> do
return $ Right True
Right _ -> do
return $ Right False return $ Right False
migrateTables :: ConnectionPool -> [Int] -> NoLoggingT IO ()
migrateTables pool versions = do
unless (1 `elem` versions) $ do
logDebugN "Making version 1 changes"
_ <-
runNoLoggingT $
PS.retryOnBusy $
flip PS.runSqlPool pool $ do
rawExecute
"ALTER TABLE \"zcash_wallet\" ADD COLUMN \"local\" BOOLEAN NOT NULL DEFAULT TRUE;"
[]
rawExecute
"ALTER TABLE \"zcash_account\" RENAME COLUMN \"orch_spend_key\" TO \"orch_spend_key_old\";"
[]
rawExecute
"ALTER TABLE \"zcash_account\" RENAME COLUMN \"sap_spend_key\" TO \"sap_spend_key_old\";"
[]
rawExecute
"ALTER TABLE \"zcash_account\" RENAME COLUMN \"t_private_key\" TO \"t_private_key_old\";"
[]
rawExecute
"ALTER TABLE \"zcash_account\" ADD COLUMN \"orch_spend_key\" VARCHAR NULL DEFAULT NULL;"
[]
rawExecute
"ALTER TABLE \"zcash_account\" ADD COLUMN \"sap_spend_key\" VARCHAR NULL DEFAULT NULL;"
[]
rawExecute
"ALTER TABLE \"zcash_account\" ADD COLUMN \"t_private_key\" VARCHAR NULL DEFAULT NULL;"
[]
rawExecute
"UPDATE \"zcash_account\" SET \"orch_spend_key\" = \"orch_spend_key_old\", \"sap_spend_key\" = \"sap_spend_key_old\", \"t_private_key\" = \"t_private_key_old\" WHERE 1=1;"
[]
rawExecute
"ALTER TABLE \"zcash_account\" DROP COLUMN \"orch_spend_key_old\";"
[]
rawExecute
"ALTER TABLE \"zcash_account\" DROP COLUMN \"sap_spend_key_old\";"
[]
rawExecute
"ALTER TABLE \"zcash_account\" DROP COLUMN \"t_private_key_old\";"
[]
rawExecute
"ALTER TABLE \"zcash_account\" ADD COLUMN \"fvk\" VARCHAR NULL DEFAULT NULL;"
[]
rawExecute
"ALTER TABLE \"zcash_account\" ADD COLUMN \"ivk\" VARCHAR NULL DEFAULT NULL;"
[]
rawExecute
"ALTER TABLE \"zcash_account\" ADD COLUMN \"type\" VARCHAR NOT NULL DEFAULT 'Local';"
[]
_ <-
runNoLoggingT $
PS.retryOnBusy $
flip PS.runSqlPool pool $ do upsert (ZenithSchema 1 "Viewing Keys") []
logDebugN "Version 1 changes complete"
initPool :: T.Text -> NoLoggingT IO ConnectionPool initPool :: T.Text -> NoLoggingT IO ConnectionPool
initPool dbPath = do initPool dbPath = do
let dbInfo = PS.mkSqliteConnectionInfo dbPath let dbInfo = PS.mkSqliteConnectionInfo dbPath
PS.createSqlitePoolFromInfo dbInfo 5 PS.createSqlitePoolFromInfo dbInfo 5
getVersions :: ConnectionPool -> IO [Int]
getVersions pool = do
versions <-
runNoLoggingT $
PS.retryOnBusy $
flip PS.runSqlPool pool $ do
select $ do
v <- from $ table @ZenithSchema
orderBy [asc $ v ^. ZenithSchemaVersion]
pure (v ^. ZenithSchemaVersion)
return $ map (\(Value x) -> x) versions
-- | Upgrade the database -- | Upgrade the database
upgradeDb :: upgradeDb ::
T.Text -- ^ database path T.Text -- ^ database path
@ -629,32 +508,6 @@ walletExists pool n =
where_ (wallets ^. ZcashWalletId ==. val (toSqlKey $ fromIntegral n)) where_ (wallets ^. ZcashWalletId ==. val (toSqlKey $ fromIntegral n))
pure wallets pure wallets
getVkWallet :: ConnectionPool -> IO (Maybe (Entity ZcashWallet))
getVkWallet pool = do
runNoLoggingT $
PS.retryOnBusy $
flip PS.runSqlPool pool $ do
selectOne $ do
wal <- from $ table @ZcashWallet
where_ (wal ^. ZcashWalletName ==. val "Viewing Keys")
where_ (wal ^. ZcashWalletLocal ==. val False)
pure wal
saveVkWallet ::
ConnectionPool -> ZcashNet -> Int -> IO (Maybe (Entity ZcashWallet))
saveVkWallet pool znet bh = do
runNoLoggingT $
PS.retryOnBusy $
flip PS.runSqlPool pool $
insertUniqueEntity $
ZcashWallet
"Viewing Keys"
(ZcashNetDB znet)
(PhraseDB $ Phrase "")
bh
0
False
getNetwork :: ConnectionPool -> WalletAddressId -> IO ZcashNet getNetwork :: ConnectionPool -> WalletAddressId -> IO ZcashNet
getNetwork pool a = do getNetwork pool a = do
n <- n <-
@ -707,23 +560,6 @@ getAccounts pool w =
where_ (accs ^. ZcashAccountWalletId ==. val w) where_ (accs ^. ZcashAccountWalletId ==. val w)
pure accs pure accs
-- | Returns a list of accounts with no viewing keys. For database migration purposes
getAccountsNoVKs ::
ConnectionPool -- ^ The database path
-> NoLoggingT IO [(Value ZcashNetDB, Entity ZcashAccount)]
getAccountsNoVKs pool =
PS.retryOnBusy $
flip PS.runSqlPool pool $ do
select $ do
(wallet :& acc) <-
from $ table @ZcashWallet `innerJoin` table @ZcashAccount `on`
(\(wallet :& acc) ->
wallet ^. ZcashWalletId ==. acc ^. ZcashAccountWalletId)
where_
(acc ^. ZcashAccountType ==. val Local &&.
isNothing (acc ^. ZcashAccountFvk))
pure (wallet ^. ZcashWalletNetwork, acc)
getAccountById :: getAccountById ::
ConnectionPool -> ZcashAccountId -> IO (Maybe (Entity ZcashAccount)) ConnectionPool -> ZcashAccountId -> IO (Maybe (Entity ZcashAccount))
getAccountById pool za = do getAccountById pool za = do
@ -794,7 +630,6 @@ getAddresses pool a =
addrs <- from $ table @WalletAddress addrs <- from $ table @WalletAddress
where_ (addrs ^. WalletAddressAccId ==. val a) where_ (addrs ^. WalletAddressAccId ==. val a)
where_ (addrs ^. WalletAddressScope ==. val (ScopeDB External)) where_ (addrs ^. WalletAddressScope ==. val (ScopeDB External))
orderBy [asc $ addrs ^. WalletAddressId]
pure addrs pure addrs
getAddressById :: getAddressById ::
@ -812,9 +647,8 @@ getAddressById pool a = do
getInternalAddresses :: getInternalAddresses ::
ConnectionPool -- ^ The database path ConnectionPool -- ^ The database path
-> ZcashAccountId -- ^ The account ID to check -> ZcashAccountId -- ^ The account ID to check
-> IO [Entity WalletAddress] -> NoLoggingT IO [Entity WalletAddress]
getInternalAddresses pool a = getInternalAddresses pool a =
runNoLoggingT $
PS.retryOnBusy $ PS.retryOnBusy $
flip PS.runSqlPool pool $ do flip PS.runSqlPool pool $ do
select $ do select $ do
@ -1075,36 +909,6 @@ upgradeQrTable pool = do
[PersistText "TransparentPool", PersistText "Transparent"] [PersistText "TransparentPool", PersistText "Transparent"]
return () return ()
upgradeAccountTable :: ConnectionPool -> NoLoggingT IO ()
upgradeAccountTable pool = do
accs <- liftIO $ runNoLoggingT $ getAccountsNoVKs pool
logDebugN $ T.pack $ show $ length accs
forM_ accs $ \(Value znet, a) -> do
FullVk b <-
liftIO $
deriveUfvk
(getNet znet)
(getOrchSK <$> zcashAccountOrchSpendKey (entityVal a))
(getSapSK <$> zcashAccountSapSpendKey (entityVal a))
(getTranSK <$> zcashAccountTPrivateKey (entityVal a))
IncomingVk c <-
liftIO $
deriveUivk
(getNet znet)
(getOrchSK <$> zcashAccountOrchSpendKey (entityVal a))
(getSapSK <$> zcashAccountSapSpendKey (entityVal a))
(getTranSK <$> zcashAccountTPrivateKey (entityVal a))
runNoLoggingT $
PS.retryOnBusy $
flip PS.runSqlPool pool $ do
update $ \w -> do
set
w
[ ZcashAccountFvk =. just (val $ UnifiedFvkDB b)
, ZcashAccountIvk =. just (val $ UnifiedIvkDB c)
]
where_ $ w ^. ZcashAccountId ==. val (entityKey a)
-- * Wallet -- * Wallet
-- | Get the block of the last transaction known to the wallet -- | Get the block of the last transaction known to the wallet
getMaxWalletBlock :: getMaxWalletBlock ::
@ -1632,8 +1436,7 @@ getWalletTransactions ::
-> NoLoggingT IO () -> NoLoggingT IO ()
getWalletTransactions pool w = do getWalletTransactions pool w = do
let w' = entityVal w let w' = entityVal w
chgAddr <- chgAddr <- getInternalAddresses pool $ walletAddressAccId $ entityVal w
liftIO $ getInternalAddresses pool $ walletAddressAccId $ entityVal w
let ctReceiver = t_rec =<< readUnifiedAddressDB (entityVal $ head chgAddr) let ctReceiver = t_rec =<< readUnifiedAddressDB (entityVal $ head chgAddr)
let csReceiver = s_rec =<< readUnifiedAddressDB (entityVal $ head chgAddr) let csReceiver = s_rec =<< readUnifiedAddressDB (entityVal $ head chgAddr)
let coReceiver = o_rec =<< readUnifiedAddressDB (entityVal $ head chgAddr) let coReceiver = o_rec =<< readUnifiedAddressDB (entityVal $ head chgAddr)

File diff suppressed because it is too large Load diff

View file

@ -13,14 +13,14 @@
module Zenith.RPC where module Zenith.RPC where
import Control.Concurrent (forkIO) import Control.Concurrent (forkIO)
import Control.Exception (SomeException(..), try) import Control.Exception (try)
import Control.Monad (unless) import Control.Monad (unless, when)
import Control.Monad.IO.Class (liftIO) import Control.Monad.IO.Class (liftIO)
import Control.Monad.Logger (runNoLoggingT, runStderrLoggingT) import Control.Monad.Logger (runFileLoggingT, runNoLoggingT, runStderrLoggingT)
import Data.Aeson import Data.Aeson
import qualified Data.HexString as H import qualified Data.HexString as H
import Data.Int import Data.Int
import Data.Scientific (Scientific(..), floatingOrInteger) import Data.Scientific (floatingOrInteger)
import qualified Data.Text as T import qualified Data.Text as T
import qualified Data.Text.Encoding as E import qualified Data.Text.Encoding as E
import Data.Time.Clock (getCurrentTime) import Data.Time.Clock (getCurrentTime)
@ -36,18 +36,12 @@ import Database.Esqueleto.Experimental
) )
import Servant import Servant
import Text.Read (readMaybe) import Text.Read (readMaybe)
import ZcashHaskell.Keys import ZcashHaskell.Keys (generateWalletSeedPhrase)
( deriveUfvk
, deriveUivk
, encodeVK
, generateWalletSeedPhrase
)
import ZcashHaskell.Orchard (parseAddress) import ZcashHaskell.Orchard (parseAddress)
import ZcashHaskell.Types import ZcashHaskell.Types
( BlockResponse(..) ( BlockResponse(..)
, RpcError(..) , RpcError(..)
, Scope(..) , Scope(..)
, ValidVk(..)
, ZcashNet(..) , ZcashNet(..)
, ZebraGetBlockChainInfo(..) , ZebraGetBlockChainInfo(..)
) )
@ -56,11 +50,7 @@ import Zenith.Core
( checkBlockChain ( checkBlockChain
, createCustomWalletAddress , createCustomWalletAddress
, createZcashAccount , createZcashAccount
, deshieldNotes
, importViewingKey
, parseVK
, prepareTxV2 , prepareTxV2
, shieldTransparentNotes
, syncWallet , syncWallet
, updateCommitmentTrees , updateCommitmentTrees
) )
@ -104,15 +94,11 @@ import Zenith.DB
import Zenith.Scanner (checkIntegrity, processTx, updateConfs) import Zenith.Scanner (checkIntegrity, processTx, updateConfs)
import Zenith.Types import Zenith.Types
( AccountBalance(..) ( AccountBalance(..)
, AccountType(..)
, Config(..) , Config(..)
, HexStringDB(..) , HexStringDB(..)
, OrchardSpendingKeyDB(..)
, PhraseDB(..) , PhraseDB(..)
, PrivacyPolicy(..) , PrivacyPolicy(..)
, ProposedNote(..) , ProposedNote(..)
, UnifiedFvkDB(..)
, UnifiedIvkDB(..)
, ZcashAccountAPI(..) , ZcashAccountAPI(..)
, ZcashAddressAPI(..) , ZcashAddressAPI(..)
, ZcashNetDB(..) , ZcashNetDB(..)
@ -135,11 +121,6 @@ data ZenithMethod
| GetNewAddress | GetNewAddress
| GetOperationStatus | GetOperationStatus
| SendMany | SendMany
| ShieldNotes
| DeshieldFunds
| GetFVK
| GetIVK
| ImportVK
| UnknownMethod | UnknownMethod
deriving (Eq, Prelude.Show) deriving (Eq, Prelude.Show)
@ -155,11 +136,6 @@ instance ToJSON ZenithMethod where
toJSON GetNewAddress = Data.Aeson.String "getnewaddress" toJSON GetNewAddress = Data.Aeson.String "getnewaddress"
toJSON GetOperationStatus = Data.Aeson.String "getoperationstatus" toJSON GetOperationStatus = Data.Aeson.String "getoperationstatus"
toJSON SendMany = Data.Aeson.String "sendmany" toJSON SendMany = Data.Aeson.String "sendmany"
toJSON ShieldNotes = Data.Aeson.String "shieldnotes"
toJSON DeshieldFunds = Data.Aeson.String "deshieldfunds"
toJSON GetFVK = Data.Aeson.String "getfullvk"
toJSON GetIVK = Data.Aeson.String "getincomingvk"
toJSON ImportVK = Data.Aeson.String "importvk"
toJSON UnknownMethod = Data.Aeson.Null toJSON UnknownMethod = Data.Aeson.Null
instance FromJSON ZenithMethod where instance FromJSON ZenithMethod where
@ -176,11 +152,6 @@ instance FromJSON ZenithMethod where
"getnewaddress" -> pure GetNewAddress "getnewaddress" -> pure GetNewAddress
"getoperationstatus" -> pure GetOperationStatus "getoperationstatus" -> pure GetOperationStatus
"sendmany" -> pure SendMany "sendmany" -> pure SendMany
"shieldnotes" -> pure ShieldNotes
"deshieldfunds" -> pure DeshieldFunds
"getfullvk" -> pure GetFVK
"getincomingvk" -> pure GetIVK
"importvk" -> pure ImportVK
_ -> pure UnknownMethod _ -> pure UnknownMethod
data ZenithParams data ZenithParams
@ -196,10 +167,6 @@ data ZenithParams
| OpParams !ZenithUuid | OpParams !ZenithUuid
| SendParams !Int ![ProposedNote] !PrivacyPolicy | SendParams !Int ![ProposedNote] !PrivacyPolicy
| TestParams !T.Text | TestParams !T.Text
| ShieldNotesParams !Int
| DeshieldParams !Int !Scientific
| ViewingKeyParams !Int
| ImportVkParams !T.Text !T.Text !Int
deriving (Eq, Prelude.Show) deriving (Eq, Prelude.Show)
instance ToJSON ZenithParams where instance ToJSON ZenithParams where
@ -224,13 +191,6 @@ instance ToJSON ZenithParams where
Data.Aeson.Array $ V.fromList [Data.Aeson.String $ U.toText $ getUuid i] Data.Aeson.Array $ V.fromList [Data.Aeson.String $ U.toText $ getUuid i]
toJSON (SendParams i ns p) = toJSON (SendParams i ns p) =
Data.Aeson.Array $ V.fromList [jsonNumber i, toJSON ns, toJSON p] Data.Aeson.Array $ V.fromList [jsonNumber i, toJSON ns, toJSON p]
toJSON (ShieldNotesParams i) = Data.Aeson.Array $ V.fromList [jsonNumber i]
toJSON (DeshieldParams i s) =
Data.Aeson.Array $ V.fromList [jsonNumber i, Data.Aeson.Number s]
toJSON (ViewingKeyParams i) = Data.Aeson.Array $ V.fromList [jsonNumber i]
toJSON (ImportVkParams n k b) =
Data.Aeson.Array $
V.fromList [Data.Aeson.String n, Data.Aeson.String k, jsonNumber b]
data ZenithResponse data ZenithResponse
= InfoResponse !T.Text !ZenithInfo = InfoResponse !T.Text !ZenithInfo
@ -243,8 +203,6 @@ data ZenithResponse
| NewAddrResponse !T.Text !ZcashAddressAPI | NewAddrResponse !T.Text !ZcashAddressAPI
| OpResponse !T.Text !Operation | OpResponse !T.Text !Operation
| SendResponse !T.Text !U.UUID | SendResponse !T.Text !U.UUID
| MultiOpResponse !T.Text ![T.Text]
| ViewingKeyResponse !T.Text !T.Text
| ErrorResponse !T.Text !Double !T.Text | ErrorResponse !T.Text !Double !T.Text
deriving (Eq, Prelude.Show) deriving (Eq, Prelude.Show)
@ -266,8 +224,6 @@ instance ToJSON ZenithResponse where
toJSON (NewAddrResponse i a) = packRpcResponse i a toJSON (NewAddrResponse i a) = packRpcResponse i a
toJSON (OpResponse i u) = packRpcResponse i u toJSON (OpResponse i u) = packRpcResponse i u
toJSON (SendResponse i o) = packRpcResponse i o toJSON (SendResponse i o) = packRpcResponse i o
toJSON (MultiOpResponse i o) = packRpcResponse i o
toJSON (ViewingKeyResponse i k) = packRpcResponse i k
instance FromJSON ZenithResponse where instance FromJSON ZenithResponse where
parseJSON = parseJSON =
@ -342,9 +298,6 @@ instance FromJSON ZenithResponse where
k5 <- parseJSON r1 k5 <- parseJSON r1
pure $ NoteListResponse i k5 pure $ NoteListResponse i k5
Nothing -> fail "Unknown object" Nothing -> fail "Unknown object"
String s -> do
k7 <- parseJSON r1
pure $ MultiOpResponse i k7
_anyOther -> fail "Malformed JSON" _anyOther -> fail "Malformed JSON"
Number k -> do Number k -> do
case floatingOrInteger k of case floatingOrInteger k of
@ -352,7 +305,7 @@ instance FromJSON ZenithResponse where
Right k' -> pure $ NewItemResponse i k' Right k' -> pure $ NewItemResponse i k'
String s -> do String s -> do
case U.fromText s of case U.fromText s of
Nothing -> pure $ ViewingKeyResponse i s Nothing -> fail "Unknown value"
Just u -> pure $ SendResponse i u Just u -> pure $ SendResponse i u
_anyOther -> fail "Malformed JSON" _anyOther -> fail "Malformed JSON"
Just e1 -> pure $ ErrorResponse i (ecode e1) (emessage e1) Just e1 -> pure $ ErrorResponse i (ecode e1) (emessage e1)
@ -536,59 +489,6 @@ instance FromJSON RpcCall where
_anyOther -> pure $ RpcCall v i SendMany BadParams _anyOther -> pure $ RpcCall v i SendMany BadParams
else pure $ RpcCall v i SendMany BadParams else pure $ RpcCall v i SendMany BadParams
_anyOther -> pure $ RpcCall v i SendMany BadParams _anyOther -> pure $ RpcCall v i SendMany BadParams
ShieldNotes -> do
p <- obj .: "params"
case p of
Array a ->
if V.length a == 1
then do
x <- parseJSON $ a V.! 0
pure $ RpcCall v i ShieldNotes (ShieldNotesParams x)
else pure $ RpcCall v i ShieldNotes BadParams
_anyOther -> pure $ RpcCall v i ShieldNotes BadParams
DeshieldFunds -> do
p <- obj .: "params"
case p of
Array a ->
if V.length a == 2
then do
x <- parseJSON $ a V.! 0
y <- parseJSON $ a V.! 1
pure $ RpcCall v i DeshieldFunds (DeshieldParams x y)
else pure $ RpcCall v i DeshieldFunds BadParams
_anyOther -> pure $ RpcCall v i DeshieldFunds BadParams
GetFVK -> do
p <- obj .: "params"
case p of
Array a ->
if V.length a == 1
then do
x <- parseJSON $ a V.! 0
pure $ RpcCall v i GetFVK (ViewingKeyParams x)
else pure $ RpcCall v i GetFVK BadParams
_anyOther -> pure $ RpcCall v i GetFVK BadParams
GetIVK -> do
p <- obj .: "params"
case p of
Array a ->
if V.length a == 1
then do
x <- parseJSON $ a V.! 0
pure $ RpcCall v i GetIVK (ViewingKeyParams x)
else pure $ RpcCall v i GetIVK BadParams
_anyOther -> pure $ RpcCall v i GetIVK BadParams
ImportVK -> do
p <- obj .: "params"
case p of
Array a ->
if V.length a == 3
then do
x <- parseJSON $ a V.! 0
y <- parseJSON $ a V.! 1
z <- parseJSON $ a V.! 2
pure $ RpcCall v i ImportVK (ImportVkParams x y z)
else pure $ RpcCall v i ImportVK BadParams
_anyOther -> pure $ RpcCall v i ImportVK BadParams
type ZenithRPC type ZenithRPC
= "status" :> Get '[ JSON] Value :<|> BasicAuth "zenith-realm" Bool :> ReqBody = "status" :> Get '[ JSON] Value :<|> BasicAuth "zenith-realm" Bool :> ReqBody
@ -611,7 +511,7 @@ zenithServer state = getinfo :<|> handleRPC
getinfo = getinfo =
return $ return $
object object
[ "version" .= ("0.9.1.0-beta" :: String) [ "version" .= ("0.7.0.0-beta" :: String)
, "network" .= ("testnet" :: String) , "network" .= ("testnet" :: String)
] ]
handleRPC :: Bool -> RpcCall -> Handler ZenithResponse handleRPC :: Bool -> RpcCall -> Handler ZenithResponse
@ -687,7 +587,7 @@ zenithServer state = getinfo :<|> handleRPC
return $ return $
InfoResponse InfoResponse
(callId req) (callId req)
(ZenithInfo "0.9.1.0-beta" (w_network state) (w_build state)) (ZenithInfo "0.7.0.0-beta" (w_network state) (w_build state))
_anyOtherParams -> _anyOtherParams ->
return $ ErrorResponse (callId req) (-32602) "Invalid params" return $ ErrorResponse (callId req) (-32602) "Invalid params"
ListReceived -> ListReceived ->
@ -734,12 +634,9 @@ zenithServer state = getinfo :<|> handleRPC
acc <- liftIO $ getAccountById pool $ toSqlKey i acc <- liftIO $ getAccountById pool $ toSqlKey i
case acc of case acc of
Just acc' -> do Just acc' -> do
if zcashAccountType (entityVal acc') /= IncomingViewKey
then do
c <- liftIO $ getPoolBalance pool $ entityKey acc' c <- liftIO $ getPoolBalance pool $ entityKey acc'
u <- liftIO $ getUnconfPoolBalance pool $ entityKey acc' u <- liftIO $ getUnconfPoolBalance pool $ entityKey acc'
return $ BalanceResponse (callId req) c u return $ BalanceResponse (callId req) c u
else return $ readOnlyError $ callId req
Nothing -> Nothing ->
return $ return $
ErrorResponse (callId req) (-32006) "Account does not exist." ErrorResponse (callId req) (-32006) "Account does not exist."
@ -768,7 +665,6 @@ zenithServer state = getinfo :<|> handleRPC
(PhraseDB sP) (PhraseDB sP)
(w_startBlock state) (w_startBlock state)
0 0
True
case r of case r of
Nothing -> Nothing ->
return $ return $
@ -798,21 +694,14 @@ zenithServer state = getinfo :<|> handleRPC
case w of case w of
Just w' -> do Just w' -> do
aIdx <- liftIO $ getMaxAccount pool $ entityKey w' aIdx <- liftIO $ getMaxAccount pool $ entityKey w'
if zcashWalletLocal $ entityVal w'
then do
nAcc <- nAcc <-
liftIO liftIO
(try $ (try $ createZcashAccount t (aIdx + 1) w' :: IO
createZcashAccount (Either IOError ZcashAccount))
t
(aIdx + 1)
(getNet $ zcashWalletNetwork $ entityVal w')
w' :: IO (Either IOError ZcashAccount))
case nAcc of case nAcc of
Left e -> Left e ->
return $ return $
ErrorResponse (callId req) (-32010) $ ErrorResponse (callId req) (-32010) $ T.pack $ show e
T.pack $ show e
Right nAcc' -> do Right nAcc' -> do
r <- liftIO $ saveAccount pool nAcc' r <- liftIO $ saveAccount pool nAcc'
case r of case r of
@ -826,7 +715,6 @@ zenithServer state = getinfo :<|> handleRPC
return $ return $
NewItemResponse (callId req) $ NewItemResponse (callId req) $
fromSqlKey $ entityKey x fromSqlKey $ entityKey x
else return $ notLocalError $ callId req
Nothing -> Nothing ->
return $ return $
ErrorResponse ErrorResponse
@ -936,8 +824,6 @@ zenithServer state = getinfo :<|> handleRPC
liftIO $ getAccountById pool $ toSqlKey $ fromIntegral a liftIO $ getAccountById pool $ toSqlKey $ fromIntegral a
case acc of case acc of
Just acc' -> do Just acc' -> do
if zcashAccountType (entityVal acc') == Local
then do
bl <- bl <-
liftIO $ liftIO $
getLastSyncBlock getLastSyncBlock
@ -958,141 +844,6 @@ zenithServer state = getinfo :<|> handleRPC
bl bl
ns ns
p p
case res of
Left e ->
finalizeOperation pool opkey' Failed $
T.pack $ show e
Right rawTx -> do
zebraRes <-
makeZebraCall
zHost
zPort
"sendrawtransaction"
[Data.Aeson.String $ H.toText rawTx]
case zebraRes of
Left e1 ->
finalizeOperation pool opkey' Failed $
T.pack $ show e1
Right txId ->
finalizeOperation
pool
opkey'
Successful $
"Tx ID: " <> H.toText txId
return $ SendResponse (callId req) opid
else return $ readOnlyError $ callId req
Nothing ->
return $
ErrorResponse
(callId req)
(-32006)
"Account does not exist."
_anyOtherParams ->
return $ ErrorResponse (callId req) (-32602) "Invalid params"
ShieldNotes -> do
case parameters req of
ShieldNotesParams i -> do
let dbPath = w_dbPath state
let net = w_network state
let zHost = w_host state
let zPort = w_port state
pool <- liftIO $ runNoLoggingT $ initPool dbPath
syncChk <- liftIO $ isSyncing pool
if syncChk
then return $
ErrorResponse
(callId req)
(-32012)
"The Zenith server is syncing, please try again later."
else do
acc <-
liftIO $ getAccountById pool $ toSqlKey $ fromIntegral i
case acc of
Just acc' -> do
bl <-
liftIO $
getLastSyncBlock
pool
(zcashAccountWalletId $ entityVal acc')
opids <-
liftIO $
runNoLoggingT $
shieldTransparentNotes
pool
zHost
zPort
net
(entityKey acc')
bl
let ops =
map
(\case
Left e -> T.pack $ show e
Right op -> U.toText op)
opids
return $ MultiOpResponse (callId req) ops
Nothing ->
return $
ErrorResponse
(callId req)
(-32006)
"Account does not exist."
_anyOtherParams ->
return $ ErrorResponse (callId req) (-32602) "Invalid params"
DeshieldFunds -> do
case parameters req of
DeshieldParams i k -> do
let dbPath = w_dbPath state
let net = w_network state
let zHost = w_host state
let zPort = w_port state
pool <- liftIO $ runNoLoggingT $ initPool dbPath
syncChk <- liftIO $ isSyncing pool
if syncChk
then return $
ErrorResponse
(callId req)
(-32012)
"The Zenith server is syncing, please try again later."
else do
opid <- liftIO nextRandom
startTime <- liftIO getCurrentTime
opkey <-
liftIO $
saveOperation pool $
Operation
(ZenithUuid opid)
startTime
Nothing
Processing
Nothing
case opkey of
Nothing ->
return $
ErrorResponse (callId req) (-32010) "Internal Error"
Just opkey' -> do
acc <-
liftIO $ getAccountById pool $ toSqlKey $ fromIntegral i
case acc of
Just acc' -> do
bl <-
liftIO $
getLastSyncBlock
pool
(zcashAccountWalletId $ entityVal acc')
_ <-
liftIO $
forkIO $ do
res <-
runNoLoggingT $
deshieldNotes
pool
zHost
zPort
net
(entityKey acc')
bl
k
case res of case res of
Left e -> Left e ->
finalizeOperation pool opkey' Failed $ finalizeOperation pool opkey' Failed $
@ -1120,65 +871,6 @@ zenithServer state = getinfo :<|> handleRPC
"Account does not exist." "Account does not exist."
_anyOtherParams -> _anyOtherParams ->
return $ ErrorResponse (callId req) (-32602) "Invalid params" return $ ErrorResponse (callId req) (-32602) "Invalid params"
GetFVK -> do
case parameters req of
ViewingKeyParams aid -> do
let dbPath = w_dbPath state
pool <- liftIO $ runNoLoggingT $ initPool dbPath
acc <- liftIO $ getAccountById pool $ toSqlKey $ fromIntegral aid
case acc of
Just acc' -> do
case zcashAccountFvk (entityVal acc') of
Nothing ->
return $
ErrorResponse (callId req) (-32010) "Internal Error"
Just fvk ->
return $
ViewingKeyResponse
(callId req)
(encodeVK (FullVk $ getFvk fvk))
Nothing ->
return $
ErrorResponse (callId req) (-32006) "Account does not exist."
_anyOtherParams ->
return $ ErrorResponse (callId req) (-32602) "Invalid params"
GetIVK -> do
case parameters req of
ViewingKeyParams aid -> do
let dbPath = w_dbPath state
pool <- liftIO $ runNoLoggingT $ initPool dbPath
acc <- liftIO $ getAccountById pool $ toSqlKey $ fromIntegral aid
case acc of
Just acc' -> do
case zcashAccountIvk (entityVal acc') of
Nothing ->
return $
ErrorResponse (callId req) (-32010) "Internal Error"
Just ivk ->
return $
ViewingKeyResponse
(callId req)
(encodeVK $ IncomingVk $ getIvk ivk)
Nothing ->
return $
ErrorResponse (callId req) (-32006) "Account does not exist."
_anyOtherParams ->
return $ ErrorResponse (callId req) (-32602) "Invalid params"
ImportVK -> do
case parameters req of
ImportVkParams n k b -> do
let dbPath = w_dbPath state
let znet = w_network state
pool <- liftIO $ runNoLoggingT $ initPool dbPath
case parseVK k of
Nothing -> return $ invalidVkError $ callId req
Just vk -> do
res <- liftIO $ importViewingKey pool n znet vk b
case res of
Left e -> return $ ErrorResponse (callId req) (-32010) e
Right x -> return $ NewItemResponse (callId req) x
_anyOtherParams ->
return $ ErrorResponse (callId req) (-32602) "Invalid params"
authenticate :: Config -> BasicAuthCheck Bool authenticate :: Config -> BasicAuthCheck Bool
authenticate config = BasicAuthCheck check authenticate config = BasicAuthCheck check
@ -1224,7 +916,7 @@ scanZebra dbPath zHost zPort net = do
updateCommitmentTrees pool zHost zPort $ ZcashNetDB net updateCommitmentTrees pool zHost zPort $ ZcashNetDB net
runNoLoggingT $ runNoLoggingT $
mapM_ mapM_
(syncWallet (Config dbPath zHost zPort "user" "pwd" 8080 "usd")) (syncWallet (Config dbPath zHost zPort "user" "pwd" 8080))
wals wals
_ <- completeSync pool Successful _ <- completeSync pool Successful
return () return ()
@ -1240,28 +932,22 @@ scanZebra dbPath zHost zPort net = do
case r of case r of
Left _ -> completeSync pool Failed Left _ -> completeSync pool Failed
Right blk -> do Right blk -> do
r2 <-
makeZebraCall
zHost
zPort
"getblock"
[Data.Aeson.String $ T.pack (show bl), jsonNumber 0]
case r2 of
Left _ -> completeSync pool Failed
Right hb -> do
let blockTime = getBlockTime hb
bi <- bi <-
saveBlock pool $ saveBlock pool $
ZcashBlock ZcashBlock
(fromIntegral $ bl_height blk) (fromIntegral $ bl_height blk)
(HexStringDB $ bl_hash blk) (HexStringDB $ bl_hash blk)
(fromIntegral $ bl_confirmations blk) (fromIntegral $ bl_confirmations blk)
(fromIntegral $ bl_time blk) blockTime
(ZcashNetDB net) (ZcashNetDB net)
mapM_ (processTx zHost zPort bi pool) $ bl_txs blk mapM_ (processTx zHost zPort bi pool) $ bl_txs blk
-- * Errors
invalidVkError :: T.Text -> ZenithResponse
invalidVkError i =
ErrorResponse i (-32013) "The viewing key provided is not valid."
readOnlyError :: T.Text -> ZenithResponse
readOnlyError i =
ErrorResponse i (-32014) "Read-only account, operation is not valid."
notLocalError :: T.Text -> ZenithResponse
notLocalError i =
ErrorResponse
i
(-32015)
"The wallet is not local, cannot create new accounts."

View file

@ -50,7 +50,6 @@ import Zenith.DB
, saveTransaction , saveTransaction
, startSync , startSync
, updateWalletSync , updateWalletSync
, upgradeAccountTable
, upgradeQrTable , upgradeQrTable
) )
import Zenith.Types import Zenith.Types
@ -78,7 +77,7 @@ rescanZebra host port dbFilePath = do
pool1 <- runNoLoggingT $ initPool dbFilePath pool1 <- runNoLoggingT $ initPool dbFilePath
{-pool2 <- runNoLoggingT $ initPool dbFilePath-} {-pool2 <- runNoLoggingT $ initPool dbFilePath-}
{-pool3 <- runNoLoggingT $ initPool dbFilePath-} {-pool3 <- runNoLoggingT $ initPool dbFilePath-}
_ <- runNoLoggingT $ initDb dbFilePath _ <- initDb dbFilePath
upgradeQrTable pool1 upgradeQrTable pool1
clearWalletTransactions pool1 clearWalletTransactions pool1
clearWalletData pool1 clearWalletData pool1
@ -134,13 +133,26 @@ processBlock host port pool pg net b = do
_ <- completeSync pool Failed _ <- completeSync pool Failed
liftIO $ throwIO $ userError e liftIO $ throwIO $ userError e
Right blk -> do Right blk -> do
r2 <-
liftIO $
makeZebraCall
host
port
"getblock"
[Data.Aeson.String $ T.pack $ show b, jsonNumber 0]
case r2 of
Left e2 -> do
_ <- completeSync pool Failed
liftIO $ throwIO $ userError e2
Right hb -> do
let blockTime = getBlockTime hb
bi <- bi <-
saveBlock pool $ saveBlock pool $
ZcashBlock ZcashBlock
(fromIntegral $ bl_height blk) (fromIntegral $ bl_height blk)
(HexStringDB $ bl_hash blk) (HexStringDB $ bl_hash blk)
(fromIntegral $ bl_confirmations blk) (fromIntegral $ bl_confirmations blk)
(fromIntegral $ bl_time blk) blockTime
net net
mapM_ (processTx host port bi pool) $ bl_txs blk mapM_ (processTx host port bi pool) $ bl_txs blk
liftIO $ tick pg liftIO $ tick pg
@ -217,9 +229,8 @@ clearSync config = do
case bc of case bc of
Left e1 -> throwIO e1 Left e1 -> throwIO e1
Right chainInfo -> do Right chainInfo -> do
x <- runNoLoggingT $ initDb dbPath x <- initDb dbPath
_ <- upgradeQrTable pool _ <- upgradeQrTable pool
_ <- runNoLoggingT $ upgradeAccountTable pool
case x of case x of
Left e2 -> throwIO $ userError e2 Left e2 -> throwIO $ userError e2
Right x' -> do Right x' -> do

View file

@ -40,8 +40,6 @@ import ZcashHaskell.Types
, Scope(..) , Scope(..)
, TransparentAddress(..) , TransparentAddress(..)
, TransparentSpendingKey , TransparentSpendingKey
, UnifiedFullViewingKey(..)
, UnifiedIncomingViewingKey(..)
, ValidAddress(..) , ValidAddress(..)
, ZcashNet(..) , ZcashNet(..)
) )
@ -105,41 +103,6 @@ newtype RseedDB = RseedDB
derivePersistField "RseedDB" derivePersistField "RseedDB"
newtype UnifiedFvkDB = UnifiedFvkDB
{ getFvk :: UnifiedFullViewingKey
} deriving newtype (Eq, Show, Read)
derivePersistField "UnifiedFvkDB"
newtype UnifiedIvkDB = UnifiedIvkDB
{ getIvk :: UnifiedIncomingViewingKey
} deriving newtype (Eq, Show, Read)
derivePersistField "UnifiedIvkDB"
data AccountType
= Local
| FullViewKey
| IncomingViewKey
deriving (Eq, Show, Read)
derivePersistField "AccountType"
instance ToJSON AccountType where
toJSON at =
case at of
Local -> Data.Aeson.String "Local"
FullViewKey -> Data.Aeson.String "FullViewKey"
IncomingViewKey -> Data.Aeson.String "IncomingViewKey"
instance FromJSON AccountType where
parseJSON =
withText "AccountType" $ \case
"Local" -> return Local
"FullViewKey" -> return FullViewKey
"IncomingViewKey" -> return IncomingViewKey
_ -> fail "Not a valid Account type"
-- * RPC -- * RPC
-- | Type for Configuration parameters -- | Type for Configuration parameters
data Config = Config data Config = Config
@ -149,7 +112,6 @@ data Config = Config
, c_zenithUser :: !BS.ByteString , c_zenithUser :: !BS.ByteString
, c_zenithPwd :: !BS.ByteString , c_zenithPwd :: !BS.ByteString
, c_zenithPort :: !Int , c_zenithPort :: !Int
, c_currencyCode :: !T.Text
} deriving (Eq, Prelude.Show) } deriving (Eq, Prelude.Show)
data ZcashPool data ZcashPool
@ -191,7 +153,6 @@ data ZcashWalletAPI = ZcashWalletAPI
, zw_network :: !ZcashNet , zw_network :: !ZcashNet
, zw_birthday :: !Int , zw_birthday :: !Int
, zw_lastSync :: !Int , zw_lastSync :: !Int
, zw_local :: !Bool
} deriving (Eq, Prelude.Show) } deriving (Eq, Prelude.Show)
$(deriveJSON defaultOptions {fieldLabelModifier = drop 3} ''ZcashWalletAPI) $(deriveJSON defaultOptions {fieldLabelModifier = drop 3} ''ZcashWalletAPI)
@ -200,7 +161,6 @@ data ZcashAccountAPI = ZcashAccountAPI
{ za_index :: !Int { za_index :: !Int
, za_wallet :: !Int , za_wallet :: !Int
, za_name :: !T.Text , za_name :: !T.Text
, za_type :: !AccountType
} deriving (Eq, Prelude.Show) } deriving (Eq, Prelude.Show)
$(deriveJSON defaultOptions {fieldLabelModifier = drop 3} ''ZcashAccountAPI) $(deriveJSON defaultOptions {fieldLabelModifier = drop 3} ''ZcashAccountAPI)
@ -547,19 +507,3 @@ encodeHexText' t =
if T.length t > 0 if T.length t > 0
then C.unpack . B64.encode $ E.encodeUtf8 t then C.unpack . B64.encode $ E.encodeUtf8 t
else C.unpack . B64.encode $ E.encodeUtf8 "Sent from Zenith" else C.unpack . B64.encode $ E.encodeUtf8 "Sent from Zenith"
-- | Define a data structure for the parsed components
data ZcashPaymentURI = ZcashPaymentURI
{ uriAddress :: String
, uriAmount :: Maybe Double
, uriMemo :: T.Text
, uriLabel :: Maybe String
, uriMessage :: Maybe String
} deriving (Show, Eq)
-- | Define a data structure for the URI QR image
data URIQrCode = URIQrCode
{ uriBytes :: BS.ByteString -- Image as ByteString
, uriWidth :: Double -- Number of columns in QR Image
, uriHeight :: Double -- Number of rows in a QR Image
} deriving (Show, Eq)

View file

@ -2,32 +2,16 @@
module Zenith.Utils where module Zenith.Utils where
import Control.Exception (SomeException, try)
import Control.Monad (when)
import Data.Aeson import Data.Aeson
import qualified Data.Aeson.Key as K
import qualified Data.Aeson.KeyMap as KM
import Data.Aeson.Types (parseMaybe)
import qualified Data.ByteString as BS
import qualified Data.ByteString.Base64 as B64
import qualified Data.ByteString.Char8 as BC
import qualified Data.ByteString.Lazy as B
import qualified Data.ByteString.Lazy.Char8 as BL
import Data.Char (isAlphaNum, isSpace) import Data.Char (isAlphaNum, isSpace)
import Data.Functor (void) import Data.Functor (void)
import Data.Maybe import Data.Maybe
import Data.Ord (clamp) import Data.Ord (clamp)
import Data.Scientific (Scientific(..), Scientific, scientific, toRealFloat) import Data.Scientific (Scientific(..), scientific)
import qualified Data.Text as T import qualified Data.Text as T
import qualified Data.Text.Encoding as E import qualified Data.Text.Encoding as E
--import qualified Data.Text.Encoding as TE
import Network.HTTP.Simple
import Network.URI (escapeURIString, isUnreserved)
import System.Directory import System.Directory
import System.Process (createProcess_, shell) import System.Process (createProcess_, shell)
import Text.Printf (printf)
import Text.Read (readMaybe)
import Text.Regex.Posix import Text.Regex.Posix
import ZcashHaskell.Orchard import ZcashHaskell.Orchard
( encodeUnifiedAddress ( encodeUnifiedAddress
@ -41,13 +25,10 @@ import ZcashHaskell.Transparent
) )
import ZcashHaskell.Types import ZcashHaskell.Types
( ExchangeAddress(..) ( ExchangeAddress(..)
, ExchangeAddress(..)
, Phrase(..)
, SaplingAddress(..) , SaplingAddress(..)
, TransparentAddress(..) , TransparentAddress(..)
, UnifiedAddress(..) , UnifiedAddress(..)
, ValidAddress(..) , ValidAddress(..)
, ValidAddress(..)
, ZcashNet(..) , ZcashNet(..)
) )
import ZcashHaskell.Utils (makeZebraCall) import ZcashHaskell.Utils (makeZebraCall)
@ -56,7 +37,6 @@ import Zenith.Types
, PrivacyPolicy(..) , PrivacyPolicy(..)
, UnifiedAddressDB(..) , UnifiedAddressDB(..)
, ZcashAddress(..) , ZcashAddress(..)
, ZcashPaymentURI(..)
, ZcashPool(..) , ZcashPool(..)
) )
@ -72,7 +52,7 @@ displayZec s
| abs s < 100000000 = show (fromIntegral s / 100000) ++ " mZEC" | abs s < 100000000 = show (fromIntegral s / 100000) ++ " mZEC"
| otherwise = show (fromIntegral s / 100000000) ++ " ZEC " | otherwise = show (fromIntegral s / 100000000) ++ " ZEC "
-- | Helper function to display small amounts of TAZ -- | Helper function to display small amounts of ZEC
displayTaz :: Integer -> String displayTaz :: Integer -> String
displayTaz s displayTaz s
| abs s < 100 = show s ++ " tazs" | abs s < 100 = show s ++ " tazs"
@ -255,7 +235,7 @@ isValidString c = do
padWithZero :: Int -> String -> String padWithZero :: Int -> String -> String
padWithZero n s padWithZero n s
| length s >= n = s | (length s) >= n = s
| otherwise = padWithZero n ("0" ++ s) | otherwise = padWithZero n ("0" ++ s)
isEmpty :: [a] -> Bool isEmpty :: [a] -> Bool
@ -268,119 +248,3 @@ getChainTip zHost zPort = do
case r of case r of
Left e1 -> pure 0 Left e1 -> pure 0
Right i -> pure i Right i -> pure i
-- Function to fetch Zcash price from CoinGecko
getZcashPrice :: T.Text -> IO (Maybe Double)
getZcashPrice currency = do
let url =
"https://api.coingecko.com/api/v3/simple/price?ids=zcash&vs_currencies=" <>
T.unpack currency
response <- httpJSONEither (parseRequest_ url)
case getResponseBody response of
Right (Object obj)
-- Extract "zcash" object
-> do
case KM.lookup "zcash" obj of
Just (Object zcashObj)
-- Extract the currency price
->
case KM.lookup (K.fromText (T.toLower currency)) zcashObj of
Just (Number price) -> return (Just (toRealFloat price))
_ -> return Nothing
_ -> return Nothing
_ -> return Nothing
-- Parse memo result to convert it to a ByteString
processEither :: Either String BC.ByteString -> BC.ByteString
processEither (Right bs) = bs
processEither (Left e) = BC.pack e -- Returns the error message
-- Parse the query string into key-value pairs
parseQuery :: String -> [(String, String)]
parseQuery query = map (breakOn '=') (splitOn '&' query)
where
splitOn :: Char -> String -> [String]
splitOn _ [] = [""]
splitOn delim (c:cs)
| c == delim = "" : rest
| otherwise = (c : head rest) : tail rest
where
rest = splitOn delim cs
breakOn :: Char -> String -> (String, String)
breakOn delim str = (key, drop 1 value)
where
(key, value) = span (/= delim) str
-- Parse a ZIP-321 encoded string into a ZcashPayment structure
parseZcashPayment :: String -> Either String ZcashPaymentURI
parseZcashPayment input
| not (T.isPrefixOf "zcash:" (T.pack input)) =
Left "Invalid scheme: must start with 'zcash:'"
| otherwise =
let (addrPart, queryPart) = break (== '?') (drop 6 input)
queryParams = parseQuery (drop 1 queryPart)
in Right
ZcashPaymentURI
{ uriAddress = addrPart
, uriAmount = lookup "amount" queryParams >>= readMaybe
, uriMemo =
case lookup "memo" queryParams of
Just m ->
T.pack
(BC.unpack
(processEither $ decodeBase64Unpadded (BC.pack m)))
_ -> ""
, uriLabel = lookup "label" queryParams
, uriMessage = lookup "message" queryParams
}
-- Function to pad a base64 string if it's not a multiple of 4
padBase64 :: BC.ByteString -> BC.ByteString
padBase64 bs = bs <> BC.replicate paddingLength '='
where
paddingLength = (4 - BC.length bs `mod` 4) `mod` 4
-- Function to decode a base64 un-padded string
decodeBase64Unpadded :: BC.ByteString -> Either String BC.ByteString
decodeBase64Unpadded = B64.decode . padBase64
-- Function to encode memo as un-padded Base64
encodeBase64Memo :: String -> String
encodeBase64Memo = BC.unpack . BC.takeWhile (/= '=') . B64.encode . BC.pack
-- Function to drop trailing zeros
dropTrailingZeros :: String -> String
dropTrailingZeros str =
let withoutZeros = reverse (dropWhile (== '0') (reverse str))
in if last withoutZeros == '.'
then withoutZeros ++ "0" -- Ensure at least one decimal place
else withoutZeros
-- Function to create a ZIP-321 URI
createZip321 :: String -> Maybe Double -> Maybe String -> String
createZip321 address mAmount mMemo =
"zcash:" ++
address ++
maybe
""
(\amount -> "?amount=" ++ dropTrailingZeros (printf "%.8f" amount))
mAmount ++
maybe
""
(\memo -> "&memo=" ++ escapeURIString isUnreserved (encodeBase64Memo memo))
mMemo
getTransparentFromUA :: UnifiedAddress -> Maybe TransparentAddress
getTransparentFromUA ua = TransparentAddress (ua_net ua) <$> t_rec ua
-- Function to check if Text is non-empty after trimming leading spaces
isNotEmptyAfterTrim :: T.Text -> Bool
isNotEmptyAfterTrim txt = not (T.null (T.stripStart txt))
-- Function to convert a Scientific number to Int
scientificToInt :: Scientific -> Int
scientificToInt sc = fromIntegral $ round $ toRealFloat sc
-- Convert a ByteString to Phrase
toPhrase :: BS.ByteString -> Phrase
toPhrase = Phrase

View file

@ -38,8 +38,7 @@ import Zenith.RPC
, zenithServer , zenithServer
) )
import Zenith.Types import Zenith.Types
( AccountType(..) ( Config(..)
, Config(..)
, PrivacyPolicy(..) , PrivacyPolicy(..)
, ProposedNote(..) , ProposedNote(..)
, ValidAddressAPI(..) , ValidAddressAPI(..)
@ -59,16 +58,7 @@ main = do
zebraPort <- require config "zebraPort" zebraPort <- require config "zebraPort"
zebraHost <- require config "zebraHost" zebraHost <- require config "zebraHost"
nodePort <- require config "nodePort" nodePort <- require config "nodePort"
currencyCode <- require config "currencyCode" let myConfig = Config dbFilePath zebraHost zebraPort nodeUser nodePwd nodePort
let myConfig =
Config
dbFilePath
zebraHost
zebraPort
nodeUser
nodePwd
nodePort
currencyCode
hspec $ do hspec $ do
describe "RPC methods" $ do describe "RPC methods" $ do
beforeAll_ (startAPI myConfig) $ do beforeAll_ (startAPI myConfig) $ do
@ -96,7 +86,7 @@ main = do
Left e -> assertFailure e Left e -> assertFailure e
Right r -> Right r ->
r `shouldBe` r `shouldBe`
InfoResponse "zh" (ZenithInfo "0.8.0.0-beta" TestNet "v2.1.0") InfoResponse "zh" (ZenithInfo "0.7.0.0-beta" TestNet "v1.9.0")
describe "Wallets" $ do describe "Wallets" $ do
describe "listwallet" $ do describe "listwallet" $ do
it "bad credentials" $ do it "bad credentials" $ do
@ -308,9 +298,7 @@ main = do
Left e -> assertFailure e Left e -> assertFailure e
Right r -> Right r ->
r `shouldBe` r `shouldBe`
AccountListResponse AccountListResponse "zh" [ZcashAccountAPI 1 1 "Personal"]
"zh"
[ZcashAccountAPI 1 1 "Personal" Local]
describe "Addresses" $ do describe "Addresses" $ do
describe "listaddresses" $ do describe "listaddresses" $ do
it "bad credentials" $ do it "bad credentials" $ do
@ -688,204 +676,6 @@ main = do
case res of case res of
Left e -> assertFailure e Left e -> assertFailure e
Right (SendResponse i o) -> o `shouldNotBe` U.nil Right (SendResponse i o) -> o `shouldNotBe` U.nil
describe "Shield notes" $ do
it "bad credentials" $ do
res <-
makeZenithCall
"127.0.0.1"
nodePort
"baduser"
"idontknow"
ShieldNotes
BlankParams
res `shouldBe` Left "Invalid credentials"
describe "correct credentials" $ do
it "no parameters" $ do
res <-
makeZenithCall
"127.0.0.1"
nodePort
nodeUser
nodePwd
ShieldNotes
BlankParams
case res of
Left e -> assertFailure e
Right (ErrorResponse i c m) -> c `shouldBe` (-32602)
it "invalid account" $ do
res <-
makeZenithCall
"127.0.0.1"
nodePort
nodeUser
nodePwd
ShieldNotes
(ShieldNotesParams 27)
case res of
Left e -> assertFailure e
Right (ErrorResponse i c m) -> c `shouldBe` (-32006)
it "valid account" $ do
res <-
makeZenithCall
"127.0.0.1"
nodePort
nodeUser
nodePwd
ShieldNotes
(ShieldNotesParams 1)
case res of
Left e -> assertFailure e
Right (MultiOpResponse i c) -> c `shouldNotBe` []
describe "Viewing Keys" $ do
describe "Full" $ do
it "bad credentials" $ do
res <-
makeZenithCall
"127.0.0.1"
nodePort
"baduser"
"idontknow"
GetFVK
BlankParams
res `shouldBe` Left "Invalid credentials"
describe "correct credentials" $ do
it "no parameters" $ do
res <-
makeZenithCall
"127.0.0.1"
nodePort
nodeUser
nodePwd
GetFVK
BlankParams
case res of
Left e -> assertFailure e
Right (ErrorResponse i c m) -> c `shouldBe` (-32602)
it "invalid account" $ do
res <-
makeZenithCall
"127.0.0.1"
nodePort
nodeUser
nodePwd
GetFVK
(ViewingKeyParams 27)
case res of
Left e -> assertFailure e
Right (ErrorResponse i c m) -> c `shouldBe` (-32006)
it "valid account" $ do
res <-
makeZenithCall
"127.0.0.1"
nodePort
nodeUser
nodePwd
GetFVK
(ViewingKeyParams 1)
case res of
Left e -> assertFailure e
Right (ViewingKeyResponse i c) -> c `shouldNotBe` ""
Right x -> assertFailure $ show x
describe "Incoming" $ do
it "bad credentials" $ do
res <-
makeZenithCall
"127.0.0.1"
nodePort
"baduser"
"idontknow"
GetIVK
BlankParams
res `shouldBe` Left "Invalid credentials"
describe "correct credentials" $ do
it "no parameters" $ do
res <-
makeZenithCall
"127.0.0.1"
nodePort
nodeUser
nodePwd
GetIVK
BlankParams
case res of
Left e -> assertFailure e
Right (ErrorResponse i c m) -> c `shouldBe` (-32602)
it "invalid account" $ do
res <-
makeZenithCall
"127.0.0.1"
nodePort
nodeUser
nodePwd
GetIVK
(ViewingKeyParams 27)
case res of
Left e -> assertFailure e
Right (ErrorResponse i c m) -> c `shouldBe` (-32006)
it "valid account" $ do
res <-
makeZenithCall
"127.0.0.1"
nodePort
nodeUser
nodePwd
GetIVK
(ViewingKeyParams 1)
case res of
Left e -> assertFailure e
Right (ViewingKeyResponse i c) -> c `shouldNotBe` ""
Right x -> assertFailure $ show x
describe "Importing" $ do
it "bad credentials" $ do
res <-
makeZenithCall
"127.0.0.1"
nodePort
"baduser"
"idontknow"
ImportVK
BlankParams
res `shouldBe` Left "Invalid credentials"
describe "correct credentials" $ do
it "no parameters" $ do
res <-
makeZenithCall
"127.0.0.1"
nodePort
nodeUser
nodePwd
ImportVK
BlankParams
case res of
Left e -> assertFailure e
Right (ErrorResponse i c m) -> c `shouldBe` (-32602)
it "correct params" $ do
res <-
makeZenithCall
"127.0.0.1"
nodePort
nodeUser
nodePwd
ImportVK
(ImportVkParams
"OldWallet"
"uviewtest1jna46ql5qns5rlg99jgs6mhf0j9tk8zxvqsm472scgvmj0vs0rqv2kvdf626gftx7dgn2tltyf0s200gvjlsdvz5celpue9wxxw78txswqmayxc3pfrt5fs5frvr3ep0jrjg8euahqzc63yx9sy4z8lql4ev6q3asptl9rhsfzzrup2g5slwnlvy3dgft44jw3l08xtzypjmsrwxskgnp5s03xlc2kg5520a25pa6fdjxhzutam4wkwr6mh4zeq3qndpks8dk0y90y7gucgsp0j5k2xnhh90m3krk5glz4794dj93pf59h85dqms6337f85ccvpxhays94kvsj2hyjsltf52tygqs8y0vp2yf39drxl687the6xkp8nxkfffc3kqlkhw53t5plplde0vk9rwv340ys04gg48fs0pxfp35rvt2f2pvxjmgmln6lp5k2yzkm0r87k89p6xqv68a6uyfpsauswh9fsckfqey02pjedz5gs934qa"
3249286)
case res of
Left e -> assertFailure e
Right (NewItemResponse i k) -> k `shouldSatisfy` (> 0)
it "list wallets" $ do
res <-
makeZenithCall
"127.0.0.1"
nodePort
nodeUser
nodePwd
ListWallets
BlankParams
case res of
Left e -> assertFailure e
Right (WalletListResponse i k) -> length k `shouldBe` 2
startAPI :: Config -> IO () startAPI :: Config -> IO ()
startAPI config = do startAPI config = do
@ -904,7 +694,7 @@ startAPI config = do
case bc of case bc of
Left e1 -> throwIO e1 Left e1 -> throwIO e1
Right chainInfo -> do Right chainInfo -> do
x <- runNoLoggingT $ initDb "test.db" x <- initDb "test.db"
case x of case x of
Left e2 -> throwIO $ userError e2 Left e2 -> throwIO $ userError e2
Right x' -> do Right x' -> do

View file

@ -9,7 +9,6 @@ import qualified Data.ByteString.Lazy as LBS
import Data.HexString import Data.HexString
import Data.List (foldl') import Data.List (foldl')
import Data.Maybe (fromJust) import Data.Maybe (fromJust)
import qualified Data.Text as T
import qualified Data.Text.Encoding as E import qualified Data.Text.Encoding as E
import Database.Persist import Database.Persist
import Database.Persist.Sqlite import Database.Persist.Sqlite
@ -70,7 +69,6 @@ import Zenith.Core
import Zenith.DB import Zenith.DB
import Zenith.Tree import Zenith.Tree
import Zenith.Types import Zenith.Types
import Zenith.Utils
main :: IO () main :: IO ()
main = do main = do
@ -645,7 +643,8 @@ main = do
case ix of case ix of
Nothing -> assertFailure "couldn't find index at block" Nothing -> assertFailure "couldn't find index at block"
Just i -> do Just i -> do
updatedTree <- runNoLoggingT $ truncateTree oTree i updatedTree <-
runFileLoggingT "test.log" $ truncateTree oTree i
let finalAnchor = let finalAnchor =
getOrchardTreeAnchor $ getOrchardTreeAnchor $
OrchardCommitmentTree $ ztiOrchard zebraTreesIn OrchardCommitmentTree $ ztiOrchard zebraTreesIn
@ -738,7 +737,7 @@ main = do
Just ua -> do Just ua -> do
pool <- runNoLoggingT $ initPool "/home/rav/Zenith/zenith.db" pool <- runNoLoggingT $ initPool "/home/rav/Zenith/zenith.db"
tx <- tx <-
runNoLoggingT $ runFileLoggingT "zenith.log" $
prepareTxV2 prepareTxV2
pool pool
"localhost" "localhost"
@ -764,7 +763,7 @@ main = do
Just ua -> do Just ua -> do
pool <- runNoLoggingT $ initPool "/home/rav/Zenith/zenith.db" pool <- runNoLoggingT $ initPool "/home/rav/Zenith/zenith.db"
tx <- tx <-
runNoLoggingT $ runFileLoggingT "zenith.log" $
prepareTxV2 prepareTxV2
pool pool
"localhost" "localhost"
@ -788,7 +787,7 @@ main = do
Just ua -> do Just ua -> do
pool <- runNoLoggingT $ initPool "/home/rav/Zenith/zenith.db" pool <- runNoLoggingT $ initPool "/home/rav/Zenith/zenith.db"
tx <- tx <-
runNoLoggingT $ runFileLoggingT "zenith.log" $
prepareTxV2 prepareTxV2
pool pool
"localhost" "localhost"
@ -816,7 +815,7 @@ main = do
Just ua -> do Just ua -> do
pool <- runNoLoggingT $ initPool "/home/rav/Zenith/zenith.db" pool <- runNoLoggingT $ initPool "/home/rav/Zenith/zenith.db"
tx <- tx <-
runNoLoggingT $ runFileLoggingT "zenith.log" $
prepareTxV2 prepareTxV2
pool pool
"localhost" "localhost"
@ -848,7 +847,7 @@ main = do
Just ua -> do Just ua -> do
pool <- runNoLoggingT $ initPool "/home/rav/Zenith/zenith.db" pool <- runNoLoggingT $ initPool "/home/rav/Zenith/zenith.db"
tx <- tx <-
runNoLoggingT $ runFileLoggingT "zenith.log" $
prepareTxV2 prepareTxV2
pool pool
"localhost" "localhost"
@ -874,7 +873,7 @@ main = do
Just ua -> do Just ua -> do
pool <- runNoLoggingT $ initPool "/home/rav/Zenith/zenith.db" pool <- runNoLoggingT $ initPool "/home/rav/Zenith/zenith.db"
tx <- tx <-
runNoLoggingT $ runFileLoggingT "zenith.log" $
prepareTxV2 prepareTxV2
pool pool
"localhost" "localhost"
@ -898,7 +897,7 @@ main = do
Just ua -> do Just ua -> do
pool <- runNoLoggingT $ initPool "/home/rav/Zenith/zenith.db" pool <- runNoLoggingT $ initPool "/home/rav/Zenith/zenith.db"
tx <- tx <-
runNoLoggingT $ runFileLoggingT "zenith.log" $
prepareTxV2 prepareTxV2
pool pool
"localhost" "localhost"
@ -927,7 +926,7 @@ main = do
Just ua -> do Just ua -> do
pool <- runNoLoggingT $ initPool "/home/rav/Zenith/zenith.db" pool <- runNoLoggingT $ initPool "/home/rav/Zenith/zenith.db"
tx <- tx <-
runNoLoggingT $ runFileLoggingT "zenith.log" $
prepareTxV2 prepareTxV2
pool pool
"localhost" "localhost"
@ -958,7 +957,7 @@ main = do
Just ua -> do Just ua -> do
pool <- runNoLoggingT $ initPool "/home/rav/Zenith/zenith.db" pool <- runNoLoggingT $ initPool "/home/rav/Zenith/zenith.db"
tx <- tx <-
runNoLoggingT $ runFileLoggingT "zenith.log" $
prepareTxV2 prepareTxV2
pool pool
"localhost" "localhost"
@ -984,7 +983,7 @@ main = do
Just ua -> do Just ua -> do
pool <- runNoLoggingT $ initPool "/home/rav/Zenith/zenith.db" pool <- runNoLoggingT $ initPool "/home/rav/Zenith/zenith.db"
tx <- tx <-
runNoLoggingT $ runFileLoggingT "zenith.log" $
prepareTxV2 prepareTxV2
pool pool
"localhost" "localhost"
@ -1008,7 +1007,7 @@ main = do
Just ua -> do Just ua -> do
pool <- runNoLoggingT $ initPool "/home/rav/Zenith/zenith.db" pool <- runNoLoggingT $ initPool "/home/rav/Zenith/zenith.db"
tx <- tx <-
runNoLoggingT $ runFileLoggingT "zenith.log" $
prepareTxV2 prepareTxV2
pool pool
"localhost" "localhost"
@ -1035,7 +1034,7 @@ main = do
Just ua -> do Just ua -> do
pool <- runNoLoggingT $ initPool "/home/rav/Zenith/zenith.db" pool <- runNoLoggingT $ initPool "/home/rav/Zenith/zenith.db"
tx <- tx <-
runNoLoggingT $ runFileLoggingT "zenith.log" $
prepareTxV2 prepareTxV2
pool pool
"localhost" "localhost"
@ -1062,7 +1061,7 @@ main = do
Just ua -> do Just ua -> do
pool <- runNoLoggingT $ initPool "/home/rav/Zenith/zenith.db" pool <- runNoLoggingT $ initPool "/home/rav/Zenith/zenith.db"
tx <- tx <-
runNoLoggingT $ runFileLoggingT "zenith.log" $
prepareTxV2 prepareTxV2
pool pool
"localhost" "localhost"
@ -1087,7 +1086,7 @@ main = do
Just ua -> do Just ua -> do
pool <- runNoLoggingT $ initPool "/home/rav/Zenith/zenith.db" pool <- runNoLoggingT $ initPool "/home/rav/Zenith/zenith.db"
tx <- tx <-
runNoLoggingT $ runFileLoggingT "zenith.log" $
prepareTxV2 prepareTxV2
pool pool
"localhost" "localhost"
@ -1104,47 +1103,3 @@ main = do
case tx of case tx of
Left e -> assertFailure $ show e Left e -> assertFailure $ show e
Right h -> h `shouldNotBe` hexString "deadbeef" Right h -> h `shouldNotBe` hexString "deadbeef"
describe "Call CoinGecko to get ZEC price" $ do
it "Testing for USD " $ do
price <- getZcashPrice $ T.pack "usd"
case price of
Just p -> p `shouldNotBe` 0.0
Nothing -> assertFailure "Failed to get ZEC price"
describe "Parse an URI payment string (all fields filled) " $ do
it ("Parsing URI -> " ++ "zcash:ztestsapling10yy2ex5....") $ do
let zcashURI2 =
"zcash:ztestsapling10yy2ex5dcqkclhc7z7yrnjq2z6feyjad56ptwlfgmy77dmaqqrl9gyhprdx59qgmsnyfska2kez?amount=100&memo=SGVsbG8sIFdvcmxkIQ==&message=Test"
case parseZcashPayment zcashURI2 of
Right p -> do
print p
(uriAmount p) `shouldBe` Just 100.0
Left e -> assertFailure $ "Error: " ++ e
describe
"Parse an URI payment string (just address and amount fields provided) " $ do
it ("Parsing URI -> " ++ "zcash:ztestsapling10yy2ex5....") $ do
let zcashURI3 =
"zcash:ztestsapling10yy2ex5dcqkclhc7z7yrnjq2z6feyjad56ptwlfgmy77dmaqqrl9gyhprdx59qgmsnyfska2kez?amount=100"
case parseZcashPayment zcashURI3 of
Right p -> do
print p
(uriAmount p) `shouldBe` Just 100.0
Left e -> assertFailure $ "Error: " ++ e
describe "Parse an URI payment string (invalid URI provided) " $ do
it ("Parsing URI -> " ++ "zcash:ztestsapling10yy2ex5....") $ do
let zcashURI3 =
"z:ztestsapling10yy2ex5dcqkclhc7z7yrnjq2z6feyjad56ptwlfgmy77dmaqqrl9gyhprdx59qgmsnyfska2kez?amount=100"
case parseZcashPayment zcashURI3 of
Right p -> do
print p
(uriAmount p) `shouldBe` Just 100.0
Left e -> assertFailure $ "Error: " ++ e
describe "Create a ZIP-321 URI payment string " $ do
it "Creating an URI using a valid Zcash address, an amount, and a memo " $ do
let address =
"ztestsapling10yy2ex5dcqkclhc7z7yrnjq2z6feyjad56ptwlfgmy77dmaqqrl9gyhprdx59qgmsnyfska2kez"
let amount = Just 1.2345
let memo = Just "This is a simple memo."
let uriString = createZip321 address amount memo
print uriString
uriString `shouldBe`
"zcash:ztestsapling10yy2ex5dcqkclhc7z7yrnjq2z6feyjad56ptwlfgmy77dmaqqrl9gyhprdx59qgmsnyfska2kez?amount=1.2345&memo=VGhpcyBpcyBhIHNpbXBsZSBtZW1vLg"

@ -1 +1 @@
Subproject commit 0d042d639d471af14ebe94707f64b5ff5c2cb5eb Subproject commit d45bd7dcf3c3cf4e893900a1774d24b14bf56591

View file

@ -1,7 +1,7 @@
{ {
"openrpc": "1.0.0-rc1", "openrpc": "1.0.0-rc1",
"info": { "info": {
"version": "0.9.0.0-beta", "version": "0.7.0.0-beta",
"title": "Zenith RPC", "title": "Zenith RPC",
"description": "The RPC methods to interact with the Zenith Zcash wallet", "description": "The RPC methods to interact with the Zenith Zcash wallet",
"license": { "license": {
@ -230,8 +230,7 @@
{ "$ref": "#/components/errors/ZebraNotAvailable" }, { "$ref": "#/components/errors/ZebraNotAvailable" },
{ "$ref": "#/components/errors/DuplicateName" }, { "$ref": "#/components/errors/DuplicateName" },
{ "$ref": "#/components/errors/ZenithBusy" }, { "$ref": "#/components/errors/ZenithBusy" },
{ "$ref": "#/components/errors/InvalidWallet" }, { "$ref": "#/components/errors/InvalidWallet" }
{ "$ref": "#/components/errors/NotLocal" }
] ]
}, },
{ {
@ -497,8 +496,7 @@
} }
], ],
"errors": [ "errors": [
{ "$ref": "#/components/errors/InvalidAccount" }, { "$ref": "#/components/errors/InvalidAccount" }
{ "$ref": "#/components/errors/ReadOnly" }
] ]
}, },
{ {
@ -607,9 +605,10 @@
], ],
"paramStructure": "by-position", "paramStructure": "by-position",
"result": { "result": {
"name": "Operation ID", "name": "Operation ID(s)",
"schema": { "schema": {
"$ref": "#/components/contentDescriptors/OperationId" "type": "array",
"items": { "$ref": "#/components/contentDescriptors/OperationId"}
} }
}, },
"examples": [ "examples": [
@ -621,7 +620,7 @@
{ {
"name": "Account index", "name": "Account index",
"summary": "The index for the account to use", "summary": "The index for the account to use",
"value": 1 "value": "1"
}, },
{ {
"name": "Privacy Policy", "name": "Privacy Policy",
@ -642,16 +641,16 @@
], ],
"result": { "result": {
"name": "SendMany result", "name": "SendMany result",
"value": "3cc31c07-07cf-4a6e-9190-156c4b8c4088" "value": [
"3cc31c07-07cf-4a6e-9190-156c4b8c4088"
]
} }
} }
], ],
"errors": [ "errors": [
{ "$ref": "#/components/errors/ZebraNotAvailable" }, { "$ref": "#/components/errors/ZebraNotAvailable" },
{ "$ref": "#/components/errors/ZenithBusy" }, { "$ref": "#/components/errors/ZenithBusy" },
{ "$ref": "#/components/errors/InvalidAccount" }, { "$ref": "#/components/errors/InvalidAccount" }
{ "$ref": "#/components/errors/ReadOnly" }
] ]
}, },
{ {
@ -670,226 +669,6 @@
"errors": [ "errors": [
{ "$ref": "#/components/errors/OpNotFound" } { "$ref": "#/components/errors/OpNotFound" }
] ]
},
{
"name": "shieldnotes",
"summary": "Shield all transparent notes into the Orchard pool for the given account",
"description": "Creates one or more transactions, grouping all the unspent transparent notes for the given account by their transparent address to avoid associating different transparent addresses. These notes are sent to the given account's internal change address as shielded Orchard notes.",
"tags": [],
"params": [
{ "$ref": "#/components/contentDescriptors/AccountId"}
],
"paramStructure": "by-position",
"result": {
"name": "Operation ID(s)",
"schema": {
"type": "array",
"items": { "$ref": "#/components/contentDescriptors/OperationId"}
}
},
"examples": [
{
"name": "Shield transparent notes",
"summary": "Shield transparent notes",
"description": "Shield the transparent notes in a given account",
"params": [
{
"name": "Account index",
"summary": "The index for the account to use",
"value": "3"
}
],
"result": {
"name": "ShieldNotes result",
"value": [
"ab350df0-9f57-44c0-9e0d-f7b8af1f4231",
"8c6f2656-22ef-4f9d-b465-80ddd13fc485"
]
}
},
{
"name": "No transparent funds",
"summary": "Shield transparent notes with no transparent funds",
"description": "Attempt to shield the transparent notes in a given account, when account has none",
"params": [
{
"name": "Account index",
"summary": "The index for the account to use",
"value": "3"
}
],
"result": {
"name": "ShieldNotes result",
"value": [
"InsufficientFunds"
]
}
}
],
"errors": [
{ "$ref": "#/components/errors/ZebraNotAvailable" },
{ "$ref": "#/components/errors/ZenithBusy" },
{ "$ref": "#/components/errors/InvalidAccount" }
]
},
{
"name": "deshieldfunds",
"summary": "De-shield the given amount of ZEC from the given account",
"description": "Creates a new internal transaction with the requested amount of ZEC to the transparent pool. The fee is not included in the given amount.",
"tags": [],
"params": [
{ "$ref": "#/components/contentDescriptors/AccountId"},
{ "$ref": "#/components/contentDescriptors/Amount"}
],
"paramStructure": "by-position",
"result": {
"name": "Operation ID",
"schema": {
"$ref": "#/components/contentDescriptors/OperationId"
}
},
"examples": [
{
"name": "De-Shield funds",
"summary": "De-shield funds",
"description": "Move the given amount of ZEC for the given acount from the shielded pool to the transparent pool",
"params": [
{
"name": "Account index",
"summary": "The index for the account to use",
"value": "3"
},
{
"name": "Amount",
"summary": "The amount of ZEC to use",
"value": 1.23
}
],
"result": {
"name": "Deshield funds result",
"value": "ab350df0-9f57-44c0-9e0d-f7b8af1f4231"
}
},
{
"name": "No transparent funds",
"summary": "Shield transparent notes with no transparent funds",
"description": "Attempt to shield the transparent notes in a given account, when account has none",
"params": [
{
"name": "Account index",
"summary": "The index for the account to use",
"value": "3"
}
],
"result": {
"name": "ShieldNotes result",
"value": [
"InsufficientFunds"
]
}
}
],
"errors": [
{ "$ref": "#/components/errors/ZebraNotAvailable" },
{ "$ref": "#/components/errors/ZenithBusy" },
{ "$ref": "#/components/errors/InvalidAccount" }
]
},
{
"name": "getfullvk",
"summary": "Derive the full viewing key for the given account.",
"description": "Derive the full viewing key for the given account, encoded per ZIP-316.",
"tags": [],
"params": [
{ "$ref": "#/components/contentDescriptors/AccountId"}
],
"paramStructure": "by-position",
"result": {
"name": "Full viewing key",
"schema": {
"$ref": "#/components/schemas/ViewingKey"
}
},
"examples": [
{
"name": "Get full viewing key",
"summary": "Get the full viewing key",
"description": "Get the full viewing key for the give account, encoded per ZIP-316",
"params": [
{
"name": "Account index",
"summary": "The index for the account to use",
"value": 1
}
],
"result": {
"name": "Full Viewing key",
"value": "uview16qdhd9e283s4y53gmw72ag7adzdrj9f9v96dw89ggv9el0yrf7vkappau69j8luq7uf540sr78ncslnqk6kwpc4qeqgfg5vn4xcmllynyfr32cgq6nx5ptku44kfxtsj99px2g9yp7kyc32quun0elakgltqmqflprwmryuelcfwwt58vqap065as7qwljg02l6mkutsh2y9aefd284dsrj0246fd2n4hra3r03uftsh4njh3w590z78tpnfqhjvvwhgus476zrw3fd69qekru709ghr0zr7h8majy9aclwg7uhakt24lmuec8dd7t270kamcs99rz8jasj3jl6m9y77dvkdn23e2kwuc6kyagpstzrdjnlzdldmgsu4k056v80ucajcjvl99pcf2znjg37vztdp4zr5qrphxs4y7wppxmankmdwwgjxhlmyrjd68z80q0n0t2cyqge6mlc7pd5wre4392pjtdaqvtyeg0denh4ekynnjxnm"
}
}
],
"errors": [
{ "$ref": "#/components/errors/InvalidAccount" }
]
},
{
"name": "getincomingvk",
"summary": "Get the incoming viewing key for the given account.",
"description": "Derives the incoming viewing key for the given account per ZIP-316.",
"tags": [],
"params": [
{ "$ref": "#/components/contentDescriptors/AccountId"}
],
"paramStructure": "by-position",
"result": {
"name": "Incoming viewing key",
"schema": {
"$ref": "#/components/schemas/ViewingKey"
}
},
"examples": [
{
"name": "Get incoming viewing key",
"summary": "Get the incoming viewing key",
"description": "Get the incoming viewing key for the give account, encoded per ZIP-316",
"params": [
{
"name": "Account index",
"summary": "The index for the account to use",
"value": 1
}
],
"result": {
"name": "Incoming Viewing key",
"value": "uivk199qcjxrj73n7fapg79a2ltah6f3j83haljcux5t5kvn5unn7rpfmvglttdt9g6na3llkefnd3pn0x9ky6lh8s42trj0vfg5wtv0nrerq0wsq5v4q7lt5j4l9svppspr6h7407ztgsuvkfk977c3tj408nx5phxap8fn3ecdmdrah9spp76md9tel89tuqz6m0xplqp83wj33qf7s3hwfe79t04rq49g24nr3emlpm298wpqla2dvh4rr584kwdtxc9ahse5x0drcjr95tt4k0hxr32l6yturje7dptlgjnr4cm6uk29ysu9l5xwgz40p6alyedzzqltqf5nswy48ekru4ahapw"
}
}
],
"errors": [
{ "$ref": "#/components/errors/InvalidAccount" }
]
},
{
"name": "importvk",
"summary": "Import the given Unified Viewing Key",
"description": "Imports the given Unified Viewing Key, autodetecting if it is Full or Incoming. Caution: If the given birthday height is lower than the lowest birthday height in the wallet, this will trigger a full re-scan of the wallet.",
"tags": [
],
"params": [
{ "$ref": "#/components/contentDescriptors/Name"},
{ "$ref": "#/components/contentDescriptors/ViewingKey"},
{ "$ref": "#/components/contentDescriptors/BirthdayHeight"}
],
"paramStructure": "by-position",
"result": {
"name": "Account Identifier",
"schema": { "$ref": "#/components/contentDescriptors/AccountId"}
},
"errors": [
{ "$ref": "#/components/errors/ZebraNotAvailable" },
{ "$ref": "#/components/errors/DuplicateName" },
{ "$ref": "#/components/errors/ZenithBusy" }
]
} }
], ],
"components": { "components": {
@ -921,15 +700,6 @@
"type": "string" "type": "string"
} }
}, },
"Amount": {
"name": "A numeric amount",
"summary": "A numeric amount",
"description": "A number that represents an amount to be used by a function as an input",
"required": true,
"schema": {
"type": "number"
}
},
"Name": { "Name": {
"name": "Name", "name": "Name",
"summary": "A user-friendly name", "summary": "A user-friendly name",
@ -985,24 +755,6 @@
"type": "string", "type": "string",
"enum": ["None", "Low", "Medium", "Full"] "enum": ["None", "Low", "Medium", "Full"]
} }
},
"BirthdayHeight": {
"name": "Birthday Height",
"summary": "The block height at which a wallet was created.",
"description": "The block height where a wallet was created. The wallet will not scan blocks at a lower block height than this, assuming there are no transactions on-chain before this point.",
"required": true,
"schema": {
"type": "integer"
}
},
"ViewingKey": {
"name": "Viewing Key",
"summary": "A Unified viewing key.",
"description": "A Unified viewing key encoded per [ZIP-316](https://zips.z.cash/zip-0316). Zenith supports both full and incoming viewing keys.",
"required": true,
"schema": {
"type": "string"
}
} }
}, },
"schemas": { "schemas": {
@ -1021,8 +773,7 @@
"name": { "type": "string", "description": "User-friendly name of the wallet" }, "name": { "type": "string", "description": "User-friendly name of the wallet" },
"network": { "type": "string", "description": "Network the wallet is for. Testnet or MainNet" }, "network": { "type": "string", "description": "Network the wallet is for. Testnet or MainNet" },
"birthday": { "type": "integer", "description": "Wallet's birthday height" }, "birthday": { "type": "integer", "description": "Wallet's birthday height" },
"lastSync": { "type": "integer", "description": "Last block the wallet is synced to" }, "lastSync": { "type": "integer", "description": "Last block the wallet is synced to" }
"local": { "type": "boolean", "description": "True for wallets belonging to this Zenith instance, False for wallets created to manage viewing keys"}
} }
}, },
"ZcashAccount": { "ZcashAccount": {
@ -1030,8 +781,7 @@
"properties": { "properties": {
"index": { "type": "integer", "description": "Internal index for account"}, "index": { "type": "integer", "description": "Internal index for account"},
"wallet": { "type": "integer", "description": "ID of the wallet this account belongs to"}, "wallet": { "type": "integer", "description": "ID of the wallet this account belongs to"},
"name": { "type": "string", "description": "User-friendly name of the account"}, "name": { "type": "string", "description": "User-friendly name of the account"}
"type": { "type": "string", "description": "Local for accounts belonging to the wallet, FullViewKey for full viewing keys, IncomingViewKey for incoming"}
} }
}, },
"ZcashAddress": { "ZcashAddress": {
@ -1085,10 +835,6 @@
"amount": { "type": "number", "description": "The amount to send in ZEC"}, "amount": { "type": "number", "description": "The amount to send in ZEC"},
"memo": { "type": "string", "description": "The shielded memo to include, if applicable"} "memo": { "type": "string", "description": "The shielded memo to include, if applicable"}
} }
},
"ViewingKey": {
"type": "string",
"description": "The viewing key, encoded per ZIP-316"
} }
}, },
"examples": {}, "examples": {},
@ -1148,18 +894,6 @@
"ZenithBusy": { "ZenithBusy": {
"code": -32012, "code": -32012,
"message": "The Zenith server is syncing, please try again later." "message": "The Zenith server is syncing, please try again later."
},
"InvalidVK": {
"code": -32013,
"message": "The viewing key provided is not valid."
},
"ReadOnly": {
"code": -32014,
"message": "Read-only account, operation is not valid."
},
"NotLocal": {
"code": -32015,
"message": "The wallet is not local, cannot create new accounts."
} }
} }
} }

View file

@ -1,6 +1,6 @@
cabal-version: 3.0 cabal-version: 3.0
name: zenith name: zenith
version: 0.9.1.0-beta version: 0.7.0.0-beta
license: MIT license: MIT
license-file: LICENSE license-file: LICENSE
author: Rene Vergara author: Rene Vergara
@ -96,8 +96,6 @@ library
, vty-crossplatform , vty-crossplatform
, word-wrap , word-wrap
, zcash-haskell , zcash-haskell
, unordered-containers
, network-uri
--pkgconfig-depends: rustzcash_wrapper --pkgconfig-depends: rustzcash_wrapper
default-language: Haskell2010 default-language: Haskell2010

View file

@ -1,42 +1,5 @@
#
# Zenith Configuration File
#
# -------------------------------------------------------------
# nodeUser -
# -------------------------------------------------------------
nodeUser = "user" nodeUser = "user"
# -------------------------------------------------------------
# nodePwd -
nodePwd = "superSecret" nodePwd = "superSecret"
# ------------------------------------------------------------- dbFilePath = "zenith.db"
# nodePort -
nodePort = 8234
# -------------------------------------------------------------
# nodePwd -
# dbFileName - contains the SQLite database name used for
# keeping all Zenith's data
# default = zenith.db
#
dbFileName = "zenith.db"
# -------------------------------------------------------------
# zebraHost - Zebra IP
# Default - "127.0.0.1"
zebraHost = "127.0.0.1" zebraHost = "127.0.0.1"
# ------------------------------------------------------------- zebraPort = 18232
# zebraPort - Port used for access Zebra API endpoints
# must be the same port configured for your
# Zebra node
zebraPort = 8232
# -------------------------------------------------------------
# currencyCode - ISO 4217 currency code
#
# Example of currency codes are:
#
# United States -> currencyCode = "usd"
# Canada -> currencyCode = "cnd"
# Australia -> currencyCode = "aud"
# Euro Region -> currencyCode = "eur"
# Great Britain -> currencyCode = "gbp"
# Japan -> currencyCode = "jpy"
#
currencyCode = "usd"

BIN
zenith_er.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 MiB

BIN
zenith_er.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 329 KiB