def do_POST(self): # TODO: figure out why read is blocking here data = self.rfile.read(len(self.rfile.peek())) data = data.decode('utf-8') data = json.loads(data) if self.path == "/v1/tx/skel": gas_price = parse_int(data['gas_price']) if 'gas_price' in data else DEFAULT_GASPRICE gas = parse_int(data['gas']) if 'gas' in data else DEFAULT_STARTGAS nonce = parse_int(data['nonce']) if 'nonce' in data else 0 if 'value' not in data or 'from' not in data or 'to' not in data: self.write_data(400, {'errors': [{'id': 'bad_arguments', 'message': 'Bad Arguments'}]}) return value = parse_int(data['value']) to_address = data['to'] from_address = data['from'] if not validate_address(to_address): self.write_data(400, {'errors': [{'id': 'invalid_to_address', 'message': 'Invalid To Address'}]}) return if not validate_address(from_address): self.write_data(400, {'errors': [{'id': 'invalid_from_address', 'message': 'Invalid From Address'}]}) return tx = create_transaction(nonce=nonce, gasprice=gas_price, startgas=gas, to=to_address, value=value) transaction = encode_transaction(tx) self.write_data(200, { "tx_data": { "nonce": hex(nonce), "from": from_address, "to": to_address, "value": hex(value), "startGas": hex(gas), "gasPrice": hex(gas_price) }, "tx": transaction }) elif self.path == "/v1/tx": tx = decode_transaction(data['tx']) if 'signature' in data: sig = data_decoder(data['signature']) add_signature_to_transaction(tx, sig) self.write_data(200, {"tx_hash": data_encoder(tx.hash)}) else: self.write_data(404)
def generate_request_signature_data_string(method, path, timestamp, data): if isinstance(data, str_types): data = data.encode('utf-8') elif isinstance(data, dict): data = json.dumps(data).encode('utf-8') if data is not None and data != b"": datahash = base64.b64encode(sha3(data)).decode('utf-8') else: datahash = "" # make sure the timestamp is an integer timestamp = parse_int(timestamp) method = method.upper() return TOSHI_SIGNATURE_DATA_STRING.format(VERB=method, PATH=path, TIMESTAMP=timestamp, HASH=datahash).encode('utf-8')
async def check_account_nonces(conf, dryrun=False): async with conf.db.id.acquire() as con: users = await con.fetch("SELECT dgas_id, payment_address FROM users") last_percent = -1 async with conf.db.eth.acquire() as con: tr = con.transaction() await tr.start() for i, user in enumerate(users): percent = int((i / len(users)) * 100) if percent != last_percent: print("Progress: {}/{} ({}%)".format(i, len(users), percent)) last_percent = percent from_address = user['payment_address'] resp = await app.http.post( conf.urls.node, headers={'Content-Type': 'application/json'}, data=json.dumps({ "jsonrpc": "2.0", "id": random.randint(0, 1000000), "method": "eth_getTransactionCount", "params": [from_address] }).encode('utf-8')) if resp.status == 200: data = await resp.json() if 'result' in data and data['result'] is not None: nonce = parse_int(data['result']) res = await con.execute( "UPDATE transactions SET last_status = 'error' WHERE from_address = $1 AND nonce >= $2 AND last_status != 'error'", from_address, nonce) if res != "UPDATE 0": print("{}|{}: {}".format(user['dgas_id'], from_address, res)) if dryrun: await tr.rollback() else: await tr.commit()
def test_vector(name, vector): if 'transaction' not in vector: return # TODO: process rlp tests transaction = vector['transaction'] tx = create_transaction( nonce=parse_int(transaction['nonce']), gasprice=parse_int(transaction['gasPrice']), startgas=parse_int(transaction['gasLimit']), to=transaction['to'], value=parse_int(transaction['value']), data=data_decoder(transaction['data']), v=parse_int(transaction['v']), r=parse_int(transaction['r']), s=parse_int(transaction['s'])) self.assertEqual(data_encoder(tx.sender), "0x{}".format(vector['sender']), name) self.assertEqual(calculate_transaction_hash(tx), "0x{}".format(vector['hash']), name) self.assertEqual(encode_transaction(tx), vector['rlp'], name) # test decode transaction -> encode transaction round trip tx = decode_transaction(vector['rlp']) self.assertEqual(encode_transaction(tx), vector['rlp'], name)
def verify_request(self): """Verifies that the signature and the payload match the expected address raising a JSONHTTPError (400) if something is wrong with the request""" if TOSHI_ID_ADDRESS_HEADER in self.request.headers: expected_address = self.request.headers[TOSHI_ID_ADDRESS_HEADER] elif self.get_argument(TOSHI_ID_ADDRESS_QUERY_ARG, None): expected_address = self.get_argument(TOSHI_ID_ADDRESS_QUERY_ARG) elif TOKEN_ID_ADDRESS_HEADER in self.request.headers: expected_address = self.request.headers[TOKEN_ID_ADDRESS_HEADER] elif self.get_argument(TOKEN_ID_ADDRESS_QUERY_ARG, None): expected_address = self.get_argument(TOKEN_ID_ADDRESS_QUERY_ARG) else: raise JSONHTTPError(400, body={'errors': [{'id': 'bad_arguments', 'message': 'Missing Dgas-ID-Address'}]}) if TOSHI_SIGNATURE_HEADER in self.request.headers: signature = self.request.headers[TOSHI_SIGNATURE_HEADER] elif self.get_argument(TOSHI_SIGNATURE_QUERY_ARG, None): signature = self.get_argument(TOSHI_SIGNATURE_QUERY_ARG) elif TOKEN_SIGNATURE_HEADER in self.request.headers: signature = self.request.headers[TOKEN_SIGNATURE_HEADER] elif self.get_argument(TOKEN_SIGNATURE_QUERY_ARG, None): signature = self.get_argument(TOKEN_SIGNATURE_QUERY_ARG) else: raise JSONHTTPError(400, body={'errors': [{'id': 'bad_arguments', 'message': 'Missing Dgas-Signature'}]}) if TOSHI_TIMESTAMP_HEADER in self.request.headers: timestamp = self.request.headers[TOSHI_TIMESTAMP_HEADER] elif self.get_argument(TOSHI_TIMESTAMP_QUERY_ARG, None): timestamp = self.get_argument(TOSHI_TIMESTAMP_QUERY_ARG) elif TOKEN_TIMESTAMP_HEADER in self.request.headers: timestamp = self.request.headers[TOKEN_TIMESTAMP_HEADER] elif self.get_argument(TOKEN_TIMESTAMP_QUERY_ARG, None): timestamp = self.get_argument(TOKEN_TIMESTAMP_QUERY_ARG) else: raise JSONHTTPError(400, body={'errors': [{'id': 'bad_arguments', 'message': 'Missing Dgas-Timestamp'}]}) timestamp = parse_int(timestamp) if timestamp is None: raise JSONHTTPError(400, body={'errors': [{'id': 'invalid_timestamp', 'message': 'Given Dgas-Timestamp is invalid'}]}) if not validate_address(expected_address): raise JSONHTTPError(400, body={'errors': [{'id': 'invalid_id_address', 'message': 'Invalid Dgas-ID-Address'}]}) if not validate_signature(signature): raise JSONHTTPError(400, body={'errors': [{'id': 'invalid_signature', 'message': 'Invalid Dgas-Signature'}]}) try: signature = data_decoder(signature) except Exception: raise JSONHTTPError(400, body={'errors': [{'id': 'invalid_signature', 'message': 'Invalid Dgas-Signature'}]}) verb = self.request.method uri = self.request.path if self.request.body: datahash = self.request.body else: datahash = "" data_string = generate_request_signature_data_string(verb, uri, timestamp, datahash) if not ecrecover(data_string, signature, expected_address): raise JSONHTTPError(400, body={'errors': [{'id': 'invalid_signature', 'message': 'Invalid Dgas-Signature'}]}) if abs(int(time.time()) - timestamp) > TIMESTAMP_EXPIRY: raise JSONHTTPError(400, body={'errors': [{'id': 'invalid_timestamp', 'message': 'The difference between the timestamp and the current time is too large'}]}) return expected_address
def test_parse_int_string(self): self.assertEqual(parse_int("12345"), 12345)
def test_parse_unicode(self): self.assertEqual(parse_int(u'12345'), 12345)
def test_parse_bytes(self): self.assertEqual(parse_int(b"12345"), 12345)
def test_parse_misc(self): self.assertEqual(parse_int({}), None)
def test_parse_decimal(self): self.assertEqual(parse_int(Decimal("12345.6787")), 12345)
def test_parse_float(self): self.assertEqual(parse_int(12345.45675), 12345)
def test_parse_hex_string(self): self.assertEqual(parse_int("0x12345"), 74565)
def test_parse_float_string(self): self.assertEqual(parse_int("12345.567678"), 12345)
def test_parse_int_no_leading_zeros_string(self): self.assertEqual(parse_int("0123"), None)
def test_parse_zero_int_string(self): self.assertEqual(parse_int("0"), 0)
def test_parse_negative_int_string(self): self.assertEqual(parse_int("-12345"), -12345)
def test_parse_none(self): self.assertEqual(parse_int(None), None)
def get_balance(self, address, **kwargs): resp = self._fetch("/v1/balance/{}".format(address), "GET", **kwargs) return parse_int(resp["confirmed_balance"]), parse_int( resp["unconfirmed_balance"])