forked from digibyte/digibyte
-
Notifications
You must be signed in to change notification settings - Fork 65
/
p2p_dandelion.py
executable file
·149 lines (126 loc) · 5.64 KB
/
p2p_dandelion.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
#!/usr/bin/env python3
# Copyright (c) 2018 Bradley Denby
# Distributed under the MIT software license. See the accompanying file COPYING
# or http://www.opensource.org/licenses/mit-license.php.
"""Test transaction behaviors under the Dandelion spreading policy
Tests:
1. Resistance to active probing:
Stem: 0 --> 1 --> 2 --> 0 where each node has argument "-dandelion=1"
Probe: TestNode --> 0
Node 0 generates a Dandelion transaction "tx": 1.0 BTC from Node 0 to Node 2
TestNode immediately sends getdata for tx to Node 0
Assert that Node 0 does not reply with tx
2. Loop behavior:
Stem: 0 --> 1 --> 2 --> 0 where each node has argument "-dandelion=1"
Probe: TestNode --> 0
Wait ~5 seconds after Test 1, then TestNode sends getdata for tx to Node 0
Assert that Node 0 does not reply with tx
3. Resistance to black holes:
Stem: 0 --> 1 --> 2 --> 0 where each node has argument "-dandelion=1"
Probe: TestNode --> 0
Wait ~45 seconds after Test 2, then TestNode sends getdata for tx to Node 0
Assert that Node 0 replies with tx
"""
from test_framework.p2p import P2PInterface
from test_framework.test_framework import DigiByteTestFramework
from test_framework.blocktools import COINBASE_MATURITY_2
from test_framework.messages import (
CTransaction,
tx_from_hex,
msg_getdata,
CInv,
)
import time # sleep
class TestP2PConn(P2PInterface):
def __init__(self):
super().__init__()
def send_dandeliontx_getdata(self, dandeliontx_hash):
msg = msg_getdata()
msg.inv.append(CInv(5, dandeliontx_hash)) # 5: "DandelionTx"
self.send_message(msg)
class DandelionTest(DigiByteTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
self.num_nodes = 3
self.extra_args = []
for i in range(self.num_nodes):
self.extra_args.append(["-dandelion=1"]) # ,"-debug=dandelion","-printtoconsole=1"
def setup_network(self):
self.setup_nodes()
# Tests 1,2,3: 0 --> 1 --> 2 --> 0
self.connect_nodes(0, 1)
self.connect_nodes(1, 2)
self.connect_nodes(2, 0)
self.nodes[0].createwallet(wallet_name='w0', load_on_startup=True)
self.nodes[1].createwallet(wallet_name='w1', load_on_startup=True)
self.nodes[2].createwallet(wallet_name='w2', load_on_startup=True)
def run_test(self):
# Convenience variables
node0, node1, node2 = self.nodes
w0 = node0.get_wallet_rpc('w0')
w1 = node0.get_wallet_rpc('w1')
w2 = node0.get_wallet_rpc('w2')
# Setup TestP2PConns
test_node0 = node0.add_p2p_connection(TestP2PConn())
# Start networking thread
test_node0.wait_for_verack()
# Get out of Initial Block Download (IBD)
for node in self.nodes:
self.generatetoaddress(node, 1, w0.getnewaddress(), sync_fun=self.no_op)
# Generate funds for node0
self.generate(node0, COINBASE_MATURITY_2 + 1)
# Tests 1,2,3
# There is a low probability that one of these tests will fail even if
# the implementation is correct. Thus, these tests are repeated upon
# failure. A true bug will result in repeated failures.
self.log.info('Starting tests...')
test_1_passed = False
test_2_passed = False
test_3_passed = False
tries_left = 5
while(not (test_1_passed and test_2_passed and test_3_passed) and tries_left > 0):
tries_left -= 1
# Test 1: Resistance to active probing
test_node0.message_count['notfound'] = 0
node0_txid = node0.sendtoaddress(node2.getnewaddress(), 1.0)
node0_tx = tx_from_hex(node0.gettransaction(node0_txid)['hex'])
test_node0.send_dandeliontx_getdata(node0_tx.calc_sha256(True))
time.sleep(1)
try:
assert(test_node0.message_count['notfound']==1)
if not test_1_passed:
test_1_passed = True
self.log.info('Success: resistance to active probing')
except AssertionError:
if not test_1_passed and tries_left == 0:
self.log.info('Failed: resistance to active probing')
# Test 2: Loop behavior
test_node0.message_count['notfound'] = 0
time.sleep(3)
test_node0.send_dandeliontx_getdata(node0_tx.calc_sha256(True))
time.sleep(1)
try:
assert(test_node0.message_count['notfound']==1)
if not test_2_passed:
test_2_passed = True
self.log.info('Success: loop behavior')
except AssertionError:
if not test_2_passed and tries_left == 0:
self.log.info('Failed: loop behavior')
# Test 3: Resistance to black holes
test_node0.message_count['tx'] = 0
time.sleep(44)
test_node0.send_dandeliontx_getdata(node0_tx.calc_sha256(True))
time.sleep(1)
try:
assert(test_node0.message_count['tx']==1)
if not test_3_passed:
test_3_passed = True
self.log.info('Success: resistance to black holes')
except AssertionError:
if not test_3_passed and tries_left == 0:
self.log.info('Failed: resistance to black holes')
all_tests_passed = test_1_passed and test_2_passed and test_3_passed
assert(all_tests_passed)
if __name__ == '__main__':
DandelionTest().main()