def fetch_signed(self, path, *, signing_key=None, signature=None, timestamp=None, address=None, **kwargs): if not isinstance(path, str) or not path.startswith("/"): # for simplicity's sake, don't accept HTTPRequest objects or external urls in the tests raise Exception("first argument must be path string starting with a / (e.g. /v1/tx)") # find out if there's a path prefix added by get_url prefix = "/{}".format(self.get_url(path).split('/', 3)[-1]).split(path)[0] headers = kwargs.setdefault('headers', tornado.httputil.HTTPHeaders()) if 'body' in kwargs: body = kwargs.pop('body') if isinstance(body, dict): headers['Content-Type'] = "application/json" body = tornado.escape.json_encode(body).encode('utf-8') elif isinstance(body, str): # try and find the charset to use to encode this if 'Content-Type' in headers: idx = headers['Content-Type'].find('charset=') if idx >= 0: charset = headers['Content-Type'][idx + 8:] idx = charset.find(';') if idx >= 0: charset = charset[:idx] else: charset = 'utf-8' else: charset = 'utf-8' # encode to a byte string body = body.encode(charset) elif not isinstance(body, bytes): raise Exception("Unable to handle bodys of type '{}'".format(type(body))) else: body = None method = kwargs.setdefault('method', 'GET').upper() if signing_key is None and (address is None or signature is None): raise Exception("signing_key is required unless address and signature is given") if timestamp is None and signature is not None: raise Exception("timestamp is required if signature is given explicitly") if address is None: address = private_key_to_address(signing_key) if timestamp is None: timestamp = int(time.time()) if signature is None: signature = sign_request(signing_key, method, "{}{}".format(prefix, path), timestamp, body) headers[TOSHI_ID_ADDRESS_HEADER] = address headers[TOSHI_SIGNATURE_HEADER] = signature headers[TOSHI_TIMESTAMP_HEADER] = str(timestamp) # because tornado doesn't like POSTs with body set to None if body is None and method == "POST": body = b"" return self.fetch(path, body=body, **kwargs)
def __init__(self, name, contract, *, from_key=None, constant=None, return_raw_tx=False): self.name = name self.contract = contract # TODO: forcing const seems to do nothing, since eth_call # will just return a tx_hash (on parity at least) if constant is None: self.is_constant = self.contract.translator.function_data[name][ 'is_constant'] else: # force constantness of this function self.is_constant = constant if from_key: if isinstance(from_key, str): self.from_key = data_decoder(from_key) else: self.from_key = from_key self.from_address = private_key_to_address(from_key) else: self.from_key = None self.from_address = None self.return_raw_tx = return_raw_tx
async def faucet(self, to, value, *, from_private_key=FAUCET_PRIVATE_KEY, startgas=None, gasprice=DEFAULT_GASPRICE, nonce=None, data=b"", wait_on_confirmation=True): if isinstance(from_private_key, str): from_private_key = data_decoder(from_private_key) from_address = private_key_to_address(from_private_key) ethclient = JsonRPCClient(config['ethereum']['url']) to = data_decoder(to) if len(to) not in (20, 0): raise Exception( 'Addresses must be 20 or 0 bytes long (len was {})'.format( len(to))) if nonce is None: nonce = await ethclient.eth_getTransactionCount(from_address) balance = await ethclient.eth_getBalance(from_address) if startgas is None: startgas = await ethclient.eth_estimateGas(from_address, to, data=data, nonce=nonce, value=value, gasprice=gasprice) tx = Transaction(nonce, gasprice, startgas, to, value, data, 0, 0, 0) if balance < (tx.value + (tx.startgas * tx.gasprice)): raise Exception("Faucet doesn't have enough funds") tx.sign(from_private_key) tx_encoded = data_encoder(rlp.encode(tx, Transaction)) tx_hash = await ethclient.eth_sendRawTransaction(tx_encoded) while wait_on_confirmation: resp = await ethclient.eth_getTransactionByHash(tx_hash) if resp is None or resp['blockNumber'] is None: await asyncio.sleep(0.1) else: break if to == b'': print("contract address: {}".format(data_encoder(tx.creates))) return tx_hash
async def deploy_contract(self, bytecode, *, from_private_key=FAUCET_PRIVATE_KEY, startgas=None, gasprice=DEFAULT_GASPRICE, wait_on_confirmation=True): if isinstance(from_private_key, str): from_private_key = data_decoder(from_private_key) from_address = private_key_to_address(from_private_key) ethclient = JsonRPCClient(config['ethereum']['url']) nonce = await ethclient.eth_getTransactionCount(from_address) balance = await ethclient.eth_getBalance(from_address) gasestimate = await ethclient.eth_estimateGas(from_address, '', data=bytecode, nonce=nonce, value=0, gasprice=gasprice) if startgas is None: startgas = gasestimate elif gasestimate > startgas: raise Exception( "Estimated gas usage is larger than the provided gas") tx = Transaction(nonce, gasprice, startgas, '', 0, bytecode, 0, 0, 0) if balance < (tx.value + (tx.startgas * tx.gasprice)): raise Exception("Faucet doesn't have enough funds") tx.sign(from_private_key) tx_encoded = data_encoder(rlp.encode(tx, Transaction)) tx_hash = await ethclient.eth_sendRawTransaction(tx_encoded) contract_address = data_encoder(tx.creates) while wait_on_confirmation: resp = await ethclient.eth_getTransactionByHash(tx_hash) if resp is None or resp['blockNumber'] is None: await asyncio.sleep(0.1) else: code = await ethclient.eth_getCode(contract_address) if code == '0x': raise Exception("Failed to deploy contract") break return tx_hash, contract_address
async def connect(self): # find out if there's a path prefix added by get_url path = "/{}".format(self.url.split('/', 3)[-1]) headers = {'User-Agent': 'Dgas-Test-Websocket-Client'} if self.signing_key: address = private_key_to_address(self.signing_key) timestamp = int(time.time()) signature = sign_request(self.signing_key, "GET", path, timestamp, None) headers.update({ TOSHI_ID_ADDRESS_HEADER: address, TOSHI_SIGNATURE_HEADER: signature, TOSHI_TIMESTAMP_HEADER: str(timestamp) }) request = tornado.httpclient.HTTPRequest(self.url, headers=headers) self.con = await tornado.websocket.websocket_connect(request) return self.con
async def from_source_code(cls, sourcecode, contract_name, constructor_data=None, *, address=None, deployer_private_key=None, import_mappings=None, libraries=None, optimize=False, deploy=True, cwd=None, wait_for_confirmation=True): if deploy: ethurl = get_url() if address is None and deployer_private_key is None: raise TypeError( "requires either address or deployer_private_key") if address is None and not isinstance(constructor_data, (list, type(None))): raise TypeError( "must supply constructor_data as a list (hint: use [] if args should be empty)" ) args = ['solc', '--combined-json', 'bin,abi'] if libraries: args.extend([ '--libraries', ','.join(['{}:{}'.format(*library) for library in libraries]) ]) if optimize: args.append('--optimize') if import_mappings: args.extend([ "{}={}".format(path, mapping) for path, mapping in import_mappings ]) # check if sourcecode is actually a filename if cwd: filename = os.path.join(cwd, sourcecode) else: filename = sourcecode if os.path.exists(filename): args.append(filename) sourcecode = None else: filename = '<stdin>' process = subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=cwd) output, stderrdata = process.communicate(input=sourcecode) try: output = json_decode(output) except json.JSONDecodeError: if output and stderrdata: output += b'\n' + stderrdata elif stderrdata: output = stderrdata raise Exception("Failed to compile source: {}\n{}\n{}".format( filename, ' '.join(args), output.decode('utf-8'))) try: contract = output['contracts']['{}:{}'.format( filename, contract_name)] except KeyError: print(output) raise abi = json_decode(contract['abi']) # deploy contract translator = ContractTranslator(abi) # fix things that don't have a constructor if not deploy: return Contract(abi=abi, address=address, translator=translator) ethclient = JsonRPCClient(ethurl) if address is not None: # verify there is code at the given address for i in range(10): code = await ethclient.eth_getCode(address) if code == "0x": await asyncio.sleep(1) continue break else: raise Exception("No code found at given address") return Contract(abi=abi, address=address, translator=translator) try: bytecode = data_decoder(contract['bin']) except binascii.Error: print(contract['bin']) raise if constructor_data is not None: constructor_call = translator.encode_constructor_arguments( constructor_data) bytecode += constructor_call if isinstance(deployer_private_key, str): deployer_private_key = data_decoder(deployer_private_key) deployer_address = private_key_to_address(deployer_private_key) nonce = await ethclient.eth_getTransactionCount(deployer_address) balance = await ethclient.eth_getBalance(deployer_address) gasprice = 20000000000 value = 0 startgas = await ethclient.eth_estimateGas(deployer_address, '', data=bytecode, nonce=nonce, value=0, gasprice=gasprice) if balance < (startgas * gasprice): raise Exception("Given account doesn't have enough funds") tx = Transaction(nonce, gasprice, startgas, '', value, bytecode, 0, 0, 0) tx.sign(deployer_private_key) tx_encoded = data_encoder(rlp.encode(tx, Transaction)) contract_address = data_encoder(tx.creates) tx_hash = await ethclient.eth_sendRawTransaction(tx_encoded) # wait for the contract to be deployed while wait_for_confirmation: resp = await ethclient.eth_getTransactionByHash(tx_hash) if resp is None or resp['blockNumber'] is None: await asyncio.sleep(0.1) else: code = await ethclient.eth_getCode(contract_address) if code == '0x': raise Exception( "Failed to deploy contract: resulting address '{}' has no code" .format(contract_address)) break return Contract(abi=abi, address=contract_address, translator=translator, creation_tx_hash=tx_hash)
def __init__(self, signing_key): self.address = private_key_to_address(signing_key) self.signing_key = signing_key