haskoin-wallet-0.2.0: tests/Network/Haskoin/Wallet/Units.hs
module Network.Haskoin.Wallet.Units (tests) where
import Test.HUnit (Assertion, assertEqual, assertFailure)
import Test.Framework (Test, testGroup)
import Test.Framework.Providers.HUnit (testCase)
import Control.Monad (liftM, guard)
import Control.Monad.Trans (liftIO)
import Control.Exception (Exception, handleJust)
import Control.Monad.Trans.Resource (ResourceT)
import Control.Monad.Logger (NoLoggingT)
import Data.Word (Word32, Word64)
import Data.Maybe (fromJust)
import qualified Data.ByteString as BS
( ByteString
, empty
, pack
)
import Database.Persist (Entity(..), entityVal, getBy)
import Database.Persist.Sqlite
( runSqlite
, runMigrationSilent
, SqlPersistT
)
import Network.Haskoin.Wallet.Internals
import Network.Haskoin.Node.HeaderTree
import Network.Haskoin.Block
import Network.Haskoin.Transaction
import Network.Haskoin.Script
import Network.Haskoin.Crypto
import Network.Haskoin.Util
type App = SqlPersistT (NoLoggingT (ResourceT IO))
tests :: [Test]
tests =
[ testGroup "KeyRing creation"
[ testCase "Calling newKeyRing with an empty seed should fail" $
assertException
(WalletException "The seed is empty")
(newKeyRing "main" BS.empty)
, testCase "Creating two KeyRings with the same name should fail" $
assertException
(WalletException "KeyRing main already exists") $ do
_ <- newKeyRing "main" $ BS.pack [0]
newKeyRing "main" $ BS.pack [1]
]
, testGroup "Account tests"
[ testCase "Creating two accounts with the same name should fail" $
assertException (WalletException "Account acc already exists") $ do
keyE <- newKeyRing "main" $ BS.pack [1]
_ <- newAccount keyE "acc" (AccountRegular False) []
newAccount keyE "acc" (AccountRegular False) []
, testCase "Invalid multisig parameters (0 of 1)" $
assertException (WalletException "Invalid account type") $ do
keyE <- newKeyRing "main" $ BS.pack [0]
newAccount keyE "ms" (AccountMultisig False 0 1) []
, testCase "Invalid multisig parameters (2 of 1)" $
assertException (WalletException "Invalid account type") $ do
keyE <- newKeyRing "main" $ BS.pack [0]
newAccount keyE "ms" (AccountMultisig False 2 1) []
, testCase "Invalid multisig parameters (15 of 16)" $
assertException (WalletException "Invalid account type") $ do
keyE <- newKeyRing "main" $ BS.pack [0]
newAccount keyE "ms" (AccountMultisig False 15 16) []
, testCase "To many multisig keys (2 keys for 1 of 2)" $
assertException
(WalletException "Invalid account keys") $ do
keyE <- newKeyRing "main" $ BS.pack [0]
newAccount keyE "ms" (AccountMultisig False 1 2)
[ deriveXPubKey $ makeXPrvKey (BS.pack [1])
, deriveXPubKey $ makeXPrvKey (BS.pack [2])
]
, testCase "Calling addAccountKeys with an empty key list should fail" $
assertException
(WalletException "Invalid account keys") $ do
keyE <- newKeyRing "main" $ BS.pack [0]
accE <- newAccount keyE "default" (AccountRegular True) []
addAccountKeys accE []
, testCase "Calling addAccountKeys on a non-multisig account should fail" $
assertException
(WalletException "The account is already complete") $ do
keyE <- newKeyRing "main" $ BS.pack [0]
_ <- newAccount keyE "default" (AccountRegular False) []
(_, accE) <- getAccount "main" "default"
addAccountKeys accE [ deriveXPubKey $ makeXPrvKey (BS.pack [1]) ]
, testCase "Adding keys to a complete multisig account should fail" $
assertException
(WalletException "The account is already complete") $ do
keyE <- newKeyRing "main" $ BS.pack [0]
_ <- newAccount keyE "ms" (AccountMultisig False 2 3)
[ deriveXPubKey $ makeXPrvKey (BS.pack [1])
, deriveXPubKey $ makeXPrvKey (BS.pack [2])
]
(_, accE) <- getAccount "main" "ms"
addAccountKeys accE [deriveXPubKey $ makeXPrvKey (BS.pack [3])]
, testCase "Getting a non-existing account should fail" $
assertException
(WalletException "Account default does not exist") $ do
_ <- newKeyRing "main" $ BS.pack [0]
getAccount "main" "default"
]
, testGroup "Address tests"
[ testCase "Displaying page 0 should fail" $
assertException
(WalletException "Invalid page request (Page: 0, Page size: 1)" ) $ do
keyE <- newKeyRing "main" $ BS.pack [0]
accE <- newAccount keyE "default" (AccountRegular False) []
addressPage accE AddressExternal $ PageRequest 0 1 False
, testCase "Displaying 0 results per page should fail" $
assertException
(WalletException "Invalid page request (Page: 1, Page size: 0)" ) $ do
keyE <- newKeyRing "main" $ BS.pack [0]
accE <- newAccount keyE "default" (AccountRegular False) []
addressPage accE AddressExternal $ PageRequest 1 0 False
, testCase "Displaying a page number that is too high should fail" $
assertException
(WalletException "Invalid page number 5") $ do
keyE <- newKeyRing "main" $ BS.pack [0]
accE <- newAccount keyE "default" (AccountRegular False) []
addressPage accE AddressExternal $ PageRequest 5 3 False
, testCase "Decreasing the address gap should fail" $
assertException (WalletException "The gap of an account can only be increased") $ do
keyE <- newKeyRing "main" $ BS.pack [0]
_ <- newAccount keyE "default" (AccountRegular False) []
(_, acc1E) <- getAccount "main" "default"
_ <- setAccountGap acc1E 15
(_, acc2E) <- getAccount "main" "default"
setAccountGap acc2E 14
, testCase "Setting a label on a hidden address key should fail" $
assertException (WalletException "Invalid address index 10") $ do
keyE <- newKeyRing "main" $ BS.pack [0]
accE <- newAccount keyE "default" (AccountRegular False) []
setAddrLabel accE 10 AddressExternal "Gym membership"
, testCase "Setting a label on an invalid address key should fail" $
assertException (WalletException "Invalid address index 20") $ do
keyE <- newKeyRing "main" $ BS.pack [0]
accE <- newAccount keyE "default" (AccountRegular False) []
setAddrLabel accE 20 AddressExternal "Gym membership"
, testCase "Requesting an address prvkey on a read-only account should fail" $
assertException
(WalletException "Invalid address") $ do
keyE@(Entity _ kr) <- newKeyRing "main" $ BS.pack [0]
accE <- newAccount keyE "default" (AccountRegular True)
[deriveXPubKey $ makeXPrvKey $ BS.pack [1]]
addressPrvKey kr accE 2 AddressExternal
]
, testGroup "Wallet tests"
[ testCase "Verify address derivations" $ runUnit testDerivations
, testCase "Verify balances" $ runUnit testBalances
, testCase "Verify balances in conflict" $ runUnit testConflictBalances
, testCase "Offline transactions" $ runUnit testOffline
, testCase "Kill an offline tx by spending his coins" $ runUnit testKillOffline
, testCase "Offline transaction exceptions" testOfflineExceptions
, testCase "Multisig test 1" $ runUnit testImportMultisig
, testCase "Kill Tx" $ runUnit testKillTx
]
]
assertException :: (Exception e, Eq e) => e -> App a -> Assertion
assertException ex action =
handleJust matchEx (const $ return ()) $ do
runUnit action
assertFailure $ "Expecting exception: " ++ show ex
where
matchEx = guard . (== ex)
runUnit :: App a -> Assertion
runUnit action = do
_ <- runSqlite ":memory:" $ do
_ <- runMigrationSilent migrateWallet
initWallet 0.0001
action
return ()
bs1 :: BS.ByteString
bs1 = fromRight $ mnemonicToSeed pass
"mass coast dance birth online various renew alert crunch middle absurd health"
pass :: BS.ByteString
pass = "passw0rd"
tid1 :: TxHash
tid1 = "0000000000000000000000000000000000000000000000000000000000000001"
bid0 :: BlockHash
bid0 = "0000000000000000000000000000000000000000000000000000000000000000"
bid1 :: BlockHash
bid1 = "0000000000000000000000000000000000000000000000000000000000000001"
bid2 :: BlockHash
bid2 = "0000000000000000000000000000000000000000000000000000000000000002"
bid3 :: BlockHash
bid3 = "0000000000000000000000000000000000000000000000000000000000000003"
bid4 :: BlockHash
bid4 = "0000000000000000000000000000000000000000000000000000000000000004"
bid5 :: BlockHash
bid5 = "0000000000000000000000000000000000000000000000000000000000000005"
bid6 :: BlockHash
bid6 = "0000000000000000000000000000000000000000000000000000000000000006"
bid7 :: BlockHash
bid7 = "0000000000000000000000000000000000000000000000000000000000000007"
-- Creates fake testing blocks
fakeNode :: Word32 -> BlockHash -> BlockHeaderNode
fakeNode i h = BlockHeaderNode
{ nodeBlockHash = h
, nodeHeader = BlockHeader 1 z1 z2 0 0 0
, nodeHeaderHeight = i
, nodeChainWork = 0
, nodeChild = Nothing
, nodeMedianTimes = []
, nodeMinWork = 0
}
where
z1 = "0000000000000000000000000000000000000000000000000000000000000000"
z2 = "0000000000000000000000000000000000000000000000000000000000000000"
fakeTx :: [(TxHash, Word32)] -> [(BS.ByteString, Word64)] -> Tx
fakeTx xs ys =
Tx 1 txi txo 0
where
txi = map (\(h,p) -> TxIn (OutPoint h p) (BS.pack [1]) maxBound) xs
f = encodeOutputBS . PayPKHash . fromJust . base58ToAddr
txo = map (\(a,v) -> TxOut v $ f a ) ys
testDerivations :: App ()
testDerivations = do
keyE <- newKeyRing "test" bs1
accE <- newAccount keyE "acc1" (AccountRegular False) []
unusedAddresses accE AddressExternal
>>= liftIO . assertEqual "Generated external addresses do not match"
[ "13XaDQvvE4rqiVKMi4MApsaZwTcDNiwfuR"
, "1BECmeSVxBYCwL493wt9Vqx8mvaWozTF4r"
, "1J7n7Lz1VKYdemEDWfyFoGQpSByK9doqeZ"
, "184p3tofVNgFXfA7Ry3VU1uTPyr5dGCiUF"
, "1FkBfN2P6RdvSE6M4k1BGZqFYRLXMXyJen"
, "1MQRM1Luzq4rkrKV8ii7BiukjCa63wt91D"
, "14zzWHCS5969DL4ZqphMrsG7p2gCSJnCV7"
, "1FFCS3SzGduAv2MBM9Ak9tALT5snVySST"
, "18VNX8vQre2hGneuCrXtXwB5D1NVTBUB46"
, "17mE4ZUaWETvjyLXbTcgoyqTc3A1f7eWVs"
] . map (addrToBase58 . keyRingAddrAddress)
unusedAddresses accE AddressInternal
>>= liftIO . assertEqual "Generated internal addresses do not match"
[ "1BwbQ8Wp7YUfaYeiQPgXu6br5e4ogKjuKd"
, "16wQCfrqW5QegVe5pXpczHaxDmqTAn4ieM"
, "1PZjbfPbGzvB7jvoRSkCQZfne154mjU3sY"
, "152Nc7WrB24foAydrHJ7Sie954NgXCx5Tn"
, "1HojKLGEQb9bZMMckXgujnv9HGCNxtowCP"
, "13X9ds52rRYGvLwfbAvQDVU7K13j9cU7BR"
, "1LSBEYAcmsZuxyPVpF1GqxXTRxpg4CaJPF"
, "1MUcLFqrYhkSHjYcQdfZJRwnkEi9xWaGZU"
, "12vgEgi8ExgCo7EBPG1kxwJGR5FCXmZpoB"
, "1K14RjZ3he6erLHFNrPWwvmxm4nbr1MEYC"
] . map (addrToBase58 . keyRingAddrAddress)
testBalances :: App ()
testBalances = do
keyE <- newKeyRing "test" bs1
accE@(Entity ai _) <- newAccount keyE "acc1" (AccountRegular False) []
let fundingTx = fakeTx
[ (tid1, 0) ]
[ ("13XaDQvvE4rqiVKMi4MApsaZwTcDNiwfuR", 10000000)
, ("1BECmeSVxBYCwL493wt9Vqx8mvaWozTF4r", 20000000)
]
let tx1 = fakeTx
[ (txHash fundingTx, 0)
, (txHash fundingTx, 1)
]
[ ("1MchgrtQEUgV1f7Nqe1vEzvdmBzJHz8zrY", 30000000) ] -- external
tx2 = fakeTx
[ (txHash fundingTx, 0) ]
[ ("1MchgrtQEUgV1f7Nqe1vEzvdmBzJHz8zrY", 5000000) -- external
, ("1BwbQ8Wp7YUfaYeiQPgXu6br5e4ogKjuKd", 5000000) -- change
]
accountBalance ai 0 False >>=
liftIO . (assertEqual "Balance is not 0") 0
-- Import funding transaction twice. This operation should be idempotent
importNetTx fundingTx
>>= liftIO
. (assertEqual "Confidence is not pending"
([(ai, TxPending)], 2))
. testTx
importNetTx fundingTx
>>= liftIO
. (assertEqual "Confidence is not pending"
([(ai, TxPending)], 0))
. testTx
spendableCoins ai 0 (const . const []) >>=
liftIO . (assertEqual "0-conf spendable coins is not 2" 2) . length
spendableCoins ai 1 (const . const []) >>=
liftIO . (assertEqual "1-conf spendable coins is not 0" 0) . length
accountBalance ai 0 False >>=
liftIO . (assertEqual "Balance is not 30000000") 30000000
accountBalance ai 1 False >>=
liftIO . (assertEqual "Balance is not 0") 0
addressBalances accE 0 0 AddressExternal 0 False
>>= liftIO
. (assertEqual "Address 0 balance is not (10000000, 0, 1, 0)")
[(0, BalanceInfo 10000000 0 1 0)]
addressBalances accE 1 1 AddressExternal 0 False
>>= liftIO
. (assertEqual "Address 1 balance is not (20000000, 0, 1, 0)")
[(1, BalanceInfo 20000000 0 1 0)]
addressBalances accE 0 0 AddressExternal 1 False
>>= liftIO
. (assertEqual "Address 0 1-conf balance is not (0, 0, 0, 0)")
[(0, BalanceInfo 0 0 0 0)]
addressBalances accE 1 1 AddressExternal 1 False
>>= liftIO
. (assertEqual "Address 1 1-conf balance is not (0, 0, 0, 0)")
[(1, BalanceInfo 0 0 0 0)]
importNetTx tx1
>>= liftIO
. (assertEqual "Confidence is not pending"
([(ai, TxPending)], 0))
. testTx
accountBalance ai 0 False >>=
liftIO . (assertEqual "Balance is not 0") 0
accountBalance ai 1 False >>=
liftIO . (assertEqual "Balance is not 0") 0
addressBalances accE 0 0 AddressExternal 0 False
>>= liftIO
. (assertEqual "Address 0 balance is not (10000000, 10000000, 1, 1)")
[(0, BalanceInfo 10000000 10000000 1 1)]
addressBalances accE 1 1 AddressExternal 0 False
>>= liftIO
. (assertEqual "Address 1 balance is not (20000000, 20000000, 1, 1)")
[(1, BalanceInfo 20000000 20000000 1 1)]
addressBalances accE 0 0 AddressExternal 1 False
>>= liftIO
. (assertEqual "Address 0 1-conf balance is not (0, 0, 0, 0)")
[(0, BalanceInfo 0 0 0 0)]
addressBalances accE 1 1 AddressExternal 1 False
>>= liftIO
. (assertEqual "Address 1 1-conf balance is not (0, 0, 0, 0)")
[(1, BalanceInfo 0 0 0 0)]
-- We re-import tx1. This operation has to be idempotent with respect to
-- balances.
importNetTx tx1
>>= liftIO
. (assertEqual "Confidence is not pending"
([(ai, TxPending)], 0))
. testTx
accountBalance ai 0 False >>=
liftIO . (assertEqual "Balance is not 0") 0
accountBalance ai 1 False >>=
liftIO . (assertEqual "Balance is not 0") 0
addressBalances accE 0 0 AddressExternal 0 False
>>= liftIO
. (assertEqual "Address 0 balance is not (10000000, 10000000, 1, 1)")
[(0, BalanceInfo 10000000 10000000 1 1)]
addressBalances accE 1 1 AddressExternal 0 False
>>= liftIO
. (assertEqual "Address 1 balance is not (20000000, 20000000, 1, 1)")
[(1, BalanceInfo 20000000 20000000 1 1)]
addressBalances accE 0 0 AddressExternal 1 False
>>= liftIO
. (assertEqual "Address 0 1-conf balance is not (0, 0, 0, 0)")
[(0, BalanceInfo 0 0 0 0)]
addressBalances accE 1 1 AddressExternal 1 False
>>= liftIO
. (assertEqual "Address 1 1-conf balance is not (0, 0, 0, 0)")
[(1, BalanceInfo 0 0 0 0)]
-- Importing tx2 twice. This operation has to be idempotent.
importNetTx tx2
>>= liftIO
. (assertEqual "Confidence is not dead"
([(ai, TxDead)], 1))
. testTx
importNetTx tx2
>>= liftIO
. (assertEqual "Confidence is not dead"
([(ai, TxDead)], 0))
. testTx
accountBalance ai 0 False >>=
liftIO . (assertEqual "Balance is not 0") 0
accountBalance ai 1 False >>=
liftIO . (assertEqual "Balance is not 0") 0
addressBalances accE 0 0 AddressExternal 0 False
>>= liftIO
. (assertEqual "Address 0 balance is not (10000000, 10000000, 1, 1)")
[(0, BalanceInfo 10000000 10000000 1 1)]
addressBalances accE 1 1 AddressExternal 0 False
>>= liftIO
. (assertEqual "Address 1 balance is not (20000000, 20000000, 1, 1)")
[(1, BalanceInfo 20000000 20000000 1 1)]
-- Confirm the funding transaction at height 1
importMerkles ((BestChain [fakeNode 1 bid1])) [[txHash fundingTx]]
accountBalance ai 0 False >>=
liftIO . (assertEqual "Balance is not 0") 0
accountBalance ai 1 False >>=
liftIO . (assertEqual "Balance is not 0") 0
accountBalance ai 2 False >>=
liftIO . (assertEqual "Balance is not 0") 0
addressBalances accE 0 0 AddressExternal 0 False
>>= liftIO
. (assertEqual "Address 0 balance is not (10000000, 10000000, 1, 1)")
[(0, BalanceInfo 10000000 10000000 1 1)]
addressBalances accE 1 1 AddressExternal 0 False
>>= liftIO
. (assertEqual "Address 1 balance is not (20000000, 20000000, 1, 1)")
[(1, BalanceInfo 20000000 20000000 1 1)]
addressBalances accE 0 0 AddressExternal 1 False
>>= liftIO
. (assertEqual "Address 0 1-conf balance is not (10000000, 10000000, 1, 1)")
[(0, BalanceInfo 10000000 10000000 1 1)]
addressBalances accE 1 1 AddressExternal 1 False
>>= liftIO
. (assertEqual "Address 1 1-conf balance is not (20000000, 20000000, 1, 1)")
[(1, BalanceInfo 20000000 20000000 1 1)]
-- Confirm tx1 at height 2
importMerkles ((BestChain [fakeNode 2 bid2])) [[txHash tx1]]
accountBalance ai 0 False >>=
liftIO . (assertEqual "Balance is not 0") 0
accountBalance ai 1 False >>=
liftIO . (assertEqual "Balance is not 0") 0
accountBalance ai 2 False >>=
liftIO . (assertEqual "Balance is not 0") 0
accountBalance ai 3 False >>=
liftIO . (assertEqual "Balance is not 0") 0
addressBalances accE 0 0 AddressExternal 2 False
>>= liftIO
. (assertEqual "Address 0 2-conf balance is not (10000000, 10000000, 1, 1)")
[(0, BalanceInfo 10000000 10000000 1 1)]
addressBalances accE 1 1 AddressExternal 2 False
>>= liftIO
. (assertEqual "Address 1 2-conf balance is not (20000000, 20000000, 1, 1)")
[(1, BalanceInfo 20000000 20000000 1 1)]
addressBalances accE 0 0 AddressExternal 3 False
>>= liftIO
. (assertEqual "Address 0 3-conf balance is not (0, 0, 0, 0)")
[(0, BalanceInfo 0 0 0 0)]
addressBalances accE 1 1 AddressExternal 3 False
>>= liftIO
. (assertEqual "Address 1 3-conf balance is not (0, 0, 0, 0)")
[(1, BalanceInfo 0 0 0 0)]
-- Reorg on tx2
let s = fakeNode 1 bid1
o = [fakeNode 2 bid2]
n = [fakeNode 2 bid3, fakeNode 3 bid4]
importMerkles (ChainReorg s o n) [[], [txHash tx2]]
getBy (UniqueAccTx ai (txHash tx1))
>>= liftIO
. (assertEqual "Confidence is not dead" TxDead)
. keyRingTxConfidence . entityVal . fromJust
getBy (UniqueAccTx ai (txHash tx2))
>>= liftIO
. (assertEqual "Confidence is not building" TxBuilding)
. keyRingTxConfidence . entityVal . fromJust
accountBalance ai 0 False >>=
liftIO . (assertEqual "Balance is not 25000000") 25000000
accountBalance ai 1 False >>=
liftIO . (assertEqual "Balance is not 25000000") 25000000
accountBalance ai 2 False >>=
liftIO . (assertEqual "Balance is not 20000000") 20000000
accountBalance ai 3 False >>=
liftIO . (assertEqual "Balance is not 20000000") 20000000
accountBalance ai 4 False >>=
liftIO . (assertEqual "Balance is not 0") 0
addressBalances accE 0 0 AddressExternal 0 False
>>= liftIO
. (assertEqual "Address 0 balance is not (10000000, 10000000, 1, 1)")
[(0, BalanceInfo 10000000 10000000 1 1)]
addressBalances accE 1 1 AddressExternal 0 False
>>= liftIO
. (assertEqual "Address 1 balance is not (20000000, 0, 1, 0)")
[(1, BalanceInfo 20000000 0 1 0)]
addressBalances accE 0 0 AddressExternal 3 False
>>= liftIO
. (assertEqual "Address 0 3-conf balance is not (10000000, 10000000, 1, 1)")
[(0, BalanceInfo 10000000 10000000 1 1)]
addressBalances accE 1 1 AddressExternal 3 False
>>= liftIO
. (assertEqual "Address 1 3-conf balance is not (20000000, 0, 1, 0)")
[(1, BalanceInfo 20000000 0 1 0)]
addressBalances accE 0 0 AddressInternal 0 False
>>= liftIO
. (assertEqual "Address 0 balance is not (5000000, 0, 1, 0)")
[(0, BalanceInfo 5000000 0 1 0)]
addressBalances accE 0 0 AddressInternal 1 False
>>= liftIO
. (assertEqual "Address 0 1-conf balance is not (5000000, 0, 1, 0)")
[(0, BalanceInfo 5000000 0 1 0)]
addressBalances accE 0 0 AddressInternal 2 False
>>= liftIO
. (assertEqual "Address 0 2-conf balance is not (0, 0, 0, 0)")
[(0, BalanceInfo 0 0 0 0)]
-- Reimporting tx2 should be idempotent and return TxBuilding
importNetTx tx2
>>= liftIO
. (assertEqual "Confidence is not building"
([(ai, TxBuilding)], 0))
. testTx
accountBalance ai 0 False >>=
liftIO . (assertEqual "Balance is not 25000000") 25000000
accountBalance ai 1 False >>=
liftIO . (assertEqual "Balance is not 25000000") 25000000
accountBalance ai 2 False >>=
liftIO . (assertEqual "Balance is not 20000000") 20000000
accountBalance ai 3 False >>=
liftIO . (assertEqual "Balance is not 20000000") 20000000
accountBalance ai 4 False >>=
liftIO . (assertEqual "Balance is not 0") 0
addressBalances accE 0 0 AddressExternal 0 False
>>= liftIO
. (assertEqual "Address 0 balance is not (10000000, 10000000, 1, 1)")
[(0, BalanceInfo 10000000 10000000 1 1)]
addressBalances accE 1 1 AddressExternal 0 False
>>= liftIO
. (assertEqual "Address 1 balance is not (20000000, 0, 1, 0)")
[(1, BalanceInfo 20000000 0 1 0)]
addressBalances accE 0 0 AddressExternal 3 False
>>= liftIO
. (assertEqual "Address 0 3-conf balance is not (10000000, 10000000, 1, 1)")
[(0, BalanceInfo 10000000 10000000 1 1)]
addressBalances accE 1 1 AddressExternal 3 False
>>= liftIO
. (assertEqual "Address 1 3-conf balance is not (20000000, 0, 1, 0)")
[(1, BalanceInfo 20000000 0 1 0)]
addressBalances accE 0 0 AddressInternal 0 False
>>= liftIO
. (assertEqual "Address 0 balance is not (5000000, 0, 1, 0)")
[(0, BalanceInfo 5000000 0 1 0)]
addressBalances accE 0 0 AddressInternal 1 False
>>= liftIO
. (assertEqual "Address 0 1-conf balance is not (5000000, 0, 1, 0)")
[(0, BalanceInfo 5000000 0 1 0)]
addressBalances accE 0 0 AddressInternal 2 False
>>= liftIO
. (assertEqual "Address 0 2-conf balance is not (0, 0, 0, 0)")
[(0, BalanceInfo 0 0 0 0)]
-- Reorg back onto tx1
let s2 = fakeNode 1 bid1
o2 = [fakeNode 2 bid3, fakeNode 3 bid4]
n2 = [fakeNode 2 bid2, fakeNode 3 bid5, fakeNode 4 bid6]
importMerkles (ChainReorg s2 o2 n2) [[txHash tx1], [], []]
accountBalance ai 0 False >>=
liftIO . (assertEqual "Balance is not 0") 0
accountBalance ai 1 False >>=
liftIO . (assertEqual "Balance is not 0") 0
accountBalance ai 2 False >>=
liftIO . (assertEqual "Balance is not 0") 0
accountBalance ai 3 False >>=
liftIO . (assertEqual "Balance is not 0") 0
accountBalance ai 4 False >>=
liftIO . (assertEqual "Balance is not 0") 0
accountBalance ai 5 False >>=
liftIO . (assertEqual "Balance is not 0") 0
addressBalances accE 0 0 AddressExternal 0 False
>>= liftIO
. (assertEqual "Address 0 balance is not (10000000, 10000000, 1, 1)")
[(0, BalanceInfo 10000000 10000000 1 1)]
addressBalances accE 1 1 AddressExternal 0 False
>>= liftIO
. (assertEqual "Address 1 balance is not (20000000, 20000000, 1, 1)")
[(1, BalanceInfo 20000000 20000000 1 1)]
addressBalances accE 0 0 AddressExternal 4 False
>>= liftIO
. (assertEqual "Address 0 2-conf balance is not (10000000, 10000000, 1, 1)")
[(0, BalanceInfo 10000000 10000000 1 1)]
addressBalances accE 1 1 AddressExternal 4 False
>>= liftIO
. (assertEqual "Address 1 2-conf balance is not (20000000, 20000000, 1, 1)")
[(1, BalanceInfo 20000000 20000000 1 1)]
addressBalances accE 0 0 AddressExternal 5 False
>>= liftIO
. (assertEqual "Address 0 4-conf balance is not (0, 0, 0, 0)")
[(0, BalanceInfo 0 0 0 0)]
addressBalances accE 1 1 AddressExternal 5 False
>>= liftIO
. (assertEqual "Address 1 4-conf balance is not (0, 0, 0, 0)")
[(1, BalanceInfo 0 0 0 0)]
addressBalances accE 0 0 AddressInternal 0 False
>>= liftIO
. (assertEqual "Address 0 balance is not (0, 0, 0, 0)")
[(0, BalanceInfo 0 0 0 0)]
-- tx1, tx2 and tx3 form a chain, and tx4 is in conflict with tx1
testConflictBalances :: App ()
testConflictBalances = do
keyE <- newKeyRing "test" bs1
accE@(Entity ai _) <- newAccount keyE "acc1" (AccountRegular False) []
let tx1 = fakeTx
[ (tid1, 4) ]
[ ("13XaDQvvE4rqiVKMi4MApsaZwTcDNiwfuR", 10000000) ]
tx2 = fakeTx
[ (txHash tx1, 0) ]
[ ("1MchgrtQEUgV1f7Nqe1vEzvdmBzJHz8zrY", 6000000) -- external
, ("1BwbQ8Wp7YUfaYeiQPgXu6br5e4ogKjuKd", 4000000) -- change
]
tx3 = fakeTx
[ (txHash tx2, 1) ]
[ ("1MchgrtQEUgV1f7Nqe1vEzvdmBzJHz8zrY", 4000000) ] -- external
tx4 = fakeTx
[ (tid1, 4) ]
[ ("13XaDQvvE4rqiVKMi4MApsaZwTcDNiwfuR", 20000000) ]
-- Import first transaction
importNetTx tx1
>>= liftIO
. (assertEqual "Confidence is not pending"
([(ai, TxPending)], 1))
. testTx
accountBalance ai 0 False >>=
liftIO . (assertEqual "Balance is not 10000000") 10000000
accountBalance ai 0 True >>=
liftIO . (assertEqual "Balance is not 10000000") 10000000
accountBalance ai 1 False >>=
liftIO . (assertEqual "Balance is not 0") 0
addressBalances accE 0 0 AddressExternal 0 False
>>= liftIO
. (assertEqual "Address 0 balance is not (10000000, 0, 1, 0)")
[(0, BalanceInfo 10000000 0 1 0)]
-- Import second transaction
importNetTx tx2
>>= liftIO
. (assertEqual "Confidence is not pending"
([(ai, TxPending)], 1))
. testTx
accountBalance ai 0 False
>>= liftIO . (assertEqual "Balance is not 4000000") 4000000
accountBalance ai 1 False
>>= liftIO . (assertEqual "Balance is not 0") 0
addressBalances accE 0 0 AddressExternal 0 False
>>= liftIO
. (assertEqual "Address 0 balance is not (10000000, 10000000, 1, 1)")
[(0, BalanceInfo 10000000 10000000 1 1)]
addressBalances accE 0 0 AddressInternal 0 False
>>= liftIO
. (assertEqual "Address 0 balance is not (4000000, 0, 1, 0)")
[(0, BalanceInfo 4000000 0 1 0)]
-- Let's confirm these two transactions
importMerkles
(BestChain [fakeNode 1 bid1, fakeNode 2 bid2 ])
[[txHash tx1], [txHash tx2]]
accountBalance ai 0 False >>=
liftIO . (assertEqual "Balance is not 4000000") 4000000
accountBalance ai 1 False >>=
liftIO . (assertEqual "Balance is not 4000000") 4000000
accountBalance ai 2 False >>=
liftIO . (assertEqual "Balance is not 0") 0
accountBalance ai 3 False >>=
liftIO . (assertEqual "Balance is not 0") 0
addressBalances accE 0 0 AddressExternal 1 False
>>= liftIO
. (assertEqual "Address 0 1-conf balance is not (10000000, 10000000, 1, 1)")
[(0, BalanceInfo 10000000 10000000 1 1)]
addressBalances accE 0 0 AddressInternal 1 False
>>= liftIO
. (assertEqual "Address 0 1-conf balance is not (4000000, 0, 1, 0)")
[(0, BalanceInfo 4000000 0 1 0)]
addressBalances accE 0 0 AddressExternal 2 False
>>= liftIO
. (assertEqual "Address 0 2-conf balance is not (10000000, 10000000, 1, 1)")
[(0, BalanceInfo 10000000 10000000 1 1)]
addressBalances accE 0 0 AddressInternal 2 False
>>= liftIO
. (assertEqual "Address 0 2-conf balance is not (0, 0, 0, 0)")
[(0, BalanceInfo 0 0 0 0)]
-- Import third transaction
importNetTx tx3
>>= liftIO
. (assertEqual "Confidence is not pending"
([(ai, TxPending)], 0))
. testTx
accountBalance ai 0 False >>=
liftIO . (assertEqual "Balance is not 0") 0
accountBalance ai 1 False >>=
liftIO . (assertEqual "Balance is not 0") 0
accountBalance ai 2 False >>=
liftIO . (assertEqual "Balance is not 0") 0
accountBalance ai 3 False >>=
liftIO . (assertEqual "Balance is not 0") 0
addressBalances accE 0 0 AddressExternal 0 False
>>= liftIO
. (assertEqual "Address 0 balance is not (10000000, 10000000, 1, 1)")
[(0, BalanceInfo 10000000 10000000 1 1)]
addressBalances accE 0 0 AddressInternal 0 False
>>= liftIO
. (assertEqual "Address 0 balance is not (4000000, 4000000, 1, 1)")
[(0, BalanceInfo 4000000 4000000 1 1)]
addressBalances accE 0 0 AddressExternal 1 False
>>= liftIO
. (assertEqual "Address 0 1-conf balance is not (10000000, 10000000, 1, 1)")
[(0, BalanceInfo 10000000 10000000 1 1)]
addressBalances accE 0 0 AddressInternal 1 False
>>= liftIO
. (assertEqual "Address 0 1-conf balance is not (4000000, 4000000, 1, 1)")
[(0, BalanceInfo 4000000 4000000 1 1)]
addressBalances accE 0 0 AddressExternal 2 False
>>= liftIO
. (assertEqual "Address 0 2-conf balance is not (10000000, 10000000, 1, 1)")
[(0, BalanceInfo 10000000 10000000 1 1)]
addressBalances accE 0 0 AddressInternal 2 False
>>= liftIO
. (assertEqual "Address 0 2-conf balance is not (0, 0, 0, 0)")
[(0, BalanceInfo 0 0 0 0)]
-- Now let's add tx4 which is in conflict with tx1
importNetTx tx4
>>= liftIO
. (assertEqual "Confidence is not pending"
([(ai, TxDead)], 0))
. testTx
accountBalance ai 0 False >>=
liftIO . (assertEqual "Balance is not 0") 0
accountBalance ai 1 False >>=
liftIO . (assertEqual "Balance is not 0") 0
accountBalance ai 2 False >>=
liftIO . (assertEqual "Balance is not 0") 0
accountBalance ai 3 False >>=
liftIO . (assertEqual "Balance is not 0") 0
addressBalances accE 0 0 AddressExternal 0 False
>>= liftIO
. (assertEqual "Address 0 balance is not (10000000, 10000000, 1, 1)")
[(0, BalanceInfo 10000000 10000000 1 1)]
addressBalances accE 0 0 AddressInternal 0 False
>>= liftIO
. (assertEqual "Address 0 balance is not (4000000, 4000000, 1, 1)")
[(0, BalanceInfo 4000000 4000000 1 1)]
addressBalances accE 0 0 AddressExternal 1 False
>>= liftIO
. (assertEqual "Address 0 1-conf balance is not (10000000, 10000000, 1, 1)")
[(0, BalanceInfo 10000000 10000000 1 1)]
addressBalances accE 0 0 AddressInternal 1 False
>>= liftIO
. (assertEqual "Address 0 1-conf balance is not (4000000, 4000000, 1, 1)")
[(0, BalanceInfo 4000000 4000000 1 1)]
addressBalances accE 0 0 AddressExternal 2 False
>>= liftIO
. (assertEqual "Address 0 2-conf balance is not (10000000, 10000000, 1, 1)")
[(0, BalanceInfo 10000000 10000000 1 1)]
addressBalances accE 0 0 AddressInternal 2 False
>>= liftIO
. (assertEqual "Address 0 2-conf balance is not (0, 0, 0, 0)")
[(0, BalanceInfo 0 0 0 0)]
-- Now we trigger a reorg that validates tx4. tx1, tx2 and tx3 should be dead
let s = fakeNode 0 bid0
o = [fakeNode 1 bid1, fakeNode 2 bid2]
n = [fakeNode 1 bid3, fakeNode 2 bid4, fakeNode 3 bid5]
importMerkles (ChainReorg s o n) [[], [txHash tx4], []]
getBy (UniqueAccTx ai $ txHash tx1) >>=
liftIO . (assertEqual "tx1 confidence is not dead") (Just TxDead)
. fmap (keyRingTxConfidence . entityVal)
getBy (UniqueAccTx ai $ txHash tx2) >>=
liftIO . (assertEqual "tx2 confidence is not dead") (Just TxDead)
. fmap (keyRingTxConfidence . entityVal)
getBy (UniqueAccTx ai $ txHash tx3) >>=
liftIO . (assertEqual "tx3 confidence is not dead") (Just TxDead)
. fmap (keyRingTxConfidence . entityVal)
accountBalance ai 0 False >>=
liftIO . (assertEqual "Balance is not 20000000") 20000000
accountBalance ai 1 False >>=
liftIO . (assertEqual "Balance is not 20000000") 20000000
accountBalance ai 2 False >>=
liftIO . (assertEqual "Balance is not 20000000") 20000000
accountBalance ai 3 False >>=
liftIO . (assertEqual "Balance is not 0") 0
addressBalances accE 0 0 AddressExternal 0 False
>>= liftIO
. (assertEqual "Address 0 balance is not (20000000, 0, 1, 0)")
[(0, BalanceInfo 20000000 0 1 0)]
addressBalances accE 0 0 AddressInternal 0 False
>>= liftIO
. (assertEqual "Address 0 balance is not (0, 0, 0, 0)")
[(0, BalanceInfo 0 0 0 0)]
addressBalances accE 0 0 AddressExternal 2 False
>>= liftIO
. (assertEqual "Address 0 2-conf balance is not (20000000, 0, 1, 0)")
[(0, BalanceInfo 20000000 0 1 0)]
addressBalances accE 0 0 AddressExternal 3 False
>>= liftIO
. (assertEqual "Address 0 2-conf balance is not (0, 0, 0, 0)")
[(0, BalanceInfo 0 0 0 0)]
-- Reorg back to tx1, tx2 and tx3
let s2 = fakeNode 0 bid0
o2 = [ fakeNode 1 bid3, fakeNode 2 bid4, fakeNode 3 bid5 ]
n2 = [ fakeNode 1 bid1, fakeNode 2 bid2
, fakeNode 3 bid6, fakeNode 4 bid7
]
importMerkles (ChainReorg s2 o2 n2) [[txHash tx1], [txHash tx2], [], []]
getBy (UniqueAccTx ai $ txHash tx1)
>>= liftIO
. (assertEqual "tx1 confidence is not building") (Just TxBuilding)
. fmap (keyRingTxConfidence . entityVal)
getBy (UniqueAccTx ai $ txHash tx2)
>>= liftIO
. (assertEqual "tx2 confidence is not building") (Just TxBuilding)
. fmap (keyRingTxConfidence . entityVal)
-- Tx3 remains dead until it is included into a block. Dead transaction are
-- only revived upon confirmations. They are not revived if they are not
-- confirmed even if they have no conflicts anymore.
getBy (UniqueAccTx ai $ txHash tx3) >>=
liftIO . (assertEqual "tx3 confidence is not dead") (Just TxDead)
. fmap (keyRingTxConfidence . entityVal)
getBy (UniqueAccTx ai $ txHash tx4) >>=
liftIO . (assertEqual "tx4 confidence is not dead") (Just TxDead)
. fmap (keyRingTxConfidence . entityVal)
accountBalance ai 0 False >>=
liftIO . (assertEqual "Balance is not 4000000") 4000000
accountBalance ai 1 False >>=
liftIO . (assertEqual "Balance is not 4000000") 4000000
accountBalance ai 2 False >>=
liftIO . (assertEqual "Balance is not 4000000") 4000000
accountBalance ai 3 False >>=
liftIO . (assertEqual "Balance is not 4000000") 4000000
accountBalance ai 4 False >>=
liftIO . (assertEqual "Balance is not 0") 0
accountBalance ai 5 False >>=
liftIO . (assertEqual "Balance is not 0") 0
addressBalances accE 0 0 AddressExternal 0 False
>>= liftIO
. (assertEqual "Address 0 balance is not (10000000, 10000000, 1, 1)")
[(0, BalanceInfo 10000000 10000000 1 1)]
addressBalances accE 0 0 AddressInternal 0 False
>>= liftIO
. (assertEqual "Address 0 balance is not (4000000, 0, 1, 0)")
[(0, BalanceInfo 4000000 0 1 0)]
addressBalances accE 0 0 AddressExternal 3 False
>>= liftIO
. (assertEqual "Address 0 3-conf balance is not (10000000, 10000000, 1, 1)")
[(0, BalanceInfo 10000000 10000000 1 1)]
addressBalances accE 0 0 AddressInternal 3 False
>>= liftIO
. (assertEqual "Address 0 3-conf balance is not (4000000, 0, 1, 0)")
[(0, BalanceInfo 4000000 0 1 0)]
addressBalances accE 0 0 AddressExternal 4 False
>>= liftIO
. (assertEqual "Address 0 4-conf balance is not (10000000, 10000000, 1, 1)")
[(0, BalanceInfo 10000000 10000000 1 1)]
addressBalances accE 0 0 AddressInternal 4 False
>>= liftIO
. (assertEqual "Address 0 4-conf balance is not (0, 0, 0, 0)")
[(0, BalanceInfo 0 0 0 0)]
addressBalances accE 0 0 AddressExternal 5 False
>>= liftIO
. (assertEqual "Address 0 5-conf balance is not (0, 0, 0, 0)")
[(0, BalanceInfo 0 0 0 0)]
testOffline :: App ()
testOffline = do
keyE <- newKeyRing "test" bs1
accE@(Entity ai _) <- newAccount keyE "acc1" (AccountRegular False) []
let tx1 = fakeTx
[ (tid1, 4) ]
[ ("13XaDQvvE4rqiVKMi4MApsaZwTcDNiwfuR", 10000000) ]
tx2 = fakeTx
[ (txHash tx1, 0) ]
[ ("1MchgrtQEUgV1f7Nqe1vEzvdmBzJHz8zrY", 6000000) -- external
, ("1BwbQ8Wp7YUfaYeiQPgXu6br5e4ogKjuKd", 4000000) -- change
]
tx3 = fakeTx
[ (txHash tx2, 1) ]
[ ("1MchgrtQEUgV1f7Nqe1vEzvdmBzJHz8zrY", 4000000) ] -- external
tx4 = fakeTx
[ (tid1, 4) ]
[ ("13XaDQvvE4rqiVKMi4MApsaZwTcDNiwfuR", 20000000) ]
-- Import first transaction
importTx tx1 ai
>>= liftIO
. (assertEqual "Confidence is not offline"
([(ai, TxOffline)], 1))
. testTx
accountBalance ai 0 False >>=
liftIO . (assertEqual "Balance is not 0") 0
accountBalance ai 0 True >>=
liftIO . (assertEqual "Balance is not 10000000") 10000000
accountBalance ai 1 False >>=
liftIO . (assertEqual "Balance is not 0") 0
addressBalances accE 0 0 AddressExternal 0 False >>=
liftIO . (assertEqual "Address 0 balance is not (0, 0, 0, 0)")
[(0, BalanceInfo 0 0 0 0)]
addressBalances accE 0 0 AddressExternal 0 True >>=
liftIO . (assertEqual "Address 0 balance is not (10000000, 0, 1, 0)")
[(0, BalanceInfo 10000000 0 1 0)]
-- Reimporting a transaction should me idempotent
importTx tx1 ai
>>= liftIO
. (assertEqual "Confidence is not offline"
([(ai, TxOffline)], 0))
. testTx
accountBalance ai 0 False >>=
liftIO . (assertEqual "Balance is not 0") 0
accountBalance ai 0 True >>=
liftIO . (assertEqual "Balance is not 10000000") 10000000
accountBalance ai 1 False >>=
liftIO . (assertEqual "Balance is not 0") 0
addressBalances accE 0 0 AddressExternal 0 False >>=
liftIO . (assertEqual "Address 0 balance is not (0, 0, 0, 0)")
[(0, BalanceInfo 0 0 0 0)]
addressBalances accE 0 0 AddressExternal 0 True >>=
liftIO . (assertEqual "Address 0 balance is not (10000000, 0, 1, 0)")
[(0, BalanceInfo 10000000 0 1 0)]
-- Import tx2
importTx tx2 ai
>>= liftIO
. (assertEqual "Confidence is not offline"
([(ai, TxOffline)], 1))
. testTx
accountBalance ai 0 True >>=
liftIO . (assertEqual "Balance is not 4000000") 4000000
addressBalances accE 0 0 AddressExternal 0 True >>=
liftIO . (assertEqual "Address 0 balance is not (10000000, 10000000, 1, 1)")
[(0, BalanceInfo 10000000 10000000 1 1)]
addressBalances accE 0 0 AddressInternal 0 True >>=
liftIO . (assertEqual "Address 0 balance is not (4000000, 0, 1, 0)")
[(0, BalanceInfo 4000000 0 1 0)]
-- Import tx3
importTx tx3 ai
>>= liftIO
. (assertEqual "Confidence is not offline"
([(ai, TxOffline)], 0))
. testTx
accountBalance ai 0 True >>=
liftIO . (assertEqual "Balance is not 0") 0
addressBalances accE 0 0 AddressExternal 0 True
>>= liftIO
. (assertEqual "Address 0 balance is not (10000000, 10000000, 1, 1)")
[(0, BalanceInfo 10000000 10000000 1 1)]
addressBalances accE 0 0 AddressInternal 0 True
>>= liftIO
. (assertEqual "Address 0 balance is not (4000000, 4000000, 1, 1)")
[(0, BalanceInfo 4000000 4000000 1 1)]
-- Import tx4
importTx tx4 ai
>>= liftIO
. (assertEqual "Confidence is not offline"
([(ai, TxOffline)], 0))
. testTx
getBy (UniqueAccTx ai $ txHash tx1) >>=
liftIO . (assertEqual "tx1 confidence is not dead") (Just TxDead)
. fmap (keyRingTxConfidence . entityVal)
getBy (UniqueAccTx ai $ txHash tx2) >>=
liftIO . (assertEqual "tx2 confidence is not dead") (Just TxDead)
. fmap (keyRingTxConfidence . entityVal)
getBy (UniqueAccTx ai $ txHash tx3) >>=
liftIO . (assertEqual "tx3 confidence is not dead") (Just TxDead)
. fmap (keyRingTxConfidence . entityVal)
accountBalance ai 0 True >>=
liftIO . (assertEqual "Balance is not 20000000") 20000000
addressBalances accE 0 0 AddressExternal 0 True >>=
liftIO . (assertEqual "Address 0 balance is not (20000000, 0, 1, 0)")
[(0, BalanceInfo 20000000 0 1 0)]
addressBalances accE 0 0 AddressInternal 0 True >>=
liftIO . (assertEqual "Address 0 balance is not (0, 0, 0, 0)")
[(0, BalanceInfo 0 0 0 0)]
-- importTx should be idempotent
importTx tx4 ai
>>= liftIO
. (assertEqual "Confidence is not offline"
([(ai, TxOffline)], 0))
. testTx
accountBalance ai 0 True >>=
liftIO . (assertEqual "Balance is not 20000000") 20000000
addressBalances accE 0 0 AddressExternal 0 True >>=
liftIO . (assertEqual "Address 0 balance is not (20000000, 0, 1, 0)")
[(0, BalanceInfo 20000000 0 1 0)]
addressBalances accE 0 0 AddressInternal 0 True >>=
liftIO . (assertEqual "Address 0 balance is not (0, 0, 0, 0)")
[(0, BalanceInfo 0 0 0 0)]
testKillOffline :: App ()
testKillOffline = do
keyE <- newKeyRing "test" bs1
accE@(Entity ai _) <- newAccount keyE "acc1" (AccountRegular False) []
let tx1 = fakeTx
[ (tid1, 4) ]
[ ("13XaDQvvE4rqiVKMi4MApsaZwTcDNiwfuR", 10000000) ]
tx2 = fakeTx
[ (txHash tx1, 0) ]
[ ("1MchgrtQEUgV1f7Nqe1vEzvdmBzJHz8zrY", 6000000) -- external
, ("1BwbQ8Wp7YUfaYeiQPgXu6br5e4ogKjuKd", 4000000) -- change
]
tx3 = fakeTx
[ (txHash tx2, 1) ]
[ ("1MchgrtQEUgV1f7Nqe1vEzvdmBzJHz8zrY", 4000000) ] -- external
tx4 = fakeTx
[ (txHash tx1, 0) ]
[ ("1MchgrtQEUgV1f7Nqe1vEzvdmBzJHz8zrY", 2000000) -- external
, ("1BwbQ8Wp7YUfaYeiQPgXu6br5e4ogKjuKd", 3000000) -- change
, ("13XaDQvvE4rqiVKMi4MApsaZwTcDNiwfuR", 5000000) -- more change
]
-- Import tx1 as a network transaction
importNetTx tx1
>>= liftIO
. (assertEqual "Confidence is not pending"
([(ai, TxPending)], 1))
. testTx
accountBalance ai 0 False >>=
liftIO . (assertEqual "Balance is not 10000000") 10000000
accountBalance ai 0 True >>=
liftIO . (assertEqual "Balance is not 10000000") 10000000
accountBalance ai 1 False >>=
liftIO . (assertEqual "Balance is not 0") 0
addressBalances accE 0 0 AddressExternal 0 False
>>= liftIO
. (assertEqual "Address 0 balance is not (10000000, 0, 1, 0)")
[(0, BalanceInfo 10000000 0 1 0)]
-- Import tx2 as offline
importTx tx2 ai
>>= liftIO
. (assertEqual "Confidence is not offline"
([(ai, TxOffline)], 1))
. testTx
accountBalance ai 0 False >>=
liftIO . (assertEqual "Balance is not 10000000") 10000000
accountBalance ai 0 True >>=
liftIO . (assertEqual "Offline balance is not 4000000") 4000000
accountBalance ai 1 False >>=
liftIO . (assertEqual "1-conf Balance is not 0") 0
accountBalance ai 1 True >>=
liftIO . (assertEqual "1-conf Offline balance is not 0") 0
addressBalances accE 0 0 AddressExternal 0 False
>>= liftIO
. (assertEqual "Address 0 balance is not (10000000, 0, 1, 0)")
[(0, BalanceInfo 10000000 0 1 0)]
addressBalances accE 0 0 AddressInternal 0 False
>>= liftIO
. (assertEqual "Address 0 balance is not (0, 0, 0, 0)")
[(0, BalanceInfo 0 0 0 0)]
addressBalances accE 0 0 AddressExternal 0 True
>>= liftIO
. (assertEqual "Address 0 balance is not (10000000, 10000000, 1 1)")
[(0, BalanceInfo 10000000 10000000 1 1)]
addressBalances accE 0 0 AddressInternal 0 True
>>= liftIO
. (assertEqual "Address 0 balance is not (4000000, 0, 1, 0)")
[(0, BalanceInfo 4000000 0 1 0)]
-- Import tx3 as offline
importTx tx3 ai
>>= liftIO
. (assertEqual "Confidence is not offline"
([(ai, TxOffline)], 0))
. testTx
accountBalance ai 0 False >>=
liftIO . (assertEqual "Balance is not 10000000") 10000000
accountBalance ai 0 True >>=
liftIO . (assertEqual "Offline balance is not 0") 0
accountBalance ai 1 False >>=
liftIO . (assertEqual "1-conf Balance is not 0") 0
accountBalance ai 1 True >>=
liftIO . (assertEqual "1-conf Offline balance is not 0") 0
addressBalances accE 0 0 AddressExternal 0 False
>>= liftIO
. (assertEqual "Address 0 balance is not (10000000, 0, 1, 0)")
[(0, BalanceInfo 10000000 0 1 0)]
addressBalances accE 0 0 AddressInternal 0 False
>>= liftIO
. (assertEqual "Address 0 balance is not (0, 0, 0, 0)")
[(0, BalanceInfo 0 0 0 0)]
addressBalances accE 0 0 AddressExternal 0 True
>>= liftIO
. (assertEqual "Address 0 balance is not (10000000, 10000000, 1 1)")
[(0, BalanceInfo 10000000 10000000 1 1)]
addressBalances accE 0 0 AddressInternal 0 True
>>= liftIO
. (assertEqual "Address 0 balance is not (4000000, 4000000, 1, 1)")
[(0, BalanceInfo 4000000 4000000 1 1)]
-- Import tx4 as a network transaction. It should override tx2 and tx3.
importNetTx tx4
>>= liftIO
. (assertEqual "Confidence is not pending"
([(ai, TxPending)], 0))
. testTx
getBy (UniqueAccTx ai (txHash tx2))
>>= liftIO
. (assertEqual "Confidence is not dead" TxDead)
. keyRingTxConfidence . entityVal . fromJust
getBy (UniqueAccTx ai (txHash tx3))
>>= liftIO
. (assertEqual "Confidence is not dead" TxDead)
. keyRingTxConfidence . entityVal . fromJust
accountBalance ai 0 False >>=
liftIO . (assertEqual "Balance is not 8000000") 8000000
accountBalance ai 0 True >>=
liftIO . (assertEqual "Offline balance is not 8000000") 8000000
accountBalance ai 1 False >>=
liftIO . (assertEqual "1-conf Balance is not 0") 0
accountBalance ai 1 True >>=
liftIO . (assertEqual "1-conf Offline balance is not 0") 0
addressBalances accE 0 0 AddressExternal 0 False
>>= liftIO
. (assertEqual "Address 0 balance is not (15000000, 10000000, 2, 1)")
[(0, BalanceInfo 15000000 10000000 2 1)]
addressBalances accE 0 0 AddressInternal 0 False
>>= liftIO
. (assertEqual "Address 0 balance is not (3000000, 0, 1, 0)")
[(0, BalanceInfo 3000000 0 1 0)]
addressBalances accE 0 0 AddressExternal 0 True
>>= liftIO
. (assertEqual "Address 0 balance is not (15000000, 10000000, 2 1)")
[(0, BalanceInfo 15000000 10000000 2 1)]
addressBalances accE 0 0 AddressInternal 0 True
>>= liftIO
. (assertEqual "Address 0 balance is not (3000000, 0, 1, 0)")
[(0, BalanceInfo 3000000 0 1 0)]
testOfflineExceptions :: Assertion
testOfflineExceptions = do
let tx1 = fakeTx
[ (tid1, 4) ]
[ ("13XaDQvvE4rqiVKMi4MApsaZwTcDNiwfuR", 10000000) ]
tx2 = fakeTx
[ (txHash tx1, 0) ]
[ ("1MchgrtQEUgV1f7Nqe1vEzvdmBzJHz8zrY", 6000000) -- external
, ("1BwbQ8Wp7YUfaYeiQPgXu6br5e4ogKjuKd", 4000000) -- change
]
tx3 = fakeTx
[ (txHash tx2, 1) ]
[ ("1MchgrtQEUgV1f7Nqe1vEzvdmBzJHz8zrY", 4000000) ] -- external
tx4 = fakeTx
[ (tid1, 4) ]
[ ("13XaDQvvE4rqiVKMi4MApsaZwTcDNiwfuR", 20000000) ]
assertException (WalletException "Could not import offline transaction") $ do
keyE <- newKeyRing "test" bs1
_ <- newAccount keyE "acc1" (AccountRegular False) []
(_, Entity ai _) <- getAccount "test" "acc1"
importNetTx tx1
>>= liftIO
. (assertEqual "Confidence is not pending"
([(ai, TxPending)], 1))
. testTx
importTx tx4 ai
assertException (WalletException "Could not import offline transaction") $ do
keyE <- newKeyRing "test" bs1
_ <- newAccount keyE "acc1" (AccountRegular False) []
(_, Entity ai _) <- getAccount "test" "acc1"
importNetTx tx4
>>= liftIO
. (assertEqual "Confidence is not pending"
([(ai, TxPending)], 1))
. testTx
importNetTx tx1
>>= liftIO
. (assertEqual "Confidence is not dead"
([(ai, TxDead)], 0))
. testTx
importNetTx tx2
>>= liftIO
. (assertEqual "Confidence is not dead"
([(ai, TxDead)], 1))
. testTx
importTx tx3 ai
assertException (WalletException "Could not import offline transaction") $ do
keyE <- newKeyRing "test" bs1
_ <- newAccount keyE "acc1" (AccountRegular False) []
(_, Entity ai _) <- getAccount "test" "acc1"
importNetTx tx1
>>= liftIO
. (assertEqual "Confidence is not pending"
([(ai, TxPending)], 1))
. testTx
importTx tx1 ai
-- This test create a multisig account with the key of testImportMultisig2
testImportMultisig :: App ()
testImportMultisig = do
keyE <- newKeyRing "test" bs1
_ <- newAccount keyE "ms1" (AccountMultisig False 2 2)
[fromJust $ xPubImport "xpub69iinth3CTrfkmijzhQXi3kwhGQjba31fncrBgA9vM9T9tv69qSwp525yDVYmX2BTAdeuYSZqkcWhkrqD5Xbsz5YHJZL6CzYGL2WACorpdS"]
_ <- newAccount keyE "ms2" (AccountMultisig False 2 2)
[fromJust $ xPubImport "xpub69iinth3CTrfh5efv7baTWwk9hHi4zqcQEsNFgVwEJvdaZVEPytZzmNxjYTnF5F5x2CamLXvmD1T4RhpsuaXSFPo2MnLN5VqWqrWb82U7ED"]
Entity _ keyRing <- getKeyRing "test"
(_, accE1@(Entity ai1 _)) <- getAccount "test" "ms1"
(_, accE2@(Entity ai2 _)) <- getAccount "test" "ms2"
let fundingTx =
Tx 1 [ TxIn (OutPoint tid1 0) (BS.pack [1]) maxBound ] -- dummy input
[ TxOut 10000000 $
encodeOutputBS $ PayScriptHash $ fromJust $
base58ToAddr "3Dgz9gqsAMPr7i9qocLMNHU8wuoKqtUNoM"
] 0
importNetTx fundingTx
>>= liftIO
. (assertEqual "Confidence is not pending"
([(ai1, TxPending), (ai2, TxPending)], 2))
. testTx
-- Create a transaction which has 0 signatures in ms1
(tx1, _) <- createTx keyRing accE1
[ ( fromJust $ base58ToAddr "3C9fz8kDwX2rV25YeWC7YcDNHtTreAV52m"
, 5000000
)
] 10000 0 False True
liftIO $ assertEqual "Confidence is not offline" TxOffline $
keyRingTxConfidence tx1
spendableCoins ai1 0 (const . const [])
>>= liftIO
. (assertEqual "Wrong txhash in coins" [])
. map (keyRingCoinHash . entityVal . inCoinDataCoin)
txPage ai1 (PageRequest 1 10 False)
>>= liftIO
. (assertEqual "Wrong txhash in tx list"
[txHash fundingTx, keyRingTxHash tx1])
. (map keyRingTxHash) . fst
accountBalance ai1 0 False >>=
liftIO . (assertEqual "Balance is not 10000000") 10000000
accountBalance ai1 1 False >>=
liftIO . (assertEqual "Balance is not 0") 0
accountBalance ai1 0 True >>=
liftIO . (assertEqual "Offline balance is not 9990000") 9990000
-- Import the empty transaction in ms2
(tx2:_, _) <- importTx (keyRingTxTx tx1) ai2
-- This second import should be idempotent
_ <- importTx (keyRingTxTx tx1) ai2
liftIO $ assertEqual "Txid do not match"
(keyRingTxHash tx1) (keyRingTxHash tx2)
liftIO $ assertEqual "Confidence is not offline" TxOffline $
keyRingTxConfidence tx2
spendableCoins ai2 0 (const . const [])
>>= liftIO
. (assertEqual "Wrong txhash in coins" [])
. map (keyRingCoinHash . entityVal . inCoinDataCoin)
txPage ai2 (PageRequest 1 10 False)
>>= liftIO
. (assertEqual "Wrong txhash in tx list"
[txHash fundingTx, keyRingTxHash tx2])
. (map keyRingTxHash) . fst
accountBalance ai2 0 False >>=
liftIO . (assertEqual "Balance is not 10000000") 10000000
accountBalance ai2 1 False >>=
liftIO . (assertEqual "Balance is not 0") 0
accountBalance ai2 0 True >>=
liftIO . (assertEqual "Balance is not 9990000") 9990000
-- Sign the transaction in ms2
(tx3:_, _) <- signKeyRingTx keyRing accE2 $ keyRingTxHash tx2
liftIO $ assertEqual "Confidence is not pending" TxPending $
keyRingTxConfidence tx3
spendableCoins ai2 0 (const . const [])
>>= liftIO
. (assertEqual "Wrong txhash in coins"
[keyRingTxHash tx3, keyRingTxHash tx3])
. map (keyRingCoinHash . entityVal . inCoinDataCoin)
txPage ai2 (PageRequest 1 10 False)
>>= liftIO
. (assertEqual "Wrong txhash in tx list"
[txHash fundingTx, keyRingTxHash tx3])
. (map keyRingTxHash) . fst
accountBalance ai2 0 False >>=
liftIO . (assertEqual "Balance is not 9990000") 9990000
accountBalance ai2 1 False >>=
liftIO . (assertEqual "Balance is not 0") 0
accountBalance ai2 0 True >>=
liftIO . (assertEqual "Balance is not 9990000") 9990000
tx4 <- liftM (entityVal . fromJust) $
getBy $ UniqueAccTx ai1 $ keyRingTxHash tx3
liftIO $ assertEqual "Confidence is not pending" TxPending $
keyRingTxConfidence tx4
spendableCoins ai1 0 (const . const [])
>>= liftIO
. (assertEqual "Wrong txhash in coins"
[keyRingTxHash tx3, keyRingTxHash tx3])
. map (keyRingCoinHash . entityVal . inCoinDataCoin)
txPage ai1 (PageRequest 1 10 False)
>>= liftIO
. (assertEqual "Wrong txhash in tx list"
[txHash fundingTx, keyRingTxHash tx3])
. (map keyRingTxHash ) . fst
accountBalance ai1 0 False >>=
liftIO . (assertEqual "Balance is not 9990000") 9990000
accountBalance ai1 1 False >>=
liftIO . (assertEqual "Balance is not 0") 0
accountBalance ai1 0 True >>=
liftIO . (assertEqual "Balance is not 9990000") 9990000
-- Importing the transaction should have no effect as it was globally
-- imported already in the previous step.
(tx5:_, _) <- importTx (keyRingTxTx tx3) ai1
liftIO $ assertEqual "Confidence is not pending" TxPending $
keyRingTxConfidence tx5
spendableCoins ai1 0 (const . const [])
>>= liftIO
. (assertEqual "Wrong txhash in coins"
[keyRingTxHash tx5, keyRingTxHash tx5])
. map (keyRingCoinHash . entityVal . inCoinDataCoin)
txPage ai1 (PageRequest 1 10 False)
>>= liftIO
. (assertEqual "Wrong txhash in tx list"
[txHash fundingTx, keyRingTxHash tx5])
. (map keyRingTxHash) . fst
accountBalance ai1 0 False >>=
liftIO . (assertEqual "Balance is not 9990000") 9990000
accountBalance ai1 1 False >>=
liftIO . (assertEqual "Balance is not 0") 0
accountBalance ai1 0 True >>=
liftIO . (assertEqual "Balance is not 9990000") 9990000
testKillTx :: App ()
testKillTx = do
keyE <- newKeyRing "test" bs1
_ <- newAccount keyE "acc1" (AccountRegular False) []
(_, accE@(Entity ai _)) <- getAccount "test" "acc1"
let tx1 = fakeTx
[ (tid1, 4) ]
[ ("13XaDQvvE4rqiVKMi4MApsaZwTcDNiwfuR", 10000000) ]
tx2 = fakeTx
[ (txHash tx1, 0) ]
[ ("1MchgrtQEUgV1f7Nqe1vEzvdmBzJHz8zrY", 6000000) -- external
, ("1BwbQ8Wp7YUfaYeiQPgXu6br5e4ogKjuKd", 4000000) -- change
]
tx3 = fakeTx
[ (txHash tx2, 1) ]
[ ("1MchgrtQEUgV1f7Nqe1vEzvdmBzJHz8zrY", 4000000) ] -- external
importNetTx tx1
>>= liftIO
. (assertEqual "Confidence is not pending" ([(ai, TxPending)], 1))
. testTx
importNetTx tx2
>>= liftIO
. (assertEqual "Confidence is not pending" ([(ai, TxPending)], 1))
. testTx
importNetTx tx3
>>= liftIO
. (assertEqual "Confidence is not pending" ([(ai, TxPending)], 0))
. testTx
accountBalance ai 0 True >>=
liftIO . (assertEqual "Balance is not 0") 0
addressBalances accE 0 0 AddressExternal 0 True >>=
liftIO . (assertEqual "Address 0 balance is not (10000000, 10000000, 1, 1)")
[(0, BalanceInfo 10000000 10000000 1 1)]
addressBalances accE 0 0 AddressInternal 0 True >>=
liftIO . (assertEqual "Address 0 balance is not (4000000, 4000000, 1, 1)")
[(0, BalanceInfo 4000000 4000000 1 1)]
killTxs [txHash tx2]
accountBalance ai 0 True >>=
liftIO . (assertEqual "Balance is not 10000000") 10000000
addressBalances accE 0 0 AddressExternal 0 True >>=
liftIO . (assertEqual "Address 0 balance is not (10000000, 0, 1, 0)")
[(0, BalanceInfo 10000000 0 1 0)]
addressBalances accE 0 0 AddressInternal 0 True >>=
liftIO . (assertEqual "Address 0 balance is not (0, 0, 0, 0)")
[(0, BalanceInfo 0 0 0 0)]
-- Killing a transaction should be idempotent
killTxs [txHash tx2]
accountBalance ai 0 True >>=
liftIO . (assertEqual "Balance is not 10000000") 10000000
addressBalances accE 0 0 AddressExternal 0 True >>=
liftIO . (assertEqual "Address 0 balance is not (10000000, 0, 1, 0)")
[(0, BalanceInfo 10000000 0 1 0)]
addressBalances accE 0 0 AddressInternal 0 True >>=
liftIO . (assertEqual "Address 0 balance is not (0, 0, 0, 0)")
[(0, BalanceInfo 0 0 0 0)]
killTxs [txHash tx3]
accountBalance ai 0 True >>=
liftIO . (assertEqual "Balance is not 10000000") 10000000
addressBalances accE 0 0 AddressExternal 0 True >>=
liftIO . (assertEqual "Address 0 balance is not (10000000, 0, 1, 0)")
[(0, BalanceInfo 10000000 0 1 0)]
addressBalances accE 0 0 AddressInternal 0 True >>=
liftIO . (assertEqual "Address 0 balance is not (0, 0, 0, 0)")
[(0, BalanceInfo 0 0 0 0)]
reviveTx tx2
accountBalance ai 0 True >>=
liftIO . (assertEqual "Balance is not 4000000") 4000000
addressBalances accE 0 0 AddressExternal 0 True >>=
liftIO . (assertEqual "Address 0 balance is not (10000000, 10000000, 1, 1)")
[(0, BalanceInfo 10000000 10000000 1 1)]
addressBalances accE 0 0 AddressInternal 0 True >>=
liftIO . (assertEqual "Address 0 balance is not (4000000, 0, 1, 0)")
[(0, BalanceInfo 4000000 0 1 0)]
-- Reviving a transaction should be idempotent
reviveTx tx2
accountBalance ai 0 True >>=
liftIO . (assertEqual "Balance is not 4000000") 4000000
addressBalances accE 0 0 AddressExternal 0 True >>=
liftIO . (assertEqual "Address 0 balance is not (10000000, 10000000, 1, 1)")
[(0, BalanceInfo 10000000 10000000 1 1)]
addressBalances accE 0 0 AddressInternal 0 True >>=
liftIO . (assertEqual "Address 0 balance is not (4000000, 0, 1, 0)")
[(0, BalanceInfo 4000000 0 1 0)]
testTx :: ([KeyRingTx], [KeyRingAddr])
-> ([(KeyRingAccountId, TxConfidence)], Int)
testTx (txs, addrs) = (map f txs, length addrs)
where
f tx = (keyRingTxAccount tx, keyRingTxConfidence tx)