-
Notifications
You must be signed in to change notification settings - Fork 9
/
test_coinswap.py
197 lines (177 loc) · 8.22 KB
/
test_coinswap.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
#!/usr/bin/env python
from __future__ import print_function
import jmbitcoin as btc
import pytest
import sys
import os
data_dir = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
sys.path.insert(0, os.path.join(data_dir))
from coinswap import cs_single, get_log, load_coinswap_config
from jmclient import sync_wallet
from bad_participants import (BadAlice, BadCarol,
AliceBadHandshake, AliceWrongSecret,
AliceBadNegotiate, AliceBadCompleteNegotiation,
AliceFailSendTX0id, AliceFailReceiveTX1id,
AliceBadTX3Sig, AliceNoBrTX0, AliceBadTX01Monitor,
AliceFailReceiveTX5Sig,
CarolBadHandshake, CarolBadNegotiate,
CarolFailSendTX1id, CarolFailReceiveTX3Sig,
CarolNoBrTX1, CarolFailReceiveSecret,
CarolBadSendTX5Sig, CarolFailReceiveTX4Sig)
from twisted.internet import reactor, task
from twisted.python import log
from commontest import make_wallets, make_sign_and_push
from coinswap_run import main_cs
cslog = get_log()
alice_classes = {"cooperative": None,
"fakesecret": AliceWrongSecret,
"badhandshake": AliceBadHandshake,
"badncs": AliceBadNegotiate,
"badcompleten": AliceBadCompleteNegotiation,
"badsendtx0id": AliceFailSendTX0id,
"badreceivetx1id": AliceFailReceiveTX1id,
"badsendtx3sig": AliceBadTX3Sig,
"nobroadcasttx0": AliceNoBrTX0,
"notx01monitor": AliceBadTX01Monitor,
"badreceivetx5sig": AliceFailReceiveTX5Sig,
"rfakesecret": BadAlice}
carol_classes = {"cbadhandshake": CarolBadHandshake,
"cbadnegotiate": CarolBadNegotiate,
"cbadsendtx1id": CarolFailSendTX1id,
"cbadreceivetx3sig": CarolFailReceiveTX3Sig,
"cnobroadcasttx1": CarolNoBrTX1,
"cbadreceivesecret": CarolFailReceiveSecret,
"cbadsendtx5sig": CarolBadSendTX5Sig,
"cbadreceivetx4sig": CarolFailReceiveTX4Sig}
alice_funds_not_moved_cases = ["badhandshake", "badncs", "badcompleten",
"badsendtx0id", "badreceivetx1id",
"badsendtx3sig", "nobroadcasttx0",
"cbadreceivetx3sig",
"cbadhandshake", "cbadnegotiate", "cbadsendtx1id"]
alice_recover_cases = {}
for i in range(3, 12):
rt = "ra" + str(i)
alice_recover_cases[rt] = i
alice_classes[rt] = BadAlice
if i < 7:
alice_funds_not_moved_cases.append(rt)
carol_funds_not_moved_cases = ["cnobroadcasttx1"]
carol_recover_cases = {}
for i in range(3, 10):
rt = "rc" + str(i)
carol_recover_cases[rt] = i
carol_classes[rt] = BadCarol
if i < 5:
alice_funds_not_moved_cases.append(rt)
if i < 6:
carol_funds_not_moved_cases.append(rt)
carol_funds_not_moved_cases += alice_funds_not_moved_cases
"""parametrize is not allowed with injected config vars from command line;
also, multiple runs of the reactor is not supported. So, we just statically
set the necessary parameters.
"""
num_alices = 1
wallet_structures = [[1, 0, 0]]*2
amounts = [10000000]
funding_amount = 2.0
dest_addr = None
fixed_seeds = False
#fees are low on regtest; tests will fail if larger than this
reasonable_fee_maximum = 20000
class Options(object):
recover = False
serverport = "http://127.0.0.1:7080"
fastsync= False
serve = True
def miner():
cs_single().bc_interface.tick_forward_chain(1)
def start_mining(l):
l.start(4.0)
def runcase(alice_class, carol_class, fail_alice_state=None, fail_carol_state=None):
options_server = Options()
wallets = make_wallets(num_alices + 1,
wallet_structures=wallet_structures,
mean_amt=funding_amount)
args_server = ["dummy"]
test_data_server = (wallets[num_alices]['seed'], args_server, options_server,
False, None, carol_class, None, fail_carol_state)
carol_bbmb = main_cs(test_data_server)
options_alice = Options()
options_alice.serve = False
alices = []
for i in range(num_alices):
args_alice = ["dummy", amounts[i]]
if dest_addr:
args_alice.append(dest_addr)
test_data_alice = (wallets[i]['seed'], args_alice, options_alice, False,
alice_class, None, fail_alice_state, None)
alices.append(main_cs(test_data_alice))
l = task.LoopingCall(miner)
reactor.callWhenRunning(start_mining, l)
reactor.run()
return (alices, carol_bbmb, wallets[num_alices]['wallet'])
def test_run_both(setup_wallets, runtype):
#hack to account for the fact that Carol does not even run
#if the handshake is bad; this is done to force the reactor to stop.
if runtype == "badhandshake":
cs_single().num_entities_running = 1
#The setup of each test case is the same; the only difference is the
#participant classes (only Alice for now)
ac = alice_classes[runtype] if runtype in alice_classes else None
cc = carol_classes[runtype] if runtype in carol_classes else None
fail_alice_state = alice_recover_cases[
runtype] if runtype in alice_recover_cases else None
fail_carol_state = carol_recover_cases[
runtype] if runtype in carol_recover_cases else None
alices, carol_bbmb, carol_wallet = runcase(ac, cc, fail_alice_state,
fail_carol_state)
#test case function will only return on reactor shutdown; Alice and Carol
#objects are set at the start, but are references so updated.
#Check the wallet states reflect the expected updates.
#TODO handle multiple alices with different amounts against one Carol.
if runtype == "badhandshake":
for a in alices:
a.bbma = a.wallet.get_balance_by_mixdepth(verbose=False)
expected_spent = reasonable_fee_maximum*4 + cs_single(
).config.getint("SERVER", "minimum_coinswap_fee")
if runtype in alice_funds_not_moved_cases:
for i, alice in enumerate(alices):
assert alice.bbmb[0] == alice.bbma[0]
elif runtype in ["cooperative", "cbadreceivetx4sig", "ra11", "rc8", "rc9"]:
#in all of these cases Alice's payment is complete
for i, alice in enumerate(alices):
funds_spent = alice.bbmb[0] - alice.bbma[0]
funds_received = alice.bbma[1] - alice.bbmb[1]
assert funds_spent - funds_received <= expected_spent + reasonable_fee_maximum
else:
#Ensure Alice did not pay too much and only spent back to 0 depth
for i, alice in enumerate(alices):
assert alice.bbma[1] == 0
funds_spent = alice.bbmb[0] - alice.bbma[0]
assert funds_spent <= expected_spent
#Carol is handled a bit differently, since Carol instances are initiated on
#the fly, we instead query the wallet object directly for the final balances.
sync_wallet(carol_wallet)
carol_bbma = carol_wallet.get_balance_by_mixdepth(verbose=False)
if runtype in carol_funds_not_moved_cases:
assert carol_bbma[0] >= carol_bbmb[0]
assert carol_bbma[0] - carol_bbmb[0] <= reasonable_fee_maximum + cs_single(
).config.getint("SERVER", "minimum_coinswap_fee")
elif runtype in ["cooperative", "rc9"]:
funds_spent = carol_bbmb[0] - carol_bbma[0]
funds_received = carol_bbma[1] - carol_bbmb[1]
assert funds_received - funds_spent >= cs_single(
).config.getint("SERVER", "minimum_coinswap_fee") - reasonable_fee_maximum
else:
#All cases of backout and funds have moved
assert carol_bbmb[1] == 0
#Here we assert carol did not lose money; the alice checks are sufficient
#to ensure carol didn't get too much
assert carol_bbma[0] - carol_bbmb[0] > 0
@pytest.fixture(scope="module")
def setup_wallets():
log.startLogging(sys.stdout)
load_coinswap_config()
#need to give up waiting for confirms artificially quickly
cs_single().one_confirm_timeout = 20
cs_single().num_entities_running = 0