Example #1
0
 def get_asset_balance(cls, contract_name):
     contract = cls.erc20_contracts.get(contract_name, None)
     if contract == None:
         raise Exception('Not supported contract name for swap:%s' %
                         contract_name)
     concise = ConciseContract(contract)
     return cls.Wei2Satoshi(concise.balanceOf(config.ETHEREUM_SCAN_ADDRESS),
                            concise.decimals())[0]
Example #2
0
class ContractInterface:
    '''
    This python interface mimics the solidity smart contract interface deployed on the blockchain
    and its methods.

    This way, it's much simpler and easier on the eyes the interaction with the smart 
    contract functions from the client side
    '''

    CONTRACT_PATH = path.dirname(
        path.abspath(__file__)) + "/../contracts/token.sol"
    COMPILED_CONTRACT_PATH = path.dirname(
        path.abspath(__file__)) + "/../contracts/contract_compiled.pickle"

    # Client number is only for tests
    def __init__(self,
                 account=None,
                 ip_address=None,
                 mac_address=None,
                 w3=None,
                 client_number: int = None):
        ''' Default constructor of the interface class, with parameters
    that make the code much cleaner in our client API .

    For now the only parameter is the client number which identifies which of 
    the default accounts provided by ganache-cli the client sets as its default one
    {1 - 9} since 0 is for the server and owner of the contract '''

        # In case there's no w3 given we initialize to the default server
        if w3 is None:
            self.w3 = Web3(HTTPProvider("http://localhost:8545"))
            #self.w3 = Web3(HTTPProvider("http://192.168.0.29:8545"))
        else:
            self.w3 = w3

        # Regular register (not from proxy)
        if ip_address is None and mac_address is None:
            self.IP = utils.getIP()
            self.MAC = utils.getMAC()
        else:
            self.IP = ip_address
            self.MAC = mac_address

        self.contract = self._getContract(self.w3)
        #self.contract = self._load_contract(self.w3)

        # The ConciseContract class is much better for reaidng variables straight away.
        # If our goal is not transact something, we'll use the Concise version of the contract
        self.contractConcise = ConciseContract(self.contract)

        # set the default account for the client, if it's None we'll register and we'll get a new one
        # Only for tests
        if client_number is not None:
            self.w3.eth.defaultAccount = self.w3.eth.accounts[client_number]
            self.account = self.w3.eth.defaultAccount
        else:
            # Usamos el w3 del proxy en ese caso
            # The client's own address is in the account variable
            self.account = account
            # The default account is however the one of the server

        # DATA FOR LEASES:

        # The dictionary is of the shape { grant_id : amount } so for the total memory we
        # just add the values
        self.remoteStorage = {}

        # The dictionary is of the shape { grant_id : amount } where amount is a number from
        # 0 to 100. 100 would mean that the amount is equal to all the available storage on
        # the remote server
        self.remoteCPU = {}

        # Load the reservations
        self._load_reservations()

    def register(self):
        ''' Registers the client in the server's contract, and sends the useful information
        so that the server can recognize each client '''

        tx_hash = None
        print('Registering with', self.account)

        # Regular register
        tx_hash = self.contract.functions.register(self.account, self.IP,
                                                   self.MAC).transact()

        self.w3.eth.waitForTransactionReceipt(tx_hash)

        # build a listener so we can get the server's response
        server_answer_filter = self.contract.events.RegisterResponse.createFilter(
            fromBlock=0,
            toBlock='latest',
            argument_filters={'account': self.account})

        print('Created filter to wait for server register response')
        while True:
            response = server_answer_filter.get_new_entries()
            if len(response) != 0:
                for grant in response:
                    args = grant['args']
                    if args['accepted']:
                        print('Accepted request')
                        # If we don't have an account we stablish it
                        if self.account is None:
                            self.account = args['account']
                            self.w3.eth.defaultAccount = self.account
                            self._pickle_address()
                            print('Address pickled')
                    else:
                        print('Grant rejected')
                break
            else:
                print('No answer yet')
                time.sleep(1)

        print('Balance after register:',
              self.contractConcise.balanceOf(self.account),
              self.contractConcise.symbol())

        del server_answer_filter

    def transfer(self, to: str, amount: int):
        ''' Transfers to the specified ethereum account (expressed
        as a hexadecimal string) the specified amount of IoT (tokens) '''

        print('Balances before: {}, {}'.format(
            self.contractConcise.balanceOf(self.account),
            self.contractConcise.balanceOf(to)))
        tx_hash = self.contract.functions.transfer(self.account, to,
                                                   amount).transact()
        self.w3.eth.waitForTransactionReceipt(tx_hash)
        print('Balances after: {}, {}'.format(
            self.contractConcise.balanceOf(self.account),
            self.contractConcise.balanceOf(to)))

    def request_storage(self, amount: int):
        ''' Requests storage from the server '''

        print('Asking for {} for account {}'.format(amount, self.account))

        tx_hash = self.contract.functions.getStorage(self.account,
                                                     amount).transact()
        self.w3.eth.waitForTransactionReceipt(tx_hash)

        # Wait for the storage grant and filter just the ones for us
        petitionFilter = self.contract.events.StorageResponse.createFilter(
            fromBlock=0,
            toBlock='latest',
            argument_filters={'account': self.account})
        print('Created filter for storage grants')

        while True:
            response = petitionFilter.get_new_entries()
            if len(response) != 0:
                for grant in response:
                    args = grant['args']
                    if args['accepted']:
                        print('Accepted request')
                        self.remoteStorage[args['grantID']] = int(
                            args['amount'])
                        print('new dict:', self.remoteStorage)
                    else:
                        print('Grant rejected')
                break
            else:
                print('No answer yet')
                time.sleep(1)

        # We no longer need the filter
        del petitionFilter
        self._update_reservations()

    def free_storage(self, id):
        ''' Free the storage acquired from the server '''

        print('Removing item with id=', id)
        tx_hash = self.contract.functions.freeStorage(self.account,
                                                      id).transact()
        self.w3.eth.waitForTransactionReceipt(tx_hash)
        print('freed storage')
        # Pop the item
        self.remoteStorage.pop(id)
        self._update_reservations()

    def request_computing_power(self, amount: int):
        """ Request computing power from the server """
        print('Asking for {}% of cpu for account {}'.format(
            amount, self.account))

        # Transact and wait for the transaction receipt
        tx_hash = self.contract.functions.getComputingPower(
            self.account, amount).transact()
        self.w3.eth.waitForTransactionReceipt(tx_hash)

        # Filter the answers to get if our request was granted
        petition_filter = self.contract.events.CPUResponse.createFilter(
            fromBlock=0,
            toBlock='latest',
            argument_filters={'account': self.account})

        print('Created computing power filter for account', self.account)

        # Wait for our response
        while True:
            response = petition_filter.get_new_entries()
            if len(response) != 0:
                for answer in response:
                    args = answer['args']
                    if args['accepted']:
                        print('Our computing power request was granted')
                        self.remoteCPU[args['grantID']] = int(args['amount'])
                        print('New dict: ', self.remoteCPU)
                    else:
                        print('Grant rejected')
                break
            else:
                print('No answer to cpu request yet')
                time.sleep(1)

        del petition_filter
        self._update_reservations()

    def free_computing_power(self, id):
        ''' Free the cpu reservation '''

        print('Removing cpu reservation with id', id)
        tx_hash = self.contract.functions.freeComputingPower(self.account,
                                                             id).transact()
        self.w3.eth.waitForTransactionReceipt(tx_hash)
        print('Freed cpu storage')
        # Pop item
        self.remoteCPU.pop(id)
        self._update_reservations()

    def force_error(self):
        """ Method to test whether the onlyOwner modifier works properly """
        print('Trying to force an error')
        tx_hash = self.contract.functions._freeStorage(self.account,
                                                       500).transact()
        self.w3.eth.waitForTransactionReceipt(tx_hash)
        print('Received transaction hash')

    def _pickle_address(self):
        """ Saves the device's address in a pickle file """
        with open(ADDRESS_PICKLE_FILE, 'wb') as pickle_file:
            # dump the address
            pickle.dump(self.account, pickle_file)

    def _unpickle_address(self):
        """ Gets this device's address from the file """
        if os.path.exists(ADDRESS_PICKLE_FILE):
            with open(ADDRESS_PICKLE_FILE, 'rb') as pickle_file:
                self.account = pickle.load(pickle_file)
                self.w3.eth.defaultAccount = self.account

                #self.w3.eth.defaultAccount = self.w3.eth.accounts[client_number]
                #self.account = self.w3.eth.defaultAccount

    def _update_reservations(self):
        """ Updates the reservations in the pickle file after a reservation is made or freed """
        with open(RESERVATIONS_PICKLE_FILE, 'wb') as pickle_file:
            # save the dictionaries
            pickle.dump(self.remoteStorage, pickle_file)
            pickle.dump(self.remoteCPU, pickle_file)

    def _load_reservations(self):
        """ Use pickle to load the remote storage and computing power reservations. 
        This way we make them persistent in case of a reboot of the service. The variables
        inside the pickle file keep the same name """
        if os.path.exists(RESERVATIONS_PICKLE_FILE):

            with open(RESERVATIONS_PICKLE_FILE, 'rb') as pickle_file:
                # restore the dictionaries
                self.remoteStorage = pickle.load(pickle_file)
                self.remoteCPU = pickle.load(pickle_file)

            print('Restored storage reservations:\n', self.remoteStorage)
            print('Restored cpu reservations:\n', self.remoteCPU)

    def _load_contract(self, w3: Web3):
        with open(self.COMPILED_CONTRACT_PATH, 'rb') as pickle_file:
            interface = pickle.load(pickle_file)
            contract = w3.eth.contract(abi=interface['abi'],
                                       address=CONTRACT_ADDRESS)
            return contract

    def _getContract(self, w3: Web3):
        with open(self.CONTRACT_PATH, "r") as contract_file:
            global CONTRACT_ADDRESS
            source_code = contract_file.read()
            compiled = compile_source(source_code)
            interface = compiled['<stdin>:IoToken']
            contract = w3.eth.contract(abi=interface['abi'],
                                       address=CONTRACT_ADDRESS)
            return contract
Example #3
0
    generated_price = web3.eth.generateGasPrice()

    c_contract = ConciseContract(contract)
    t_contract = ImplicitContract(contract)

    print('Network gas price:', web3.fromWei(web3.eth.gasPrice, 'Gwei'))
    print('Computed gas price:', web3.fromWei(generated_price, 'Gwei'))
    # print(web3.eth.accounts)
    # print(web3.txpool.content)
    params = {'from': OWNER, 'gasPrice': generated_price}

    # mint(t_contract, web3.eth.accounts[0], web3.toWei(1, 'Ether'), tx_p=params)
    # mint(t_contract, '0x8862cE71FDCDC386D5a9b6BB5640a8FefD6DDAd0', web3.toWei(100000, 'Ether'), tx_p=params)
    # mint(t_contract, '0xfF989e7D397e6fF1026429A87d7A5eF7c6B09c27', web3.toWei(100000, 'Ether'), tx_p=params)

    print('OWNER', c_contract.balanceOf(OWNER))
    print('RECIPIENT', c_contract.balanceOf(RECIPIENT))
    print('FEE_RECIPIENT', c_contract.balanceOf(FEE_RECIPIENT))

    print('starting transaction...')
    receipt = start_escrow(t_contract,
                           11223344,
                           RECIPIENT,
                           web3.toWei(50, 'Gwei'),
                           tx_p=params)
    print(receipt)
    print('releasing transaction...')
    receipt = release_escrow(t_contract, 11223344, FEE_RECIPIENT, tx_p=params)
    print(receipt)

    print('OWNER', c_contract.balanceOf(OWNER))
Example #4
0
def test_deploy(
        pack_deploy,
        instantiate,
        accounts,
        assert_tx_failed,
        pack_types,
        pack_prices,
        get_receipt,
        get_logs_for_event,
        w3,
        #Parameters
        bundle_sizes,
        bundle_caps):

    BLOCK_GAS_LIMIT = 8e6  # Well over double this now, but a possible concern.

    # Deploy an oracle and a Link Token
    (pack, processor, referrals, cards, vault, pack_r, processor_r, referrals_r, cards_r, vault_r) = pack_deploy()

    receipts = {
        "pack": (pack, pack_r),
        "processor": (processor, processor_r),
        "referrals": (referrals, referrals_r),
        "cards": (cards, cards_r),
        "vault": (vault, vault_r)
    }

    if DEBUG:
        print("")  # new line
    for name, r in receipts.items():
        if DEBUG:
            print("Gas used to deploy {}:\t{}".format(name, r[1]['gasUsed']))
        assert r[1]['gasUsed'] < BLOCK_GAS_LIMIT, "{} uses more gas than block_gas_limit!".format(name)
        assert r[0].address != 0, "Address returned not expected for {}".format(name)

    addPack = cards.functions.addPack(pack.address)
    assert_tx_failed(addPack, {'from': accounts[1]})
    addPack.transact({'from': accounts[0]})

    tokens = {}


    # Now each of the bundle tokens need to be deployed
    # For each, the pack has to be set to point to the bundle and stipulate it's price and size.
    for i, (rarity, rarity_value) in enumerate(pack_types.items()):

        setPack = pack.functions.setPack(
            rarity_value,
            pack_prices[rarity],
            "Genesis {} Bundle".format(rarity),
            "BND{}".format(rarity[0]),
            bundle_sizes[i],
            bundle_caps[i],
        )
        assert_tx_failed(setPack, {'from': accounts[1]})
        tx_hash = setPack.transact({'from': accounts[0]})
        rcpt = get_receipt(tx_hash)

        assert len(rcpt['logs']) == 1, "Should have one event emitted."
        assert rcpt['gasUsed'] < BLOCK_GAS_LIMIT, "Too much gas for block!"

        logs = get_logs_for_event(pack.events.PackAdded, tx_hash)

        tokens[rarity] = instantiate(logs[0]['args']['bundle'], abi=None, contract="Bundle")
        processor.functions.setCanSell(tokens[rarity].address, True).transact({'from': accounts[0]})

        if DEBUG:
            print("Created {} Bundles: Size: {}, Cap: {}, Gas: {}".format(
                rarity,
                bundle_sizes[i],
                bundle_caps[i],
                rcpt['gasUsed'])
            )

    print("Approval Statuses:")
    for address in accounts[:5]:
        print("{}: {}".format(address, processor.functions.approvedSellers(accounts[0]).call()))

    # For each of the tokens, check their variables and try to purchase a bunch of them,
    # making sure we can't purchase too many
    for i, (rarity, token) in enumerate(tokens.items()):
        c = ConciseContract(token)
        assert c.totalSupply() == 0, "{} tokens should start with supply of zero".format(rarity)
        assert c.balanceOf(accounts[0]) == 0, "No user should have balance before mint."
        assert c.balanceOf(accounts[1]) == 0, "No user should have balance before mint."
        assert c.name() == "Genesis {} Bundle".format(rarity), "Incorrect token name"
        assert c.symbol() == "BND{}".format(rarity[0]), "Incorrect token symbol"
        assert c.decimals() == 0, "Incorrect token decimals"
        assert c.cap() == bundle_caps[i], "Incorrect cap for token"
        assert c.packType() == pack_types[rarity], "Incorrect packType for token"

        BUNDLES_TO_BUY = 6

        for _ in range(bundle_caps[i] if bundle_caps[i] > 0 else BUNDLES_TO_BUY):
            # Shouldn't be able to buy if we send no money
            assert_tx_failed(
                token.functions.purchaseFor(accounts[0], 1, accounts[1]),
                {'from': accounts[0]}
            )
            assert processor.functions.approvedSellers(token.address).call(), "Bundle should be approved to sell."
            tx_hash = token.functions.purchaseFor(accounts[0], 1, accounts[1])\
                .transact({'from': accounts[0], 'value': pack_prices[rarity] * bundle_sizes[i]})
            rcpt = get_receipt(tx_hash)
            assert rcpt['gasUsed'] < BLOCK_GAS_LIMIT, "Too much gas for block!"
            if DEBUG:
                print("Account0 {} tokens: {}, vault: {} ({})".format(
                    rarity,
                    token.functions.balanceOf(accounts[0]).call(),
                    (vault.functions.total().call())/10**18,
                    w3.eth.getBalance(vault.address)/10**18))

        # Now there shouldn't be any more bundles available to buy
        if bundle_caps[i] > 0:
            assert_tx_failed(
                token.functions.purchaseFor(accounts[0], 1, accounts[1]),
                {'from': accounts[0]}
            )
def cpu_allocator(contract,
                  available_cpu: Value,
                  lock: Lock,
                  fromBlock=0,
                  toBlock='latest'):
    """ Similar to the storage allocator, this method allocates cpu computing power to a certain
    client after it's requested, returning an id for said reservation """

    print('[x] cpu_allocator: started')

    # Create the filter necessary
    cpu_allocator_filter = contract.events.CPUPetition.createFilter(
        fromBlock=fromBlock, toBlock=toBlock)
    time.sleep(1)

    # Concise contract to access the balances in a much more direct way
    concise_contract = ConciseContract(contract)

    try:
        # Main loop of the receiver
        while True:
            new_cpu_petitions = cpu_allocator_filter.get_new_entries()

            if len(new_cpu_petitions) != 0:

                # Open mysql connection
                try:
                    connection = _getConnection()
                    cursor = connection.cursor()

                    with lock:
                        for petition in new_cpu_petitions:
                            args = petition['args']
                            print('Cpu_petition: {} -> {}'.format(
                                args['account'], args['amount']))

                            # Check to see if rejecting and accepting works, we need
                            # to change it after
                            if available_cpu.value - args['amount'] >= 0:
                                cpu_id = uuid.uuid4()
                                price = BlockchainServer.calculate_price_of_request(
                                    Resource.computing_power,
                                    int(args['amount']), available_cpu,
                                    args['account'])

                                # Get balance of the account requesting storage
                                balance = concise_contract.balanceOf(
                                    args['account'])
                                print('Balance is {} and price is {}'.format(
                                    balance, price))

                                if balance >= price:
                                    # Decrease the balance
                                    print('Accepting Request')
                                    # Update balance, False = decrement
                                    contract.functions.updateBalance(
                                        args['account'], int(price),
                                        False).transact()
                                    contract.functions.answerComputingPowerRequest(
                                        args['account'], args['amount'],
                                        str(cpu_id), True).transact()
                                    available_cpu.value -= args['amount']

                                    # 1) Insert cpu allocation in the database
                                    cursor.execute("""
                                    INSERT INTO cpu_allocations(id, account_address, amount) value
                                    ('{}', '{}', {})""".format(
                                        str(cpu_id), args['account'],
                                        args['amount']))

                                    # 2) Update client balance in the database
                                    cursor.execute(
                                        "UPDATE clients set coin_balance = coin_balance - {} where account_address = '{}'"
                                        .format(price, args['account']))

                                    # 3) Insert new transaction pending of charge
                                    cursor.execute(
                                        "INSERT INTO pending_charges(id, account_address, charge) value ('{}','{}',{})"
                                        .format(cpu_id, args['account'],
                                                price))

                                    # Commit changes
                                    connection.commit()

                                    new_balance = concise_contract.balanceOf(
                                        args['account'])
                                    print('New balance', new_balance)

                                else:
                                    # Client does not have enough funds
                                    print('Not enough funds')
                                    print('Rejecting request')
                                    contract.functions.answerComputingPowerRequest(
                                        args['account'], args['amount'], '',
                                        False).transact()

                            else:
                                # Not enough spare CPU
                                print(
                                    'Rejecting request... number {} is not even'
                                    .format(args['amount']))
                                contract.functions.answerComputingPowerRequest(
                                    args['account'], args['amount'], '',
                                    False).transact()

                except mysql.connector.Error as error:
                    connection.rollback()
                    print('Error in mysql: {}'.format(error))

                finally:
                    cursor.close()
                    connection.close()

            # Sleep until the next comprobation
            time.sleep(1)

    except KeyboardInterrupt:
        print("[*] cpu_allocator: exiting...")
        sys.exit(0)
def storage_allocator(contract,
                      available_storage: Value,
                      lock: Lock,
                      fromBlock=0,
                      toBlock='latest'):
    ''' Process that decides whether a storage request is granted or not.

    Most of the requirement checking is done directly by the smart contract, so here
    the main task of this method is to generate a unique id for the reservation - with
    the uuid4 module - and store all the data of the reservation in the database 

    One of the requirements to be able to allocate memory is being reggistered in the system.
    If not, the call will fail '''

    print('[x] storage_allocator: started')

    # Create filters for all events related to storage
    petitionFilter = contract.events.StoragePetition.createFilter(
        fromBlock=fromBlock, toBlock=toBlock)
    time.sleep(1)

    # Concise contract to access the balances in a much more direct way
    concise_contract = ConciseContract(contract)

    try:
        while True:
            newStoragePetitions = petitionFilter.get_new_entries()

            if len(newStoragePetitions) != 0:
                try:
                    connection = _getConnection()
                    cursor = connection.cursor()

                    # Acquire lock to change the reservations
                    with lock:
                        for petition in newStoragePetitions:
                            args = petition['args']
                            print('Args: {}, {}'.format(
                                args['account'], args['amount']))

                            # Make the appropriate comprobations
                            if available_storage.value - args['amount'] >= 0:
                                st_id = uuid.uuid4()
                                price = BlockchainServer.calculate_price_of_request(
                                    Resource.storage, int(args['amount']),
                                    available_storage, args['account'])
                                print('Price of the request:', price,
                                      'IoTokens')

                                # Get balance of the account requesting storage
                                balance = concise_contract.balanceOf(
                                    args['account'])
                                print('Balance is {} and price is {}'.format(
                                    balance, price))

                                if balance >= price:
                                    # Decrease the balance
                                    print('Accepting Request')
                                    # Update balance, False = decrement
                                    contract.functions.updateBalance(
                                        args['account'], int(price),
                                        False).transact()
                                    contract.functions.answerStorageRequest(
                                        args['account'], args['amount'],
                                        str(st_id), True).transact()
                                    # Add storage reservation to the database
                                    cursor.execute('''
                                        INSERT INTO storage_allocations(id, account_address, amount) value 
                                        ('{}','{}',{})'''.format(
                                        str(st_id), args['account'],
                                        args['amount']))
                                    # Update client balance in the database
                                    cursor.execute(
                                        "UPDATE clients set coin_balance = coin_balance - {} where account_address = '{}'"
                                        .format(price, args['account']))

                                    # Insert new transaction pending of charge
                                    cursor.execute(
                                        "INSERT INTO pending_charges(id, account_address, charge) value ('{}','{}',{})"
                                        .format(st_id, args['account'], price))
                                    connection.commit()

                                    available_storage.value -= args['amount']
                                    print('Storage available:',
                                          available_storage.value, 'MB')
                                    new_balance = concise_contract.balanceOf(
                                        args['account'])
                                    print('New balance', new_balance)

                                else:
                                    # Not enough balance in the client's account
                                    print('Not enough funds')
                                    print('Rejecting request')
                                    contract.functions.answerStorageRequest(
                                        args['account'], args['amount'], '',
                                        False).transact()

                            else:
                                # Not enough available storage
                                print('Rejecting request')
                                contract.functions.answerStorageRequest(
                                    args['account'], args['amount'], '',
                                    False).transact()

                except mysql.connector.Error as error:
                    connection.rollback()
                    print('Failed inserting new record into table: {}'.format(
                        error))

                finally:
                    cursor.close()
                    connection.close()

            time.sleep(1)

    except KeyboardInterrupt:
        print("[*] storage_allocator: exiting...")
        sys.exit(0)