Ejemplo n.º 1
0
    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(self._app.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
Ejemplo n.º 2
0
    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(self._app.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
    def test_encode_decode_transaction(self):

        sender_private_key = "0x0164f7c7399f4bb1eafeaae699ebbb12050bc6a50b2836b9ca766068a9d000c0"
        sender_address = "0xde3d2d9dd52ea80f7799ef4791063a5458d13913"
        to_address = "0x056db290f8ba3250ca64a45d16284d04bc6f5fbf"
        value = 10000000000
        nonce = 1048576
        data = b''
        gasprice = DEFAULT_GASPRICE
        startgas = DEFAULT_STARTGAS
        expected_tx_hash = "0x2f321aa116146a9bc62b61c76508295f708f42d56340c9e613ebfc27e33f240c"

        tx1 = Transaction(nonce, gasprice, startgas, to_address, value, data)
        tx1.sign(data_decoder(sender_private_key))

        self.assertEqual(data_encoder(tx1.hash), expected_tx_hash)

        # rlputx1 = rlp.encode(tx1, UnsignedTransaction)
        # rlpstx1 = rlp.encode(tx1, Transaction)

        tx1 = Transaction(nonce, gasprice, startgas, to_address, value, data)
        enc1 = rlp.encode(tx1, UnsignedTransaction)

        tx2 = rlp.decode(enc1, UnsignedTransaction)
        tx2.sign(data_decoder(sender_private_key))
        tx3 = Transaction(tx2.nonce, tx2.gasprice, tx2.startgas, tx2.to,
                          tx2.value, tx2.data)
        tx3.sign(data_decoder(sender_private_key))

        self.assertEqual(data_encoder(tx3.sender), sender_address)
        self.assertEqual(data_encoder(tx3.hash), expected_tx_hash)
        self.assertEqual(data_encoder(tx2.sender), sender_address)
        # NOTE: this is false because tx2 still thinks it's an unsigned tx
        # so it doesn't include the signature variables in the tx
        # if this suddenly starts failing, it means the behaviour
        # has been modified in the library
        self.assertNotEqual(data_encoder(tx2.hash), expected_tx_hash)
from tornado.testing import gen_test

from tokenid.app import urls
from tokenservices.test.database import requires_database
from tokenservices.test.base import AsyncHandlerTest
from tokenservices.ethereum.utils import data_decoder

TEST_PRIVATE_KEY = data_decoder(
    "0xe8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35")
TEST_ADDRESS = "0x056db290f8ba3250ca64a45d16284d04bc6f5fbf"
TEST_PAYMENT_ADDRESS = "0x444433335555ffffaaaa222211119999ffff7777"

TEST_ADDRESS_2 = "0x056db290f8ba3250ca64a45d16284d04bc000000"


class UserHandlerTest(AsyncHandlerTest):
    def get_urls(self):
        return urls

    def get_url(self, path):
        path = "/v1{}".format(path)
        return super().get_url(path)

    @gen_test
    @requires_database
    async def test_update_reputation(self):

        self._app.config['reputation'] = {'id': TEST_ADDRESS}

        async with self.pool.acquire() as con:
            await con.execute("INSERT INTO users (token_id) VALUES ($1)",
import asyncio
import time
from tornado.escape import json_decode, json_encode
from tornado.testing import gen_test
from tornado.platform.asyncio import to_asyncio_future

from tokeneth.app import urls
from tokeneth.test.base import EthServiceBaseTest
from tokenservices.test.database import requires_database
from tokenservices.test.redis import requires_redis
from tokenservices.test.ethereum.parity import requires_parity, FAUCET_PRIVATE_KEY, FAUCET_ADDRESS
from tokenservices.request import sign_request
from tokenservices.ethereum.utils import data_decoder, data_encoder
from tokenservices.ethereum.tx import sign_transaction, decode_transaction, signature_from_transaction

TEST_PRIVATE_KEY = data_decoder(
    "0xe8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35")
TEST_ADDRESS = "0x056db290f8ba3250ca64a45d16284d04bc6f5fbf"

TEST_PRIVATE_KEY_2 = data_decoder(
    "0x0ffdb88a7a0a40831ca0b19bd31f3f6085764ef8b7db1bd6b57072e5eaea24ff")
TEST_ADDRESS_2 = "0x35351b44e03ec8515664a955146bf9c6e503a381"


class TransactionTest(EthServiceBaseTest):
    @gen_test(timeout=30)
    @requires_database
    @requires_redis
    @requires_parity
    async def test_create_and_send_transaction(self):

        body = {"from": FAUCET_ADDRESS, "to": TEST_ADDRESS, "value": 10**10}
Ejemplo n.º 6
0
    async def create_transaction_skeleton(self,
                                          *,
                                          to_address,
                                          from_address,
                                          value=0,
                                          nonce=None,
                                          gas=None,
                                          gas_price=None,
                                          data=None):

        if not validate_address(from_address):
            raise JsonRPCInvalidParamsError(data={
                'id': 'invalid_from_address',
                'message': 'Invalid From Address'
            })

        if not validate_address(to_address):
            raise JsonRPCInvalidParamsError(data={
                'id': 'invalid_to_address',
                'message': 'Invalid To Address'
            })

        if value:
            value = parse_int(value)
            if value is None:
                raise JsonRPCInvalidParamsError(data={
                    'id': 'invalid_value',
                    'message': 'Invalid Value'
                })

        # check optional arguments

        if nonce is None:
            # check cache for nonce
            nonce = self.redis.get("nonce:{}".format(from_address))
            if nonce:
                nonce = int(nonce)
            # get the network's value too
            nw_nonce = await self.eth.eth_getTransactionCount(from_address)
            if nonce is None or nw_nonce > nonce:
                # if not cached, or the cached value is lower than
                # the network value, use the network value!
                nonce = nw_nonce
        else:
            nonce = parse_int(nonce)
            if nonce is None:
                raise JsonRPCInvalidParamsError(data={
                    'id': 'invalid_nonce',
                    'message': 'Invalid Nonce'
                })

        if data is not None:
            if isinstance(data, int):
                data = hex(data)
            if isinstance(data, str):
                try:
                    data = data_decoder(data)
                except binascii.Error:
                    pass
            if not isinstance(data, bytes):
                raise JsonRPCInvalidParamsError(data={
                    'id': 'invalid_data',
                    'message': 'Invalid Data field'
                })
        else:
            data = b''

        if gas is None:
            # if there is data the default startgas value wont be enough
            if data:
                gas = await self.eth.eth_estimateGas(from_address,
                                                     to_address,
                                                     nonce=nonce,
                                                     data=data)
            else:
                gas = DEFAULT_STARTGAS
        else:
            gas = parse_int(gas)
            if gas is None:
                raise JsonRPCInvalidParamsError(data={
                    'id': 'invalid_gas',
                    'message': 'Invalid Gas'
                })

        if gas_price is None:
            gas_price = DEFAULT_GASPRICE
        else:
            gas_price = parse_int(gas_price)
            if gas_price is None:
                raise JsonRPCInvalidParamsError(data={
                    'id': 'invalid_gas_price',
                    'message': 'Invalid Gas Price'
                })

        tx = create_transaction(nonce=nonce,
                                gasprice=gas_price,
                                startgas=gas,
                                to=to_address,
                                value=value,
                                data=data)

        transaction = encode_transaction(tx)

        return transaction
Ejemplo n.º 7
0
    async def send_transaction(self, *, tx, signature=None):

        try:
            tx = decode_transaction(tx)
        except:
            raise JsonRPCInvalidParamsError(data={
                'id': 'invalid_transaction',
                'message': 'Invalid Transaction'
            })

        if is_transaction_signed(tx):

            if signature:

                tx_sig = signature_from_transaction(tx)
                if tx_sig != signature:

                    raise JsonRPCInvalidParamsError(
                        data={
                            'id':
                            'invalid_signature',
                            'message':
                            'Invalid Signature: Signature in payload and signature of transaction do not match'
                        })
        else:

            if signature is None:
                raise JsonRPCInvalidParamsError(data={
                    'id': 'missing_signature',
                    'message': 'Missing Signature'
                })

            if not validate_signature(signature):
                raise JsonRPCInvalidParamsError(
                    data={
                        'id':
                        'invalid_signature',
                        'message':
                        'Invalid Signature: {}'.format('Invalid length' if len(
                            signature) != 132 else 'Invalid hex value')
                    })

            try:
                signature = data_decoder(signature)
            except Exception:
                log.exception(
                    "Unexpected error decoding valid signature: {}".format(
                        signature))
                raise JsonRPCInvalidParamsError(data={
                    'id': 'invalid_signature',
                    'message': 'Invalid Signature'
                })

            add_signature_to_transaction(tx, signature)

        from_address = data_encoder(tx.sender)
        to_address = data_encoder(tx.to)

        # prevent spamming of transactions with the same nonce from the same sender
        with RedisLock(self.redis,
                       "{}:{}".format(from_address, tx.nonce),
                       raise_when_locked=partial(JsonRPCInvalidParamsError,
                                                 data={
                                                     'id':
                                                     'invalid_nonce',
                                                     'message':
                                                     'Nonce already used'
                                                 }),
                       ex=5):

            # disallow transaction overwriting for known transactions
            async with self.db:
                existing = await self.db.fetchrow(
                    "SELECT * FROM transactions WHERE "
                    "from_address = $1 AND nonce = $2 AND last_status != $3",
                    from_address, tx.nonce, 'error')
            if existing:
                # debugging checks
                existing_tx = await self.eth.eth_getTransactionByHash(
                    existing['transaction_hash'])
                raise JsonRPCInvalidParamsError(data={
                    'id': 'invalid_nonce',
                    'message': 'Nonce already used'
                })

            # make sure the account has enough funds for the transaction
            network_balance, balance = await self.get_balances(
                from_address, ignore_pending_recieved=True)

            log.info(
                "Attempting to send transaction\n{} -> {}\nValue: {} + {} (gas) * {} (startgas) = {}\nSender's Balance {} ({} unconfirmed)"
                .format(from_address, to_address, tx.value, tx.startgas,
                        tx.gasprice, tx.value + (tx.startgas * tx.gasprice),
                        network_balance, balance))

            if balance < (tx.value + (tx.startgas * tx.gasprice)):
                raise JsonRPCInsufficientFundsError(
                    data={
                        'id': 'insufficient_funds',
                        'message': 'Insufficient Funds'
                    })

            # validate the nonce
            c_nonce = self.redis.get("nonce:{}".format(from_address))
            if c_nonce:
                c_nonce = int(c_nonce)
            # get the network's value too
            nw_nonce = await self.eth.eth_getTransactionCount(from_address)
            if c_nonce is None or nw_nonce > c_nonce:
                c_nonce = nw_nonce

            if tx.nonce < c_nonce:
                raise JsonRPCInvalidParamsError(
                    data={
                        'id': 'invalid_nonce',
                        'message': 'Provided nonce is too low'
                    })
            if tx.nonce > c_nonce:
                raise JsonRPCInvalidParamsError(
                    data={
                        'id': 'invalid_nonce',
                        'message': 'Provided nonce is too high'
                    })

            # send the transaction to the network
            try:
                tx_encoded = encode_transaction(tx)
                tx_hash = await self.eth.eth_sendRawTransaction(tx_encoded)
            except JsonRPCError as e:
                log.error(e.format())
                raise JsonRPCInternalError(
                    data={
                        'id':
                        'unexpected_error',
                        'message':
                        'An error occured communicating with the ethereum network, try again later'
                    })

            # cache nonce
            self.redis.set("nonce:{}".format(from_address), tx.nonce + 1)
            # add tx to database
            async with self.db:
                await self.db.execute(
                    "INSERT INTO transactions "
                    "(transaction_hash, from_address, to_address, nonce, value, estimated_gas_cost, sender_token_id) "
                    "VALUES ($1, $2, $3, $4, $5, $6, $7)", tx_hash,
                    from_address, to_address, tx.nonce, str(tx.value),
                    str(tx.startgas * tx.gasprice), self.user_token_id)
                await self.db.commit()

            # if there is a block monitor, force send PNs for this without
            # waiting for the node to see it
            if hasattr(self.application, 'monitor'):
                txjson = transaction_to_json(tx)
                assert txjson['hash'] == tx_hash
                IOLoop.current().add_callback(
                    self.application.monitor.send_transaction_notifications,
                    txjson)

        return tx_hash
Ejemplo n.º 8
0
 def test_valid_recovery(self):
     self.assertTrue(ecrecover(
         '{"custom":{"about":"about ","location":"location "},"timestamp":1483968938,"username":"******"}',
         data_decoder('0xbd5c9009cc87c6d4ebb3ef8223fc036726bc311678890890619c787aa914d3b636aee82d885c6fb668233b5cc70ab09eea7051648f989e758ee09234f5340d9100'),
         '0x5249dc212cd9c16f107c50b6c893952d617c011e'
     ))
Ejemplo n.º 9
0
 def test_too_short_signature_comparison(self):
     self.assertFalse(ecrecover(
         '{"custom":{"about":"æ","location":""},"timestamp":1483964545,"username":"******"}',
         data_decoder('0x5301'),
         '0x5249dc212cd9c16f107c50b6c893952d617c011e'
     ))
Ejemplo n.º 10
0
 def test_too_short_signature(self):
     self.assertEqual(ecrecover(
         '{"custom":{"about":"æ","location":""},"timestamp":1483964545,"username":"******"}',
         data_decoder('0x5301')
     ), None)
Ejemplo n.º 11
0
 def test_valid_recovery_unicode(self):
     self.assertTrue(ecrecover(
         '{"custom":{"about":"æ","location":""},"timestamp":1483964545,"username":"******"}',
         data_decoder('0xb3c61812e1e73f1a75cc9a2f5e748099378b7af2dd8bc3c1b4f0c067e6e9a4012d0c411b77bab63708b350742d41de574add6b06a3d06a5ae10fc9c63c18405301'),
         '0x5249dc212cd9c16f107c50b6c893952d617c011e'
     ))
Ejemplo n.º 12
0
    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)