-
Notifications
You must be signed in to change notification settings - Fork 56
/
clienttools.py
209 lines (152 loc) · 7.15 KB
/
clienttools.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
198
199
200
201
202
203
204
205
206
207
#!/usr/bin/env python3
"""
@summary: tools to talk to an Ethereum client node
@version: v46 (03/January/2019)
@since: 19/June/2018
@organization:
@author: https://github.com/drandreaskrueger
@see: https://github.com/drandreaskrueger/chainhammer for updates
"""
################
## Dependencies:
import os
from pprint import pprint
try:
from web3 import Web3, HTTPProvider # pip3 install web3
except:
print ("Dependencies unavailable. Start virtualenv first!")
exit()
# extend sys.path for imports:
if __name__ == '__main__' and __package__ is None:
from os import sys, path
sys.path.append(path.dirname(path.dirname(path.abspath(__file__))))
from hammer.config import RPCaddress
from hammer.config import FILE_PASSPHRASE, PARITY_UNLOCK_EACH_TRANSACTION, PARITY_ALREADY_UNLOCKED
from hammer.clienttype import clientType
################
## Tools:
def printVersions():
import sys
from web3 import __version__ as web3version
from solc import get_solc_version
from testrpc import __version__ as ethtestrpcversion
import pkg_resources
pysolcversion = pkg_resources.get_distribution("py-solc").version
print ("versions: web3 %s, py-solc: %s, solc %s, testrpc %s, python %s" % (web3version, pysolcversion, get_solc_version(), ethtestrpcversion, sys.version.replace("\n", "")))
################################################################################
# get a connection, and find out as much as possible
def start_web3connection(RPCaddress=None, account=None):
"""
get a web3 object, and make it global
"""
global w3
if RPCaddress:
# HTTP provider
# (TODO: also try whether IPC provider is faster, when quorum-outside-vagrant starts working)
w3 = Web3(HTTPProvider(RPCaddress, request_kwargs={'timeout': 120}))
else:
# w3 = Web3(Web3.EthereumTesterProvider()) # does NOT work!
w3 = Web3(Web3.TestRPCProvider())
print ("web3 connection established, blockNumber =", w3.eth.blockNumber, end=", ")
print ("node version string = ", w3.version.node)
accountname="chosen"
if not account:
w3.eth.defaultAccount = w3.eth.accounts[0] # set first account as sender
accountname="first"
print (accountname + " account of node is", w3.eth.defaultAccount, end=", ")
print ("balance is %s Ether" % w3.fromWei(w3.eth.getBalance(w3.eth.defaultAccount), "ether"))
return w3
def setGlobalVariables_clientType(w3):
"""
Set global variables.
"""
global NODENAME, NODETYPE, NODEVERSION, CONSENSUS, NETWORKID, CHAINNAME, CHAINID
NODENAME, NODETYPE, NODEVERSION, CONSENSUS, NETWORKID, CHAINNAME, CHAINID = clientType(w3)
formatter="nodeName: %s, nodeType: %s, nodeVersion: %s, consensus: %s, network: %s, chainName: %s, chainId: %s"
print (formatter % (NODENAME, NODETYPE, NODEVERSION, CONSENSUS, NETWORKID, CHAINNAME, CHAINID))
return NODENAME, NODETYPE, NODEVERSION, CONSENSUS, NETWORKID, CHAINNAME, CHAINID # for when imported into other modules
def if_poa_then_bugfix(w3, NODENAME, CHAINNAME, CONSENSUS):
"""
bugfix for quorum web3.py problem, see
https://github.com/ethereum/web3.py/issues/898#issuecomment-396701172
and
https://web3py.readthedocs.io/en/stable/middleware.html#geth-style-proof-of-authority
actually also appeared when using dockerized standard geth nodes with PoA
https://github.com/javahippie/geth-dev (net_version='500')
"""
if NODENAME == "Quorum" or CHAINNAME=='500' or CONSENSUS=='clique':
from web3.middleware import geth_poa_middleware
# inject the poa compatibility middleware to the innermost layer
w3.middleware_stack.inject(geth_poa_middleware, layer=0)
# def web3connection(RPCaddress=RPCaddress, account=None):
def web3connection(RPCaddress=None, account=None):
"""
prints dependency versions, starts web3 connection, identifies client node type, if quorum then bugfix
"""
printVersions()
w3 = start_web3connection(RPCaddress=RPCaddress, account=account)
NODENAME, NODETYPE, NODEVERSION, CONSENSUS, NETWORKID, CHAINNAME, CHAINID = setGlobalVariables_clientType(w3)
if_poa_then_bugfix(w3, NODENAME, CHAINNAME, CONSENSUS)
chainInfos = NODENAME, NODETYPE, NODEVERSION, CONSENSUS, NETWORKID, CHAINNAME, CHAINID
return w3, chainInfos
################################################################################
# generally useful tools
def getBlockTransactionCount(w3, blockNumber):
"""
testRPC does not provide this endpoint yet, so replicate its functionality:
"""
block=w3.eth.getBlock(blockNumber)
# pprint (block)
return len(block["transactions"])
def correctPath(file):
"""
This is a semi-dirty hack for FILE_PASSPHRASE (="account-passphrase.txt")
to repair the FileNotFound problem which only appears when running the tests
because then the currentWorkDir is "chainhammer" not "chainhammer/hammer"
P.S.: If ever consistent solution, then also fix for the two
"contract-{abi,address}.json" which tests put into the root folder
"""
# print ("os.getcwd():", os.getcwd())
if os.getcwd().split("/")[-1] != "hammer":
return os.path.join("hammer", file)
else:
return file
def unlockAccount(duration=3600, account=None):
"""
unlock once, then leave open, to later not loose time for unlocking
"""
if ("TestRPC" in w3.version.node) or (PARITY_ALREADY_UNLOCKED and ("Parity" in w3.version.node)):
return True # TestRPC does not need unlocking; or parity can be CLI-switch unlocked when starting
if NODENAME=="Quorum":
passphrase=""
else:
# print ("os.getcwd():", os.getcwd())
with open(correctPath(FILE_PASSPHRASE), "r") as f:
passphrase=f.read().strip()
if NODENAME=="Geth" and CONSENSUS=="clique" and NETWORKID==500:
passphrase="pass" # hardcoded in geth-dev/docker-compose.yml
# print ("passphrase:", passphrase)
if not account:
account = w3.eth.defaultAccount
# print (account)
if PARITY_UNLOCK_EACH_TRANSACTION:
answer = w3.personal.unlockAccount(account=account,
passphrase=passphrase)
else:
if NODETYPE=="Parity":
# duration = w3.toHex(duration)
# # suggestions by tomusdrw in https://github.com/paritytech/parity-ethereum/issues/10382#issuecomment-466373932
# # is duration=0 backwards compatible for old v1 versions of parity too?
duration = w3.toHex(0)
answer = w3.personal.unlockAccount(account=account,
passphrase=passphrase,
duration=duration)
print ("unlocked:", answer)
return answer
if __name__ == '__main__':
# example how to call this:
# answer = web3connection()
answer = web3connection(RPCaddress=RPCaddress, account=None)
w3, chainInfos = answer
global NODENAME, NODETYPE, NODEVERSION, CONSENSUS, NETWORKID, CHAINNAME, CHAINID
NODENAME, NODETYPE, NODEVERSION, CONSENSUS, NETWORKID, CHAINNAME, CHAINID = chainInfos