Example #1
0
 def __init__(self, fname=None, search="", opts=""):
     Transactions.__init__(self)
     if not fname: fname = c['ledger-file']
     self.search = search
     self.opts = opts
     self.fname = fname
     self.name = self.fname
def test_push_tx(alice, bob, alice_secret, blockr):
    from transactions import Transactions
    transactions = Transactions(testnet=True)
    raw_tx = transactions.create(alice, (bob, 1), min_confirmations=1)
    signed_tx = transactions.sign(raw_tx, alice_secret)
    txid = blockr.push_tx(signed_tx)
    assert txid
Example #3
0
    def __init__(self,
                 testnet=False,
                 service='blockr',
                 username='',
                 password='',
                 host='',
                 port=''):
        """
        Args:
            testnet (bool): Whether to use the mainnet or testnet.
                Defaults to the mainnet (:const:`False`).
            service (str): Bitcoin communication interface: ``'blockr'``,
                ``'daemon'``, or ``'regtest'``. ``'blockr'`` refers to the
                public api, whereas ``'daemon'`` and ``'regtest'`` refer
                to the jsonrpc inteface. Defaults to ``'blockr'``.
            username (str): username for jsonrpc communications
            password (str): password for jsonrpc communications
            hostname (str): hostname of the bitcoin node when using jsonrpc
            port (str): port number of the bitcoin node when using jsonrpc

        """
        self.testnet = testnet
        self._netcode = 'XTN' if testnet else 'BTC'
        self._t = Transactions(service=service,
                               testnet=testnet,
                               username=username,
                               password=password,
                               host=host,
                               port=port)
        # simple cache for spent outputs. Useful so that rapid firing transactions don't use the same outputs
        self._spents = Queue(maxsize=self.SPENTS_QUEUE_MAXSIZE)
def test_push_tx(alice, bob, alice_secret, blockr):
    from transactions import Transactions
    transactions = Transactions(testnet=True)
    raw_tx = transactions.create(alice, (bob, 1), min_confirmations=1)
    signed_tx = transactions.sign(raw_tx, alice_secret)
    txid = blockr.push_tx(signed_tx)
    assert txid
Example #5
0
    def test_pay_fees(self):
        alg = self._init_test()
        self._prefill_db(alg)
        trs = Transactions(FILE_NAME)
        fnds = Funds(FILE_NAME)

        accs = alg.pay_fees()
        self.assertEqual(400.0, alg.portfolio_total_fund)
        accounts = [
            (1, '01', 0.0, accs[0][3], 0, 0.7, 0.7, 1, accs[0][-1]),
            (2, '02', 0.375, accs[1][3], 50, 0.3, 0.9, 1, accs[1][-1]),
            (3, '03', 0.125, accs[2][3], -50, 0.3, 0.9, 1, accs[2][-1]),
            (4, '04', 0.5, accs[3][3], 0, 0.3, 0.9, 1, accs[3][-1]),
        ]
        self.assertEqual(accounts, accs)
        acc_trs = trs.retrieve_transactions_for_account(account_id=2)
        self.assertEqual(50, acc_trs[-1][1])
        acc_trs = trs.retrieve_transactions_for_account(account_id=3)
        self.assertEqual(-50, acc_trs[-1][1])
        acc_trs = trs.retrieve_transactions_for_account(account_id=4)
        self.assertEqual(-50, acc_trs[-2][1])
        self.assertEqual(50, acc_trs[-1][1])

        acc_fnds = fnds.retrieve_funds_for_account(account_id=2)
        self.assertEqual(150, acc_fnds[-1][1])
        acc_fnds = fnds.retrieve_funds_for_account(account_id=3)
        self.assertEqual(50, acc_fnds[-1][1])
        acc_fnds = fnds.retrieve_funds_for_account(account_id=4)
        self.assertEqual(150, acc_fnds[-2][1])
        self.assertEqual(200, acc_fnds[-1][1])
def test_transaction_creation_via_create_with_blockr(alice, bob):
    from transactions import Transactions

    trxs = Transactions(testnet=True)
    assert trxs.testnet is True
    simple_transaction = trxs.create(alice, (bob, 6), min_confirmations=1)
    assert simple_transaction
Example #7
0
def transaction_checking(grid: MultiCircuit,
                         transactions: Transactions,
                         agent_id_to_grid_id,
                         dt=1):
    """
    Function that checks the transactions with electrical computations
    :param grid: GridCal grid Object
    :param transactions: Transactions object
    :param agent_id_to_grid_id: dictionary to relate the agent's id with the grid object's id
    :param dt: market interval in hours
    :return:
    """

    # declare the final transactions list
    final_transactions = Transactions()

    for transaction in transactions:

        hash = transaction_mine(grid, transaction, agent_id_to_grid_id, dt=dt)

        # if there are no errors
        if hash is not None:
            # modify the transaction, adding a hash based on the voltage
            transaction.hash = hash

            # store the transaction
            final_transactions.append(transaction)
        else:
            # since the transactions are sorted, if a critical state is found the rest are curtailed
            return final_transactions

    # return the approved transactions
    return final_transactions
Example #8
0
    def __init__(self,
                 testnet=False,
                 service='blockr',
                 username='',
                 password='',
                 host='',
                 port=''):
        """
        Args:
            testnet (bool): Whether to use the mainnet or testnet.
                Defaults to the mainnet (:const:`False`).
            service (str): Bitcoin communication interface: ``'blockr'``,
                ``'daemon'``, or ``'regtest'``. ``'blockr'`` refers to the
                public api, whereas ``'daemon'`` and ``'regtest'`` refer
                to the jsonrpc inteface. Defaults to ``'blockr'``.
            username (str): username for jsonrpc communications
            password (str): password for jsonrpc communications
            hostname (str): hostname of the bitcoin node when using jsonrpc
            port (str): port number of the bitcoin node when using jsonrpc

        """
        self._t = Transactions(service=service,
                               testnet=testnet,
                               username=username,
                               password=password,
                               host=host,
                               port=port)
Example #9
0
    def initialize_federation_wallet():
        FederationWallet = apps.get_model(app_label='bitcoin',
                                          model_name='FederationWallet')
        print 'Resetting the FederationWallet'
        FederationWallet.objects.all().delete()

        # Reset the ids after deleting all the rows
        cursor = connection.cursor()
        cursor.execute(
            "ALTER SEQUENCE bitcoin_federationwallet_id_seq RESTART WITH 1")

        print 'Populating federation wallet with unspents'
        transactions = Transactions(service='daemon',
                                    username=settings.BTC_USERNAME,
                                    password=settings.BTC_PASSWORD,
                                    host=settings.BTC_HOST,
                                    port=settings.BTC_PORT,
                                    testnet=settings.BTC_TESTNET)
        unspents = transactions.get(settings.BTC_MAIN_WALLET,
                                    min_confirmations=1).get('unspents', [])
        for unspent in unspents:
            if unspent['amount'] in [settings.BTC_TOKEN, settings.BTC_FEE]:
                # main wallet
                unspent.update({'type': 'main'})
                FederationWallet(**unspent).save()

        unspents = transactions.get(settings.BTC_REFILL_ADDRESS,
                                    min_confirmations=1).get('unspents', [])
        for unspent in unspents:
            if unspent['amount'] == settings.BTC_CHUNK:
                # main wallet
                unspent.update({'type': 'refill'})
                FederationWallet(**unspent).save()

        print 'Finished initializing wallet'
Example #10
0
 def get_txs(self):
     txs = Transactions()
     for field in self.section_names:
         field = field.lower()
         if field in self:                
             txs.extend(self[field])
     return txs
Example #11
0
def test_transaction_creation_via_simple_transaction_with_blockr(alice, bob):
    from transactions import Transactions
    trxs = Transactions(testnet=True)
    assert trxs.testnet is True
    simple_transaction = trxs.simple_transaction(alice,
                                                 (bob, 6),
                                                 min_confirmations=1)
    assert simple_transaction
Example #12
0
 def test_sums_transactions_by_sku(self):
     rates = Rates([])
     transactions = [
         {"store": "Store", "sku": "SKU123", "amount": Decimal("1.1"), "currency": "FOO"},
         {"store": "Store", "sku": "SKU123", "amount": Decimal("1.2"), "currency": "FOO"},
         {"store": "Store", "sku": "SKU123", "amount": Decimal("1.3"), "currency": "FOO"},
         {"store": "Store", "sku": "SKU234", "amount": Decimal("5.0"), "currency": "FOO"}]
     trans = Transactions(rates, transactions)
     self.assertEqual(trans.total("SKU123", "FOO"), Decimal("3.6"))
def delete_transaction():
    if request.method == 'POST':
        id = request.form['transaction-id']

        transaction_instance = Transactions()
        transaction_instance.delete_transaction(id)
        return redirect('/transactions')
    else:
        return render_template('delete_transaction.html')
def add_new_transaction():
    if request.method == 'POST':
        total = request.form['transaction-total']
        method = request.form['transaction-method']

        transaction_instance = Transactions()
        transaction_instance.add_transaction(total, method)
        return redirect('/transactions')
    else:
        return render_template('add_new_transaction.html')
Example #15
0
 def test_converts_transactions_to_currency(self):
     rates = Rates([
         {"from": "FOO", "to": "BAR", "conversion": Decimal("0.98")},
         {"from": "BAR", "to": "BAZ", "conversion": Decimal("1.02")}])
     transactions = [
         {"store": "Store", "sku": "SKU123", "amount": Decimal("1.1"), "currency": "FOO"},
         {"store": "Store", "sku": "SKU123", "amount": Decimal("1.2"), "currency": "BAR"},
         {"store": "Store", "sku": "SKU123", "amount": Decimal("1.3"), "currency": "BAZ"}]
     trans = Transactions(rates, transactions)
     self.assertEqual(trans.total("SKU123", "FOO"), Decimal('3.62501'))
Example #16
0
 def __init__(self, **kwargs):
     self.rename_pdfs()
     self.statements = []
     self.statements_dir = kwargs['statements-dir']
     self.bank_name = kwargs['bank-name']
     self.name = kwargs['name']
     self.ledger_account = kwargs['ledger-account']
     self.account_num = None
     self.config = c['banks'][self.bank_name]['accounts'][self.name]
     Transactions.__init__(self)
Example #17
0
def check_status():
    """
    Checks the status of the federation and refill wallet every hour.
    Actions performed:

    1. In the refill wallets create refill chunks of 2 160 000 satoshi if number of chunks:
        - 50 * 30000 (fee) + 150 * 3000 (token) + 210 000 (tx fee)
        - if number of refill chunks < 10 create 5 chunks

    2. Refill federation wallet if num fees < 500 or num tokens < 1500:
        - refill with 2 refill chunks

    3. If refill wallet < 0.3 bitcoin which is approximately 10 refill chunks
        - Send email warning of low funds in the Refill wallet
        - Ask trent for das money
    """
    # We cannot use base=SpoolAction because it does not work with periodic tasks
    transactions = Transactions(service=settings.BTC_SERVICE,
                                testnet=settings.BTC_TESTNET,
                                username=settings.BTC_USERNAME,
                                password=settings.BTC_PASSWORD,
                                host=settings.BTC_HOST,
                                port=settings.BTC_PORT)

    # check refill wallet
    logger.info('checking refill wallet')
    unspents = transactions.get(settings.BTC_REFILL_ADDRESS,
                                min_confirmations=1).get('unspents', [])
    refill_chunks = filter(lambda d: d['amount'] == settings.BTC_CHUNK,
                           unspents)
    if len(refill_chunks) < 10:
        # create_refill_chunks -> create 5 refill_chunks
        logger.info('refill wallet low on refill chunks. Creating ...')
        txid = create_refill_chunks.delay()
        logger.info(txid)

    unspents = transactions.get(settings.BTC_REFILL_ADDRESS,
                                min_confirmations=1).get('unspents', [])
    sum_unspents = sum([u['amount'] for u in unspents])
    if sum_unspents < 30000000:
        # TODO: we don't want this to be sent every hour, maybe
        logger.info('funds low in refill wallet. Sending email...')
        send_low_funds_email.delay(sum_unspents)

    # check federation wallet
    unspents = transactions.get(settings.BTC_MAIN_WALLET,
                                min_confirmations=1).get('unspents', [])
    fees = filter(lambda d: d['amount'] == settings.BTC_FEE, unspents)
    tokens = filter(lambda d: d['amount'] == settings.BTC_TOKEN, unspents)
    if len(fees) < 500 or len(tokens) < 1500:
        # refill federation wallet x 10 times if we have chunks
        logger.info('federation wallet low on funds. Refilling...')
        for _ in range(min(2, len(refill_chunks))):
            refill_main_wallet.delay()
        initialize_federation_wallet.delay()
    def test_create_transaction(self):
        if os.path.isfile(FILE_NAME):
            os.remove(FILE_NAME)
        create_db(FILE_NAME)
        trs = Transactions(FILE_NAME)

        c = trs.create_transaction(-20000, 1)
        ts = c[2]
        self.assertEqual((1, -20000, ts, 1), c)

        os.remove(FILE_NAME)
def test_raw_push_tx(alice, bob, alice_secret, blockr):
    from transactions import Transactions
    transactions = Transactions(testnet=True)
    raw_tx = transactions.create(alice, (bob, 1), min_confirmations=1)
    signed_tx = transactions.sign(raw_tx, alice_secret)
    response = blockr.push_tx(signed_tx, raw=True)
    assert response.status_code == 200
    assert response.json()['status'] == 'success'
    assert response.json()['message'] == ''
    assert response.json()['code'] == 200
    assert response.json()['data']
def test_raw_push_tx(alice, bob, alice_secret, blockr):
    from transactions import Transactions
    transactions = Transactions(testnet=True)
    raw_tx = transactions.create(alice, (bob, 1), min_confirmations=1)
    signed_tx = transactions.sign(raw_tx, alice_secret)
    response = blockr.push_tx(signed_tx, raw=True)
    assert response.status_code == 200
    assert response.json()['status'] == 'success'
    assert response.json()['message'] == ''
    assert response.json()['code'] == 200
    assert response.json()['data']
Example #21
0
def rescan():
    print 'Rescanning the blockchain. This may take several minutes...'
    transactions = Transactions(service=settings.BTC_SERVICE,
                                testnet=settings.BTC_TESTNET,
                                username=settings.BTC_USERNAME,
                                password=settings.BTC_PASSWORD,
                                host=settings.BTC_HOST,
                                port=settings.BTC_PORT)
    main_address = BitcoinWallet.mainAdminBtcAddress()
    print 'Sending rescan command to {} main address {}'.format(
        settings.BTC_HOST, main_address)
    transactions.import_address(main_address, 'mainaddress', rescan=True)
Example #22
0
    def __init__(self, testnet=False, service='blockr', username='', password='', host='', port=''):
        """

        :param testnet:
        :return:
        """
        self.testnet = testnet
        self._netcode = 'XTN' if testnet else 'BTC'
        self._t = Transactions(service=service, testnet=testnet, username=username,
                               password=password, host=host, port=port)
        # simple cache for spent outputs. Useful so that rapid firing transactions don't use the same outputs
        self._spents = Queue(maxsize=50)
    def test_retrieve_id(self):
        if os.path.isfile(FILE_NAME):
            os.remove(FILE_NAME)
        create_db(FILE_NAME)
        trs = Transactions(FILE_NAME)
        trs.create_transaction(10, 1)

        c = trs.retrieve_id(id=1)
        ts = c[2]
        self.assertEqual((1, 10, ts, 1), c)

        os.remove(FILE_NAME)
    def test_retrieve_transactions(self):
        if os.path.isfile(FILE_NAME):
            os.remove(FILE_NAME)
        create_db(FILE_NAME)
        trs = Transactions(FILE_NAME)
        trs.create_transaction(10, 1)
        trs.create_transaction(20, 1)

        c = trs.retrieve_transactions()
        ts1 = c[0][2]
        ts2 = c[1][2]
        self.assertEqual([(1, 10, ts1, 1), (2, 20, ts2, 1)], c)

        os.remove(FILE_NAME)
Example #25
0
    def setUp(self):
        try:
            # flag to run the tests
            test = os.environ['TEST_SPOOL']
            if test == '2':
                username = os.environ['TESTNET_USERNAME']
                password = os.environ['TESTNET_PASSWORD']
                host = os.environ['TESTNET_HOST']
                port = os.environ['TESTNET_PORT']
            self.refill_pass = os.environ['TEST_REFILL_PASS']
            self.federation_pass = os.environ['TEST_FEDERATION_PASS']
            self.refill_root = Wallet(self.refill_pass, testnet=True).root_address
            self.federation_root = Wallet(self.federation_pass, testnet=True).root_address
        except KeyError:
            raise unittest.SkipTest('TEST_REFILL_PASS and/or TEST_FEDERATION_PASS environment variables are not set.')

        # set TEST_SPOOL=2 to test with bitcoind
        if test == '2':
            print 'using bitcoind'
            self.t = Transactions(testnet=True, service='daemon', username=username, password=password, host=host, port=port)
            self.spool = Spool(testnet=True, service='daemon', username=username, password=password, host=host, port=port)
        else:
            print 'using blockr'
            self.t = Transactions(testnet=True)
            self.spool = Spool(testnet=True)

        self.user1_pass = self._get_pass()
        self.user2_pass = self._get_pass()
        self.user3_pass = self._get_pass()
        self.user1_root = Wallet(self.user1_pass, testnet=True).root_address
        self.user1_leaf = Wallet(self.user1_pass, testnet=True).address_from_path()
        self.user2_leaf = Wallet(self.user2_pass, testnet=True).address_from_path()
        self.user3_leaf = Wallet(self.user3_pass, testnet=True).address_from_path()
        self.file_hash = self._get_file_hash()

        print 'user1_pass: '******'user2_pass: '******'user3_pass: '******'user1_root: ', self.user1_root
        print 'user1_leaf: ', self.user1_leaf
        print 'user2_leaf: ', self.user2_leaf
        print 'user3_leaf: ', self.user3_leaf
        print 'file_hash :', self.file_hash

        self.spool._t.import_address(self.user1_root[1], "test",)
        self.spool._t.import_address(self.user1_leaf[1], "test",)
        self.spool._t.import_address(self.user2_leaf[1], "test",)
        self.spool._t.import_address(self.user3_leaf[1], "test",)
        self.spool._t.import_address(self.file_hash[0], "test",)
        self.spool._t.import_address(self.file_hash[1], "test",)
Example #26
0
def wallet_status():
    print 'Checking Federation wallet status...'
    transactions = Transactions(service=settings.BTC_SERVICE, testnet=settings.BTC_TESTNET,
                                username=settings.BTC_USERNAME, password=settings.BTC_PASSWORD,
                                host=settings.BTC_HOST, port=settings.BTC_PORT)
    main_address = BitcoinWallet.mainAdminBtcAddress()
    unspents = transactions.get(main_address, min_confirmations=1)['unspents']
    fees = 0
    tokens = 0
    for u in unspents:
        if u['amount'] == settings.BTC_TOKEN:
            tokens += 1
        elif u['amount'] == settings.BTC_FEE:
            fees += 1

    print "Wallet {} has {} tokens and {} fees".format(main_address, tokens, fees)
Example #27
0
def check_unconfirmed_transactions():
    # Check for unconfirmed transactions and set status to 2 if confirmed

    transactions = Transactions()
    count = 0
    for t in BitcoinTransaction.objects.filter(Q(status=0) | Q(status=1), tx__isnull=False):
        try:
            conf = transactions.get(t.tx).get('confirmations', 0)
        except Exception as e:
            conf = 0

        if conf > 0:
            t.status = 2
            t.save()
            count += 1
    print "Set status of {} transactions to 2".format(count)
Example #28
0
    def test_import_address(self):
        t = Transactions(service=settings.BTC_SERVICE,
                         testnet=settings.BTC_TESTNET,
                         username=settings.BTC_USERNAME,
                         password=settings.BTC_PASSWORD,
                         host=settings.BTC_HOST,
                         port=settings.BTC_PORT)

        with self.settings(BTC_ENABLED=False):
            address = BitcoinWallet.walletForUser(
                self.user1).create_new_address()
            BitcoinWallet.import_address(address, self.user1).delay()
            address = address.split(':')[1]

        # with btc disabled the address should not be imported
        response = t._service.make_request('getaddressesbyaccount',
                                           [self.user1.email])
        self.assertIsNone(response['error'])
        self.assertFalse(address in response['result'])

        # lets import it
        import_address(address, self.user1.email)
        response = t._service.make_request('getaddressesbyaccount',
                                           [self.user1.email])
        self.assertIsNone(response['error'])
        self.assertTrue(address in response['result'])

        # lets create a new address with btc enabled
        address = BitcoinWallet.walletForUser(self.user1).create_new_address()
        BitcoinWallet.import_address(address, self.user1).delay()
        address = address.split(':')[1]
        response = t._service.make_request('getaddressesbyaccount',
                                           [self.user1.email])
        self.assertIsNone(response['error'])
        self.assertTrue(address in response['result'])
Example #29
0
    def __init__(self, testnet=False, service='blockr', username='', password='', host='', port=''):
        """

        :param testnet: testnet flag. Defaults to False
        :return: An instance of the BlockainSpider
        """
        self._t = Transactions(service=service, testnet=testnet, username=username,
                               password=password, host=host, port=port)
Example #30
0
def fight_bandit(player, bandit):
    if skill_check(player.fighter):
        # successful fight attempt
        # take the bandits credits
        winnings = int(bandit["demand"] * (5 / 4))
        player.credit += winnings
        player.karma += 2
        player.transaction_history.append(Transactions("Bandit loot", winnings, "loot", "earnings"))
        return True
    else:
        # fail fight attempt
        # lose 1/3 credits and get damaged
        losings = int(player.credit / 3)
        player.credit = losings
        player.ship.health_level -= 20
        player.transaction_history.append(Transactions("Bandit fee", losings, "fees", "expenses"))
        return False
Example #31
0
 def purchase_fuel(self, fuel):
     cost = fuel * self.fuel_cost
     if self.credit < cost:
         return "Not enough cash"
     elif self.ship.fuel_level == self.ship.max_fuel:
         return "Already fully fueled!"
     elif self.ship.fuel_level + fuel > self.ship.max_fuel:
         new_fuel = self.ship.max_fuel - self.ship.fuel_level
         cost = new_fuel * self.fuel_cost
         self.credit -= cost
         self.ship.fuel_level = self.ship.max_fuel
         self.transaction_history.append(Transactions("fuel", cost, "fuel", "expenses"))
         return "Success"
     else:
         self.credit -= cost
         self.ship.refuel(fuel)
         self.transaction_history.append(Transactions("fuel", cost, "fuel", "expenses"))
         return "Success"
Example #32
0
def transactions(rpcuser, rpcpassword, host, port):
    return Transactions(
        service='daemon',
        username=rpcuser,
        password=rpcpassword,
        host=host,
        port=port,
        testnet=True,
    )
Example #33
0
 def test_create_refill_chunks(self, mock_unsubscribe, mock_subscribe):
     t = Transactions(service=settings.BTC_SERVICE,
                      testnet=settings.BTC_TESTNET,
                      username=settings.BTC_USERNAME,
                      password=settings.BTC_PASSWORD,
                      host=settings.BTC_HOST,
                      port=settings.BTC_PORT)
     unspents = t.get(settings.BTC_REFILL_ADDRESS,
                      min_confirmations=1).get('unspents', [])
     refill_chuncks_before = filter(
         lambda d: d['amount'] == settings.BTC_CHUNK, unspents)
     create_refill_chunks.delay()
     unspents = t.get(settings.BTC_REFILL_ADDRESS,
                      min_confirmations=1).get('unspents', [])
     refill_chuncks_after = filter(
         lambda d: d['amount'] == settings.BTC_CHUNK, unspents)
     self.assertEqual(len(refill_chuncks_after),
                      len(refill_chuncks_before) + 5)
Example #34
0
    def __init__(self,
                 testnet=False,
                 service='blockr',
                 username='',
                 password='',
                 host='',
                 port=''):
        """

        :param testnet: testnet flag. Defaults to False
        :return: An instance of the BlockainSpider
        """
        self._t = Transactions(service=service,
                               testnet=testnet,
                               username=username,
                               password=password,
                               host=host,
                               port=port)
Example #35
0
 def buy_repairs(self, repairs):
     cost = utility.repair_cost(repairs, self.engineer)
     if self.credit < cost:
         return "Not enough Money!"
     elif self.ship.health_level == self.ship.max_health:
         return "Already fully repaired!"
     elif self.ship.health_level + repairs > self.ship.max_health:
         damage = self.ship.max_health - self.ship.health_level
         cost = utility.repair_cost(damage, self.engineer)
         self.credit -= cost
         self.ship.health_level = self.ship.max_health
         self.transaction_history.append(Transactions("repairs", cost, "repairs", "expenses"))
         return "Success"
     else:
         self.credit -= cost
         self.ship.health_level += repairs
         self.transaction_history.append(Transactions("repairs", cost, "repairs", "expenses"))
         return "Success"
Example #36
0
    def __init__(self, agent_id_to_grid_id, fname='Grid.xlsx', dt=1):
        self.current_transactions = Transactions()
        self.chain = []
        self.nodes = set()

        self.agent_id_to_grid_id = agent_id_to_grid_id

        self.actors_group = ActorsGroup()

        self.market = Market(actors_group=self.actors_group)

        self.dt = dt

        self.grid = MultiCircuit()
        self.grid.load_file(fname)

        # Create the genesis block
        self.new_block(previous_hash='1', proof=100)
Example #37
0
def test_init_transactions_class(srv, srv_mod_name, srv_cls_name, is_testnet):
    from transactions import Transactions
    service_module = import_module(
        '.{}'.format(srv_mod_name), package='transactions.services')
    service_class = getattr(service_module, srv_cls_name)
    trxs = Transactions(service=srv)
    assert trxs.testnet is is_testnet
    assert isinstance(trxs._service, service_class)
    assert trxs._min_tx_fee == trxs._service._min_transaction_fee
    assert trxs._dust == trxs._service._min_dust
Example #38
0
    def test_make_transaction(self):
        alg = self._init_test()
        self._prefill_db(alg)
        trs = Transactions(FILE_NAME)
        fnds = Funds(FILE_NAME)

        accs = alg.make_transaction('01', 100)
        self.assertEqual(500.0, alg.portfolio_total_fund)
        accounts = [
            (1, '01', 0.2, accs[0][3], 0, 0.7, 0.7, 1, accs[0][-1]),
            (2, '02', 0.2, accs[1][3], 50, 0.3, 0.9, 1, accs[1][-1]),
            (3, '03', 0.2, accs[2][3], -50, 0.3, 0.9, 1, accs[2][-1]),
            (4, '04', 0.4, accs[3][3], 0, 0.3, 0.9, 1, accs[3][-1]),
        ]
        self.assertEqual(accounts, accs)
        acc_trs = trs.retrieve_transactions_for_account(account_id=1)
        self.assertEqual(100, acc_trs[-1][1])
        acc_fnds = fnds.retrieve_funds_for_account(account_id=1)
        self.assertEqual(100, acc_fnds[-1][1])

        accs = alg.make_transaction('02', -100)
        self.assertEqual(400.0, alg.portfolio_total_fund)
        accounts = [
            (1, '01', 0.25, accs[0][3], 0, 0.7, 0.7, 1, accs[0][-1]),
            (2, '02', 0.0, accs[1][3], 50, 0.3, 0.9, 1, accs[1][-1]),
            (3, '03', 0.25, accs[2][3], -50, 0.3, 0.9, 1, accs[2][-1]),
            (4, '04', 0.5, accs[3][3], 0, 0.3, 0.9, 1, accs[3][-1]),
        ]
        self.assertEqual(accounts, accs)
        acc_trs = trs.retrieve_transactions_for_account(account_id=2)
        self.assertEqual(-100, acc_trs[-1][1])
        acc_fnds = fnds.retrieve_funds_for_account(account_id=2)
        self.assertEqual(0, acc_fnds[-1][1])

        accs = alg.make_transaction('03', -150)
        self.assertEqual(400.0, alg.portfolio_total_fund)
        self.assertEqual(False, accs)
        acc_trs = trs.retrieve_transactions_for_account(account_id=3)
        self.assertEqual(0, len(acc_trs))
        acc_fnds = fnds.retrieve_funds_for_account(account_id=3)
        self.assertEqual(100, acc_fnds[-1][1])
Example #39
0
def collection():
    if request.method == "POST":
        if "addCredits" in request.form:
            category = request.form["addCredits"]
            index = state["game"].player.collection.category.index(category)
            state["game"].player.collection.complete[index] = True
            state["game"].player.credit += 100
            state["game"].player.transaction_history.append(Transactions("Collection reward", 100, "collection", "earnings"))
            return str(state["game"].player.credit)
    return render_template(
        "collection.html", game=state["game"], all_items=Item.__subclasses__()
    )
Example #40
0
    def test_internal_transaction(self):
        alg = self._init_test()
        self._prefill_db(alg)
        trs = Transactions(FILE_NAME)
        fnds = Funds(FILE_NAME)

        accs = alg.internal_transaction(sender_name='02', reciever_name='01', amount=50)
        self.assertEqual(400.0, alg.portfolio_total_fund)
        accounts = [
            (1, '01', 0.125, accs[0][3], 0, 0.7, 0.7, 1, accs[0][-1]),
            (2, '02', 0.125, accs[1][3], 50, 0.3, 0.9, 1, accs[1][-1]),
            (3, '03', 0.25, accs[2][3], -50, 0.3, 0.9, 1, accs[2][-1]),
            (4, '04', 0.5, accs[3][3], 0, 0.3, 0.9, 1, accs[3][-1]),
        ]
        self.assertEqual(accounts, accs)
        acc_trs = trs.retrieve_transactions_for_account(account_id=1)
        self.assertEqual(50, acc_trs[-1][1])
        acc_trs = trs.retrieve_transactions_for_account(account_id=2)
        self.assertEqual(-50, acc_trs[-1][1])
        acc_fnds = fnds.retrieve_funds_for_account(account_id=1)
        self.assertEqual(50, acc_fnds[-1][1])
        acc_fnds = fnds.retrieve_funds_for_account(account_id=2)
        self.assertEqual(50, acc_fnds[-1][1])

        accs = alg.internal_transaction(sender_name='02', reciever_name='01', amount=100)
        self.assertEqual(400.0, alg.portfolio_total_fund)
        self.assertEqual(False, accs)
        acc_trs = trs.retrieve_transactions_for_account(account_id=1)
        self.assertEqual(50, acc_trs[-1][1])
        acc_trs = trs.retrieve_transactions_for_account(account_id=2)
        self.assertEqual(-50, acc_trs[-1][1])
        acc_fnds = fnds.retrieve_funds_for_account(account_id=1)
        self.assertEqual(50, acc_fnds[-1][1])
        acc_fnds = fnds.retrieve_funds_for_account(account_id=2)
        self.assertEqual(50, acc_fnds[-1][1])
Example #41
0
    def __init__(self, testnet=False, service='blockr', username='', password='', host='', port=''):
        """
        Args:
            testnet (bool): Whether to use the mainnet or testnet.
                Defaults to the mainnet (:const:`False`).
            service (str): Bitcoin communication interface: ``'blockr'``,
                ``'daemon'``, or ``'regtest'``. ``'blockr'`` refers to the
                public api, whereas ``'daemon'`` and ``'regtest'`` refer
                to the jsonrpc inteface. Defaults to ``'blockr'``.
            username (str): username for jsonrpc communications
            password (str): password for jsonrpc communications
            hostname (str): hostname of the bitcoin node when using jsonrpc
            port (str): port number of the bitcoin node when using jsonrpc

        """
        self._t = Transactions(service=service, testnet=testnet, username=username,
                               password=password, host=host, port=port)
Example #42
0
    def __init__(self, testnet=False, service='blockr', username='', password='', host='', port=''):
        """
        Args:
            testnet (bool): Whether to use the mainnet or testnet.
                Defaults to the mainnet (:const:`False`).
            service (str): Bitcoin communication interface: ``'blockr'``,
                ``'daemon'``, or ``'regtest'``. ``'blockr'`` refers to the
                public api, whereas ``'daemon'`` and ``'regtest'`` refer
                to the jsonrpc inteface. Defaults to ``'blockr'``.
            username (str): username for jsonrpc communications
            password (str): password for jsonrpc communications
            hostname (str): hostname of the bitcoin node when using jsonrpc
            port (str): port number of the bitcoin node when using jsonrpc

        """
        self.testnet = testnet
        self._netcode = 'XTN' if testnet else 'BTC'
        self._t = Transactions(service=service, testnet=testnet, username=username,
                               password=password, host=host, port=port)
        # simple cache for spent outputs. Useful so that rapid firing transactions don't use the same outputs
        self._spents = Queue(maxsize=self.SPENTS_QUEUE_MAXSIZE)
Example #43
0
def test_decode_transaction_with_blockr(signed_tx_hex):
    from transactions import Transactions

    transactions = Transactions(testnet=True)
    decoded_tx = transactions.decode(signed_tx_hex)
    assert decoded_tx
Example #44
0
class BlockchainSpider(object):
    """
    Spool blockchain explorer. Retrieves from the blockchain
    the chain of ownership of a hash created with the
    `SPOOL <https://github.com/ascribe/spool>`_ protocol.

    """

    def __init__(self, testnet=False, service='blockr', username='', password='', host='', port=''):
        """
        Args:
            testnet (bool): Whether to use the mainnet or testnet.
                Defaults to the mainnet (:const:`False`).
            service (str): Bitcoin communication interface: ``'blockr'``,
                ``'daemon'``, or ``'regtest'``. ``'blockr'`` refers to the
                public api, whereas ``'daemon'`` and ``'regtest'`` refer
                to the jsonrpc inteface. Defaults to ``'blockr'``.
            username (str): username for jsonrpc communications
            password (str): password for jsonrpc communications
            hostname (str): hostname of the bitcoin node when using jsonrpc
            port (str): port number of the bitcoin node when using jsonrpc

        """
        self._t = Transactions(service=service, testnet=testnet, username=username,
                               password=password, host=host, port=port)

    def history(self, hash):
        """
        Retrieve the ownership tree of all editions of a piece given the hash.

        Args:
            hash (str): Hash of the file to check. Can be created with the
                :class:`File` class

        Returns:
            dict: Ownsership tree of all editions of a piece.

        .. note:: For now we only support searching the blockchain by
            the piece hash.

        """
        txs = self._t.get(hash, max_transactions=10000)['transactions']
        tree = defaultdict(list)
        number_editions = 0

        for tx in txs:
            _tx = self._t.get(tx['txid'])
            txid = _tx['txid']
            verb_str = BlockchainSpider.check_script(_tx['vouts'])
            verb = Spoolverb.from_verb(verb_str)
            from_address, to_address, piece_address = BlockchainSpider._get_addresses(_tx)
            timestamp_utc = _tx['time']
            action = verb.action

            edition_number = 0
            if action != 'EDITIONS':
                edition_number = verb.edition_number
            else:
                number_editions = verb.num_editions

            tree[edition_number].append({'txid': txid,
                                         'verb': verb_str,
                                         'from_address': from_address,
                                         'to_address': to_address,
                                         'piece_address': piece_address,
                                         'timestamp_utc': timestamp_utc,
                                         'action': action,
                                         'number_editions': number_editions,
                                         'edition_number': edition_number})

        # lets update the records with the number of editions of the piece since we do not know
        # this information before the EDITIONS transaction
        for edition, chain in tree.items():
            [d.update({'number_editions': number_editions}) for d in chain]
        return dict(tree)

    @staticmethod
    def chain(tree, edition_number):
        """
        Args:
            tree (dict): Tree history of all editions of a piece.
            edition_number (int): The edition number to check for.
                In the case of a piece (master edition), an empty
                string (``''``) or zero (``0``) can be passed.

        Returns:
            list: The chain of ownsership of a particular
            edition of the piece ordered by time.

        """
        # return the chain for an edition_number sorted by the timestamp
        return sorted(tree.get(edition_number, []), key=lambda d: d['timestamp_utc'])

    @staticmethod
    def strip_loan(chain):
        """
        Returns the chain without loan. This way we can
        look at the last transaction to establish ownership.

        Args:
            chain (list): Chain for a particular edition.

        Returns:
            list: Chain with loan transactions striped
            from the end of the chain.

        """
        while chain[-1]['action'] == 'LOAN':
            chain.pop()

        return chain

    @staticmethod
    def pprint(tree):
        """
        Utility function to pretty print the history tree of a piece.

        Args:
            tree (dict): History tree of a piece.

        """
        p = PrettyPrinter(indent=2)
        p.pprint(tree)

    @staticmethod
    def decode_op_return(op_return_hex):
        """
        Decodes the given ``op_return`` hexadecimal
        string representation into a string (:obj:`str`).

        Args:
            op_return_hex (str): Hexadecimal string
                representation of the ``op_return``.

        Returns:
            str: String representation of the ``op_return``.

        """
        return binascii.unhexlify(op_return_hex[4:])

    @staticmethod
    def check_script(vouts):
        """
        Looks into the vouts list of a transaction
        and returns the ``op_return`` if one exists.

        Args;
            vouts (list): List of outputs of a transaction.

        Returns:
            str: String representation of the ``op_return``.

        Raises:
            Exception: If no ``vout`` having a supported
                verb (:attr:`supported_actions`) is found.

        """
        for vout in [v for v in vouts[::-1] if v['hex'].startswith('6a')]:
            verb = BlockchainSpider.decode_op_return(vout['hex'])
            action = Spoolverb.from_verb(verb).action
            if action in Spoolverb.supported_actions:
                return verb
        raise Exception("Invalid ascribe transaction")

    @staticmethod
    def _get_addresses(tx):
        """
        Checks for the from, to, and piece address of a SPOOL transaction.

        Args:
            tx (dict): Transaction payload, as returned by
                :meth:`transactions.Transactions.get()`.

        .. note:: Formats as returned by JSON-RPC API
            ``decoderawtransaction`` have yet to be supported.

        Returns:
            Tuple([str]): Sender, receiver, and piece addresses.

        """
        from_address = set([vin['address'] for vin in tx['vins']])
        if len(from_address) != 1:
            raise InvalidTransactionError("Transaction should have inputs " \
                                          "from only one address {}".format(from_address))

        # order vouts. discard the last vout since it's the op_return
        vouts = sorted(tx['vouts'], key=lambda d: d['n'])[:-1]
        piece_address = vouts[0]['address']
        to_address = vouts[-1]['address']
        from_address = from_address.pop()

        return from_address, to_address, piece_address

    @staticmethod
    def _get_time_utc(time_utc_str):
        """
        Convert a string representation of the time (as returned by
        blockr.io api) into unix timestamp.

        Args:
            time_utc_str (str): String representation of the time, with the
                format: `'%Y-%m-%dT%H:%M:%S %Z'`.

        Returns:
            int: Unix timestamp.

        """
        dt = datetime.strptime(time_utc_str, TIME_FORMAT)
        return int(calendar.timegm(dt.utctimetuple()))
Example #45
0
class Spool(object):
    """
    Class that contains all Spool methods.

    In the SPOOL implementation there is no notion of users only addresses.
    All addresses come from BIP32 HD wallets. This makes it easier to manage all the keys
    since we can retrieve everything we need from a master secret (namely the private key
    to sign the transactions).

    Since we are dealing with HD wallets we expect all ``from_address`` to be a
    tuple of ``(path, address)`` so that we can retrieve the private key for
    that particular leaf address. If we want to use the root address we can
    just pass an empty string to the first element of the tuple e.g.
    ``('', address)``. For instance when using the federation wallet address we
    have no need to create leaf addresses.

    A file is represented by two hashes:
        - ``file_hash``: is the hash of the digital file
        - ``file_hash_metadata``: is the hash of the digital file + metadata

    The hash is passed to the methods has a tuple: ``(file_hash, file_hash_metadata)``

    Attributes:
        FEE (int): transaction fee
        TOKEN (int): token
        SPENTS_QUEUE_MAXSIZE (int): spent outputs queue maximum size

    """
    FEE = 30000
    TOKEN = 3000
    SPENTS_QUEUE_MAXSIZE = 50

    def __init__(self, testnet=False, service='blockr', username='', password='', host='', port=''):
        """
        Args:
            testnet (bool): Whether to use the mainnet or testnet.
                Defaults to the mainnet (:const:`False`).
            service (str): Bitcoin communication interface: ``'blockr'``,
                ``'daemon'``, or ``'regtest'``. ``'blockr'`` refers to the
                public api, whereas ``'daemon'`` and ``'regtest'`` refer
                to the jsonrpc inteface. Defaults to ``'blockr'``.
            username (str): username for jsonrpc communications
            password (str): password for jsonrpc communications
            hostname (str): hostname of the bitcoin node when using jsonrpc
            port (str): port number of the bitcoin node when using jsonrpc

        """
        self.testnet = testnet
        self._netcode = 'XTN' if testnet else 'BTC'
        self._t = Transactions(service=service, testnet=testnet, username=username,
                               password=password, host=host, port=port)
        # simple cache for spent outputs. Useful so that rapid firing transactions don't use the same outputs
        self._spents = Queue(maxsize=self.SPENTS_QUEUE_MAXSIZE)

    @dispatch
    def register_piece(self, from_address, to_address, hash, password, min_confirmations=6, sync=False, ownership=True):
        """
        Register a piece

        Args:
            from_address (Tuple[str]): Federation address. All register transactions
                originate from the the Federation wallet
            to_address (str): Address registering the edition
            hash (Tuple[str]): Hash of the piece. (file_hash, file_hash_metadata)
            password (str): Federation wallet password. For signing the transaction
            edition_num (int): The number of the edition to register. User
                edition_num=0 to register the master edition
            min_confirmations (int): Override the number of confirmations when
                chosing the inputs of the transaction. Defaults to 6
            sync (bool): Perform the transaction in synchronous mode, the call to the
                function will block until there is at least on confirmation on
                the blockchain. Defaults to False
            ownership (bool): Check ownsership in the blockchain before pushing the
                transaction. Defaults to True

        Returns:
            str: transaction id

        """
        file_hash, file_hash_metadata = hash
        path, from_address = from_address
        verb = Spoolverb()
        unsigned_tx = self.simple_spool_transaction(from_address,
                                                    [file_hash, file_hash_metadata, to_address],
                                                    op_return=verb.piece,
                                                    min_confirmations=min_confirmations)

        signed_tx = self._t.sign_transaction(unsigned_tx, password)
        txid = self._t.push(signed_tx)
        return txid

    @dispatch
    def register(self, from_address, to_address, hash, password, edition_num, min_confirmations=6, sync=False, ownership=True):
        """
        Register an edition or master edition of a piece

        Args:
            from_address (Tuple[str]): Federation address. All register transactions originate from the the Federation wallet
            to_address (str): Address registering the edition
            hash (Tuple[str])): Hash of the piece. Tuple (file_hash, file_hash_metadata)
            password (str): Federation wallet password. For signing the transaction
            edition_num (int): The number of the edition to register. User edition_num=0 to register the master edition
            min_confirmations (int): Override the number of confirmations when chosing the inputs of the transaction. Defaults to 6
            sync (bool): Perform the transaction in synchronous mode, the call to the function will block until there is at
                least on confirmation on the blockchain. Defaults to False
            ownership (bool): Check ownsership in the blockchain before pushing the transaction. Defaults to True

        Returns:
            str: transaction id

        """
        file_hash, file_hash_metadata = hash
        path, from_address = from_address
        verb = Spoolverb(edition_num=edition_num)
        unsigned_tx = self.simple_spool_transaction(from_address,
                                                    [file_hash, file_hash_metadata, to_address],
                                                    op_return=verb.register,
                                                    min_confirmations=min_confirmations)

        signed_tx = self._t.sign_transaction(unsigned_tx, password)
        txid = self._t.push(signed_tx)
        return txid

    @dispatch
    def consigned_registration(self, from_address, to_address, hash, password, min_confirmations=6, sync=False, ownership=True):
        """
        Register an edition or master edition of a piece consigned to ``from_address``

        Args:
            from_address (Tuple[str])): Federation address. All register transactions originate from the the Federation wallet
            to_address (str): Address registering the edition
            hash (Tuple[str]): Hash of the piece. Tuple (file_hash, file_hash_metadata)
            password (str): Federation wallet password. For signing the transaction
            min_confirmations (int): Override the number of confirmations when chosing the inputs of the transaction. Defaults to 6
            sync (bool): Perform the transaction in synchronous mode, the call to the function will block until there is at
                least on confirmation on the blockchain. Defaults to False
            ownership (bool): Check ownsership in the blockchain before pushing the transaction. Defaults to True

        Returns:
            str: transaction id

        """
        file_hash, file_hash_metadata = hash
        path, from_address = from_address
        verb = Spoolverb()
        unsigned_tx = self.simple_spool_transaction(from_address,
                                                    [file_hash, file_hash_metadata, to_address],
                                                    op_return=verb.consigned_registration,
                                                    min_confirmations=min_confirmations)

        signed_tx = self._t.sign_transaction(unsigned_tx, password)
        txid = self._t.push(signed_tx)
        return txid

    @dispatch
    def editions(self, from_address, to_address, hash, password, num_editions, min_confirmations=6, sync=False, ownership=True):
        """
        Register the number of editions of a piece

        Args:
            from_address (Tuple[str]): Federation address. All register transactions originate from the the Federation wallet
            to_address (str): Address registering the number of editions
            hash (Tuple[str]): Hash of the piece. Tuple (file_hash, file_hash_metadata)
            password (str):  Federation wallet password. For signing the transaction
            num_editions (int): Number of editions of the piece
            min_confirmations (int): Number of confirmations when chosing the inputs of the transaction. Defaults to 6
            sync (bool): Perform the transaction in synchronous mode, the call to the function will block until there is at
                least on confirmation on the blockchain. Defaults to False
            ownership (bool): Check ownsership in the blockchain before pushing the transaction. Defaults to True

        Returns:
            str: transaction id

        """
        file_hash, file_hash_metadata = hash
        path, from_address = from_address
        verb = Spoolverb(num_editions=num_editions)
        unsigned_tx = self.simple_spool_transaction(from_address,
                                                    [file_hash, file_hash_metadata, to_address],
                                                    op_return=verb.editions,
                                                    min_confirmations=min_confirmations)

        signed_tx = self._t.sign_transaction(unsigned_tx, password)
        txid = self._t.push(signed_tx)
        return txid

    @dispatch
    def transfer(self, from_address, to_address, hash, password, edition_num, min_confirmations=6, sync=False, ownership=True):
        """
        Transfer a piece between addresses

        Args:
            from_address (Tuple[str]): Address currently owning the edition
            to_address: Address to receive the edition
            hash (Tuple[str]): Hash of the piece. Tuple (file_hash, file_hash_metadata)
            password (str): Password for the wallet currently owning the edition. For signing the transaction
            edition_num (int): the number of the edition to transfer
            min_confirmations (int): Number of confirmations when chosing the inputs of the transaction. Defaults to 6
            sync (bool): Perform the transaction in synchronous mode, the call to the function will block until there is at
                least on confirmation on the blockchain. Defaults to False
            ownership (bool): Check ownsership in the blockchain before pushing the transaction. Defaults to True

        Returns:
            str: transaction id

        """
        path, from_address = from_address
        file_hash, file_hash_metadata = hash
        verb = Spoolverb(edition_num=edition_num)
        unsigned_tx = self.simple_spool_transaction(from_address,
                                                    [file_hash, to_address],
                                                    op_return=verb.transfer,
                                                    min_confirmations=min_confirmations)
        signed_tx = self._t.sign_transaction(unsigned_tx, password, path=path)
        txid = self._t.push(signed_tx)
        return txid

    @dispatch
    def consign(self, from_address, to_address, hash, password, edition_num, min_confirmations=6, sync=False, ownership=True):
        """
        Consign a piece to an address

        Args:
            from_address (Tuple[str]): Address currently owning the edition
            to_address (str): Address to where the piece will be consigned to
            hash (Tuple[str]): Hash of the piece. Tuple (file_hash, file_hash_metadata)
            password (str): Password for the wallet currently owning the edition. For signing the transaction
            edition_num (int): the number of the edition to consign
            min_confirmations (int): Number of confirmations when chosing the inputs of the transaction. Defaults to 6
            sync (bool): Perform the transaction in synchronous mode, the call to the function will block until there is at
                least on confirmation on the blockchain. Defaults to False
            ownership (bool): Check ownsership in the blockchain before pushing the transaction. Defaults to True

        Returns:
            str: transaction id

        """
        path, from_address = from_address
        file_hash, file_hash_metadata = hash
        verb = Spoolverb(edition_num=edition_num)
        unsigned_tx = self.simple_spool_transaction(from_address,
                                                    [file_hash, to_address],
                                                    op_return=verb.consign,
                                                    min_confirmations=min_confirmations)
        signed_tx = self._t.sign_transaction(unsigned_tx, password, path=path)
        txid = self._t.push(signed_tx)
        return txid

    @dispatch
    def unconsign(self, from_address, to_address, hash, password, edition_num, min_confirmations=6, sync=False, ownership=True):
        """
        Unconsign the edition

        Args:
            from_address (Tuple[str]): Address where the edition is currently consigned
            to_address (str): Address that consigned the piece to from_address
            hash (Tuple[str]): Hash of the piece. Tuple (file_hash, file_hash_metadata)
            password (str): Password for the wallet currently holding the edition. For signing the transaction
            edition_num (int): the number of the edition to unconsign
            min_confirmations (int): Number of confirmations when chosing the inputs of the transaction. Defaults to 6
            sync (bool): Perform the transaction in synchronous mode, the call to the function will block until there is at
                least on confirmation on the blockchain. Defaults to False
            ownership (bool): Check ownsership in the blockchain before pushing the transaction. Defaults to True

        Returns:
            str: transaction id

        """
        # In an unconsignment the to_address needs to be the address that created the consign transaction
        path, from_address = from_address
        file_hash, file_hash_metadata = hash
        verb = Spoolverb(edition_num=edition_num)
        unsigned_tx = self.simple_spool_transaction(from_address,
                                                    [file_hash, to_address],
                                                    op_return=verb.unconsign,
                                                    min_confirmations=min_confirmations)
        signed_tx = self._t.sign_transaction(unsigned_tx, password, path=path)
        txid = self._t.push(signed_tx)
        return txid

    @dispatch
    def loan(self, from_address, to_address, hash, password, edition_num, loan_start, loan_end, min_confirmations=6, sync=False, ownership=True):
        """
        Loan the edition

        Args:
            from_address (Tuple[str]): Address currently holding the edition
            to_address (str): Address to loan the edition to
            hash (Tuple[str]): Hash of the piece. Tuple (file_hash, file_hash_metadata)
            password (str): Password for the wallet currently holding the edition. For signing the transaction
            edition_num (int): the number of the edition to loan
            loan_start (str): Start date for the loan. In the form YYMMDD
            loan_end (str): End date for the loan. In the form YYMMDD
            min_confirmations (int): Number of confirmations when chosing the inputs of the transaction. Defaults to 6
            sync (bool): Perform the transaction in synchronous mode, the call to the function will block until there is at
                least on confirmation on the blockchain. Defaults to False
            ownership (bool): Check ownsership in the blockchain before pushing the transaction. Defaults to True

        Returns:
            str: transaction id

        """
        path, from_address = from_address
        file_hash, file_hash_metadata = hash
        verb = Spoolverb(edition_num=edition_num, loan_start=loan_start, loan_end=loan_end)
        unsigned_tx = self.simple_spool_transaction(from_address,
                                                    [file_hash, to_address],
                                                    op_return=verb.loan,
                                                    min_confirmations=min_confirmations)
        signed_tx = self._t.sign_transaction(unsigned_tx, password, path=path)
        txid = self._t.push(signed_tx)
        return txid

    @dispatch
    def migrate(self, from_address, prev_address, new_address, hash, password, edition_num, min_confirmations=6, sync=False, ownership=True):
        """
        Migrate an edition

        Args:
            from_address (Tuple[str]): Federation address. All register transactions originate from the the Federation wallet
            to_address (str): Address registering the edition
            hash (Tuple[str]): Hash of the piece. Tuple (file_hash, file_hash_metadata)
            password (str): Federation wallet password. For signing the transaction
            edition_num (int): The number of the edition to register. User edition_num=0 to register the master edition
            min_confirmations (int): Override the number of confirmations when chosing the inputs of the transaction. Defaults to 6
            sync (bool): Perform the transaction in synchronous mode, the call to the function will block until there is at
                least on confirmation on the blockchain. Defaults to False
            ownership (bool): Check ownsership in the blockchain before pushing the transaction. Defaults to True

        Returns:
            str: transaction id

        """
        file_hash, file_hash_metadata = hash
        path, from_address = from_address
        verb = Spoolverb(edition_num=edition_num)
        unsigned_tx = self.simple_spool_transaction(from_address,
                                                    [file_hash, prev_address, new_address],
                                                    op_return=verb.migrate,
                                                    min_confirmations=min_confirmations)

        signed_tx = self._t.sign_transaction(unsigned_tx, password)
        txid = self._t.push(signed_tx)
        return txid

    @dispatch
    def refill_main_wallet(self, from_address, to_address, nfees, ntokens, password, min_confirmations=6, sync=False):
        """
        Refill the Federation wallet with tokens and fees. This keeps the federation wallet clean.
        Dealing with exact values simplifies the transactions. No need to calculate change. Easier to keep track of the
        unspents and prevent double spends that would result in transactions being rejected by the bitcoin network.

        Args:

            from_address (Tuple[str]): Refill wallet address. Refills the federation wallet with tokens and fees
            to_address (str): Federation wallet address
            nfees (int): Number of fees to transfer. Each fee is 10000 satoshi. Used to pay for the transactions
            ntokens (int): Number of tokens to transfer. Each token is 600 satoshi. Used to register hashes in the blockchain
            password (str): Password for the Refill wallet. Used to sign the transaction
            min_confirmations (int): Number of confirmations when chosing the inputs of the transaction. Defaults to 6
            sync (bool): Perform the transaction in synchronous mode, the call to the function will block until there is at
                least on confirmation on the blockchain. Defaults to False

        Returns:
            str: transaction id
        """
        path, from_address = from_address
        unsigned_tx = self._t.simple_transaction(from_address,
                                                 [(to_address, self.FEE)] * nfees + [(to_address, self.TOKEN)] * ntokens,
                                                 min_confirmations=min_confirmations)

        signed_tx = self._t.sign_transaction(unsigned_tx, password)
        txid = self._t.push(signed_tx)
        return txid

    @dispatch
    def refill(self, from_address, to_address, nfees, ntokens, password, min_confirmations=6, sync=False):
        """
        Refill wallets with the necessary fuel to perform spool transactions

        Args:
            from_address (Tuple[str]): Federation wallet address. Fuels the wallets with tokens and fees. All transactions to wallets
                holding a particular piece should come from the Federation wallet
            to_address (str): Wallet address that needs to perform a spool transaction
            nfees (int): Number of fees to transfer. Each fee is 10000 satoshi. Used to pay for the transactions
            ntokens (int): Number of tokens to transfer. Each token is 600 satoshi. Used to register hashes in the blockchain
            password (str): Password for the Federation wallet. Used to sign the transaction
            min_confirmations (int): Number of confirmations when chosing the inputs of the transaction. Defaults to 6
            sync (bool): Perform the transaction in synchronous mode, the call to the function will block until there is at
                least on confirmation on the blockchain. Defaults to False

        Returns:
            str: transaction id

        """
        path, from_address = from_address
        verb = Spoolverb()
        # nfees + 1: nfees to refill plus one fee for the refill transaction itself
        inputs = self.select_inputs(from_address, nfees + 1, ntokens, min_confirmations=min_confirmations)
        outputs = [{'address': to_address, 'value': self.TOKEN}] * ntokens
        outputs += [{'address': to_address, 'value': self.FEE}] * nfees
        outputs += [{'script': self._t._op_return_hex(verb.fuel), 'value': 0}]
        unsigned_tx = self._t.build_transaction(inputs, outputs)
        signed_tx = self._t.sign_transaction(unsigned_tx, password, path=path)
        txid = self._t.push(signed_tx)
        return txid

    def simple_spool_transaction(self, from_address, to, op_return, min_confirmations=6):
        """
        Utililty function to create the spool transactions. Selects the inputs,
        encodes the op_return and constructs the transaction.

        Args:
            from_address (str): Address originating the transaction
            to (str): list of addresses to receive tokens (file_hash, file_hash_metadata, ...)
            op_return (str): String representation of the spoolverb, as returned by the properties of Spoolverb
            min_confirmations (int): Number of confirmations when chosing the inputs of the transaction. Defaults to 6

        Returns:
            str: unsigned transaction

        """
        # list of addresses to send
        ntokens = len(to)
        nfees = old_div(self._t.estimate_fee(ntokens, 2), self.FEE)
        inputs = self.select_inputs(from_address, nfees, ntokens, min_confirmations=min_confirmations)
        # outputs
        outputs = [{'address': to_address, 'value': self.TOKEN} for to_address in to]
        outputs += [{'script': self._t._op_return_hex(op_return), 'value': 0}]
        # build transaction
        unsigned_tx = self._t.build_transaction(inputs, outputs)
        return unsigned_tx

    def select_inputs(self, address, nfees, ntokens, min_confirmations=6):
        """
        Selects the inputs for the spool transaction.

        Args:
            address (str): bitcoin address to select inputs for
            nfees (int): number of fees
            ntokens (int): number of tokens
            min_confirmations (Optional[int]): minimum number of required
                confirmations; defaults to 6

        """
        unspents = self._t.get(address, min_confirmations=min_confirmations)['unspents']
        unspents = [u for u in unspents if u not in self._spents.queue]
        if len(unspents) == 0:
            raise Exception("No spendable outputs found")

        fees = [u for u in unspents if u['amount'] == self.FEE][:nfees]
        tokens = [u for u in unspents if u['amount'] == self.TOKEN][:ntokens]
        if len(fees) != nfees or len(tokens) != ntokens:
            raise SpoolFundsError("Not enough outputs to spend. Refill your wallet")
        if self._spents.qsize() > self.SPENTS_QUEUE_MAXSIZE - (nfees + ntokens):
            [self._spents.get() for i in range(self._spents.qsize() + nfees + ntokens - self.SPENTS_QUEUE_MAXSIZE)]
        [self._spents.put(fee) for fee in fees]
        [self._spents.put(token) for token in tokens]
        return fees + tokens
Example #46
0
 def test_check_scripts(self):
     t = Transactions(testnet=True)
     tx = t.get(TXID)
     vouts = tx['vouts']
     verb = BlockchainSpider.check_script(vouts)
     self.assertEqual(verb, 'ASCRIBESPOOL01EDITIONS10')
Example #47
0
class BlockchainSpider(object):
    """
    Spool blockain explorer. Retrieves from the blockchain the chain of ownership of a hash created
    with the SPOOL protocol
    """

    def __init__(self, testnet=False, service='blockr', username='', password='', host='', port=''):
        """

        :param testnet: testnet flag. Defaults to False
        :return: An instance of the BlockainSpider
        """
        self._t = Transactions(service=service, testnet=testnet, username=username,
                               password=password, host=host, port=port)

    def history(self, hash):
        """
        Retrieve the ownership tree of all editions of a piece given the hash

        :param hash: Hash of the file to check. Can be created with the File class
        :return: ownsership tree of all editions of a piece
        """

        # For now we only support searching the blockchain by the piece hash
        txs = self._t.get(hash, max_transactions=10000)['transactions']
        tree = defaultdict(list)
        number_editions = 0

        for tx in txs:
            _tx = self._t.get(tx['txid'])
            txid = _tx['txid']
            verb_str = BlockchainSpider.check_script(_tx['vouts'])
            verb = Spoolverb.from_verb(verb_str)
            from_address, to_address, piece_address = BlockchainSpider._get_addresses(_tx)
            timestamp_utc = _tx['time']
            action = verb.action

            edition_number = 0
            if action != 'EDITIONS':
                edition_number = verb.edition_number
            else:
                number_editions = verb.num_editions

            tree[edition_number].append({'txid': txid,
                                         'verb': verb_str,
                                         'from_address': from_address,
                                         'to_address': to_address,
                                         'piece_address': piece_address,
                                         'timestamp_utc': timestamp_utc,
                                         'action': action,
                                         'number_editions': number_editions,
                                         'edition_number': edition_number})

        # lets update the records with the number of editions of the piece since we do not know
        # this information before the EDITIONS transaction
        for edition, chain in tree.iteritems():
            [d.update({'number_editions': number_editions}) for d in chain]
        return dict(tree)

    @staticmethod
    def chain(tree, edition_number):
        """

        :param tree: Tree history of all editions of a piece
        :param edition_number: The edition number to check for
        :return: The chain of ownsership of a particular edition of the piece ordered by time
        """
        # return the chain for an edition_number sorted by the timestamp
        return sorted(tree.get(edition_number, []), key=lambda d: d['timestamp_utc'])

    @staticmethod
    def strip_loan(chain):
        """
        Returns the chain without loan. This way we can look at the last transaction
        to establish ownership

        :param chain: chain for a particular edition
        :return: chain with loan transactions striped from the end of the chain
        """
        while chain[-1]['action'] == 'LOAN':
            chain.pop()

        return chain

    @staticmethod
    def pprint(tree):
        """
        Utility function to pretty print the history tree of a piece
        :param tree: History tree of a piece
        :return: None
        """
        p = PrettyPrinter(indent=2)
        p.pprint(tree)

    @staticmethod
    def decode_op_return(op_return_hex):
        """
        Decodes the op_return hex representation into a string
        :param op_return_hex: hex representation of the op_return
        :return: string representation of the op_return
        """
        return binascii.unhexlify(op_return_hex[4:])

    @staticmethod
    def check_script(vouts):
        """
        Looks into the vouts list of a transaction and returns the op_return if one exists
        :param vouts: lists of outputs of a transaction
        :return: string representation of the op_return
        """

        for vout in [v for v in vouts[::-1] if v['hex'].startswith('6a')]:
            verb = BlockchainSpider.decode_op_return(vout['hex'])
            action = Spoolverb.from_verb(verb).action
            if action in Spoolverb.supported_actions:
                return verb
        raise Exception("Invalid ascribe transaction")

    @staticmethod
    def _get_addresses(tx):
        """
        checks for the from, to, and piece address of a SPOOL transactions
        """
        from_address = set([vin['address'] for vin in tx['vins']])
        if len(from_address) != 1:
            raise InvalidTransactionError("Transaction should have inputs " \
                                          "from only one address {}".format(from_address))

        # order vouts. discard the last vout since its the op_return
        vouts = sorted(tx['vouts'], key=lambda d: d['n'])[:-1]
        piece_address = vouts[0]['address']
        to_address = vouts[-1]['address']
        from_address = from_address.pop()

        return from_address, to_address, piece_address

    @staticmethod
    def _get_time_utc(time_utc_str):
        """
        Convert a string representation of the time (as returned by blockr.io api) into unix
        timestamp

        :param time_utc_str: string representation of the time
        :return: unix timestamp
        """
        dt = datetime.strptime(time_utc_str, "%Y-%m-%dT%H:%M:%SZ")
        return int(calendar.timegm(dt.utctimetuple()))
Example #48
0
class Spool(object):
    FEE = 30000
    TOKEN = 3000

    """
    Class that contains all Spool methods.

    In the SPOOL implementation there is no notion of users only addresses.
    All addresses come from BIP32 HD wallets. This makes it easier to manage all the keys
    since we can retrieve everything we need from a master secret (namely the private key
    to sign the transactions).

    Since we are dealing with HD wallets we expect all from_address to be a tuple of (path, address)
    so that we can retrieve the private key for that particular leaf address.
    If we want to use the root address we can just pass an empty string to the first element of the
    tuple e.g. ('', address). For instance when using the federation wallet address we have no
    need to create leaf addresses.

    A file is represented by two hashes:
        - file_hash: is the hash of the digital file
        - file_hash_metadata: is the hash of the digital file + metadata
    The hash is passed to the methods has a tuple (file_hash, file_hash_metadata)
    """

    def __init__(self, testnet=False, service='blockr', username='', password='', host='', port=''):
        """

        :param testnet:
        :return:
        """
        self.testnet = testnet
        self._netcode = 'XTN' if testnet else 'BTC'
        self._t = Transactions(service=service, testnet=testnet, username=username,
                               password=password, host=host, port=port)
        # simple cache for spent outputs. Useful so that rapid firing transactions don't use the same outputs
        self._spents = Queue(maxsize=50)

    @dispatch
    def register_piece(self, from_address, to_address, hash, password, min_confirmations=6, sync=False, ownership=True):
        """
        Register a piece

        :param from_address: Federation address. All register transactions originate from the the Federation wallet
        :param to_address: Address registering the edition
        :param hash: Hash of the piece. Tuple (file_hash, file_hash_metadata)
        :param password: Federation wallet password. For signing the transaction
        :param edition_num: The number of the edition to register. User edition_num=0 to register the master edition
        :param min_confirmations: Override the number of confirmations when chosing the inputs of the transaction. Defaults to 6
        :param sync: Perform the transaction in synchronous mode, the call to the function will block until there is at
        least on confirmation on the blockchain. Defaults to False
        :param ownership: Check ownsership in the blockchain before pushing the transaction. Defaults to True
        :return: transaction id
        """
        file_hash, file_hash_metadata = hash
        path, from_address = from_address
        verb = Spoolverb()
        unsigned_tx = self.simple_spool_transaction(from_address,
                                                    [file_hash, file_hash_metadata, to_address],
                                                    op_return=verb.piece,
                                                    min_confirmations=min_confirmations)

        signed_tx = self._t.sign_transaction(unsigned_tx, password)
        txid = self._t.push(signed_tx)
        return txid

    @dispatch
    def register(self, from_address, to_address, hash, password, edition_num, min_confirmations=6, sync=False, ownership=True):
        """
        Register an edition or master edition of a piece

        :param from_address: Federation address. All register transactions originate from the the Federation wallet
        :param to_address: Address registering the edition
        :param hash: Hash of the piece. Tuple (file_hash, file_hash_metadata)
        :param password: Federation wallet password. For signing the transaction
        :param edition_num: The number of the edition to register. User edition_num=0 to register the master edition
        :param min_confirmations: Override the number of confirmations when chosing the inputs of the transaction. Defaults to 6
        :param sync: Perform the transaction in synchronous mode, the call to the function will block until there is at
        least on confirmation on the blockchain. Defaults to False
        :param ownership: Check ownsership in the blockchain before pushing the transaction. Defaults to True
        :return: transaction id
        """
        file_hash, file_hash_metadata = hash
        path, from_address = from_address
        verb = Spoolverb(edition_num=edition_num)
        unsigned_tx = self.simple_spool_transaction(from_address,
                                                    [file_hash, file_hash_metadata, to_address],
                                                    op_return=verb.register,
                                                    min_confirmations=min_confirmations)

        signed_tx = self._t.sign_transaction(unsigned_tx, password)
        txid = self._t.push(signed_tx)
        return txid

    @dispatch
    def consigned_registration(self, from_address, to_address, hash, password, min_confirmations=6, sync=False, ownership=True):
        """
        Register an edition or master edition of a piece consigned to from_address

        :param from_address: Federation address. All register transactions originate from the the Federation wallet
        :param to_address: Address registering the edition
        :param hash: Hash of the piece. Tuple (file_hash, file_hash_metadata)
        :param password: Federation wallet password. For signing the transaction
        :param min_confirmations: Override the number of confirmations when chosing the inputs of the transaction. Defaults to 6
        :param sync: Perform the transaction in synchronous mode, the call to the function will block until there is at
        least on confirmation on the blockchain. Defaults to False
        :param ownership: Check ownsership in the blockchain before pushing the transaction. Defaults to True
        :return: transaction id
        """
        file_hash, file_hash_metadata = hash
        path, from_address = from_address
        verb = Spoolverb()
        unsigned_tx = self.simple_spool_transaction(from_address,
                                                    [file_hash, file_hash_metadata, to_address],
                                                    op_return=verb.consigned_registration,
                                                    min_confirmations=min_confirmations)

        signed_tx = self._t.sign_transaction(unsigned_tx, password)
        txid = self._t.push(signed_tx)
        return txid

    @dispatch
    def editions(self, from_address, to_address, hash, password, num_editions, min_confirmations=6, sync=False, ownership=True):
        """
        Register the number of editions of a piece

        :param from_address: Federation address. All register transactions originate from the the Federation wallet
        :param to_address: Address registering the number of editions
        :param hash: Hash of the piece. Tuple (file_hash, file_hash_metadata)
        :param password:  Federation wallet password. For signing the transaction
        :param num_editions: Number of editions of the piece
        :param min_confirmations: Number of confirmations when chosing the inputs of the transaction. Defaults to 6
        :param sync: Perform the transaction in synchronous mode, the call to the function will block until there is at
        least on confirmation on the blockchain. Defaults to False
        :param ownership: Check ownsership in the blockchain before pushing the transaction. Defaults to True
        :return: transaction id
        """
        file_hash, file_hash_metadata = hash
        path, from_address = from_address
        verb = Spoolverb(num_editions=num_editions)
        unsigned_tx = self.simple_spool_transaction(from_address,
                                                    [file_hash, file_hash_metadata, to_address],
                                                    op_return=verb.editions,
                                                    min_confirmations=min_confirmations)

        signed_tx = self._t.sign_transaction(unsigned_tx, password)
        txid = self._t.push(signed_tx)
        return txid

    @dispatch
    def transfer(self, from_address, to_address, hash, password, edition_num, min_confirmations=6, sync=False, ownership=True):
        """
        Transfer a piece between addresses

        :param from_address: Address currently owning the edition
        :param to_address: Address to receive the edition
        :param hash: Hash of the piece. Tuple (file_hash, file_hash_metadata)
        :param password: Password for the wallet currently owning the edition. For signing the transaction
        :param edition_num: the number of the edition to transfer
        :param min_confirmations: Number of confirmations when chosing the inputs of the transaction. Defaults to 6
        :param sync: Perform the transaction in synchronous mode, the call to the function will block until there is at
        least on confirmation on the blockchain. Defaults to False
        :param ownership: Check ownsership in the blockchain before pushing the transaction. Defaults to True
        :return: transaction id
        """
        path, from_address = from_address
        file_hash, file_hash_metadata = hash
        verb = Spoolverb(edition_num=edition_num)
        unsigned_tx = self.simple_spool_transaction(from_address,
                                                    [file_hash, to_address],
                                                    op_return=verb.transfer,
                                                    min_confirmations=min_confirmations)
        signed_tx = self._t.sign_transaction(unsigned_tx, password, path=path)
        txid = self._t.push(signed_tx)
        return txid

    @dispatch
    def consign(self, from_address, to_address, hash, password, edition_num, min_confirmations=6, sync=False, ownership=True):
        """
        Consign a piece to an address

        :param from_address: Address currently owning the edition
        :param to_address: Address to where the piece will be consigned to
        :param hash: Hash of the piece. Tuple (file_hash, file_hash_metadata)
        :param password: Password for the wallet currently owning the edition. For signing the transaction
        :param edition_num: the number of the edition to consign
        :param min_confirmations: Number of confirmations when chosing the inputs of the transaction. Defaults to 6
        :param sync: Perform the transaction in synchronous mode, the call to the function will block until there is at
        least on confirmation on the blockchain. Defaults to False
        :param ownership: Check ownsership in the blockchain before pushing the transaction. Defaults to True
        :return: transaction id
        """
        path, from_address = from_address
        file_hash, file_hash_metadata = hash
        verb = Spoolverb(edition_num=edition_num)
        unsigned_tx = self.simple_spool_transaction(from_address,
                                                    [file_hash, to_address],
                                                    op_return=verb.consign,
                                                    min_confirmations=min_confirmations)
        signed_tx = self._t.sign_transaction(unsigned_tx, password, path=path)
        txid = self._t.push(signed_tx)
        return txid

    @dispatch
    def unconsign(self, from_address, to_address, hash, password, edition_num, min_confirmations=6, sync=False, ownership=True):
        """
        Unconsign the edition

        :param from_address: Address where the edition is currently consigned
        :param to_address: Address that consigned the piece to from_address
        :param hash: Hash of the piece. Tuple (file_hash, file_hash_metadata)
        :param password: Password for the wallet currently holding the edition. For signing the transaction
        :param edition_num: the number of the edition to unconsign
        :param min_confirmations: Number of confirmations when chosing the inputs of the transaction. Defaults to 6
        :param sync: Perform the transaction in synchronous mode, the call to the function will block until there is at
        least on confirmation on the blockchain. Defaults to False
        :param ownership: Check ownsership in the blockchain before pushing the transaction. Defaults to True
        :return: transaction id
        """
        # In an unconsignment the to_address needs to be the address that created the consign transaction
        path, from_address = from_address
        file_hash, file_hash_metadata = hash
        verb = Spoolverb(edition_num=edition_num)
        unsigned_tx = self.simple_spool_transaction(from_address,
                                                    [file_hash, to_address],
                                                    op_return=verb.unconsign,
                                                    min_confirmations=min_confirmations)
        signed_tx = self._t.sign_transaction(unsigned_tx, password, path=path)
        txid = self._t.push(signed_tx)
        return txid

    @dispatch
    def loan(self, from_address, to_address, hash, password, edition_num, loan_start, loan_end, min_confirmations=6, sync=False, ownership=True):
        """
        Loan the edition

        :param from_address: Address currently holding the edition
        :param to_address: Address to loan the edition to
        :param hash: Hash of the piece. Tuple (file_hash, file_hash_metadata)
        :param password: Password for the wallet currently holding the edition. For signing the transaction
        :param edition_num: the number of the edition to unconsign
        :param loan_start: Start date for the loan. In the form YYMMDD
        :param loan_end: End date for the loan. In the form YYMMDD
        :param min_confirmations: Number of confirmations when chosing the inputs of the transaction. Defaults to 6
        :param sync: Perform the transaction in synchronous mode, the call to the function will block until there is at
        least on confirmation on the blockchain. Defaults to False
        :param ownership: Check ownsership in the blockchain before pushing the transaction. Defaults to True
        :return: transaction id
        """
        path, from_address = from_address
        file_hash, file_hash_metadata = hash
        verb = Spoolverb(edition_num=edition_num, loan_start=loan_start, loan_end=loan_end)
        unsigned_tx = self.simple_spool_transaction(from_address,
                                                    [file_hash, to_address],
                                                    op_return=verb.loan,
                                                    min_confirmations=min_confirmations)
        signed_tx = self._t.sign_transaction(unsigned_tx, password, path=path)
        txid = self._t.push(signed_tx)
        return txid

    @dispatch
    def migrate(self, from_address, prev_address, new_address, hash, password, edition_num, min_confirmations=6, sync=False, ownership=True):
        """
        Migrate an edition

        :param from_address: Federation address. All register transactions originate from the the Federation wallet
        :param to_address: Address registering the edition
        :param hash: Hash of the piece. Tuple (file_hash, file_hash_metadata)
        :param password: Federation wallet password. For signing the transaction
        :param edition_num: The number of the edition to register. User edition_num=0 to register the master edition
        :param min_confirmations: Override the number of confirmations when chosing the inputs of the transaction. Defaults to 6
        :param sync: Perform the transaction in synchronous mode, the call to the function will block until there is at
        least on confirmation on the blockchain. Defaults to False
        :param ownership: Check ownsership in the blockchain before pushing the transaction. Defaults to True
        :return: transaction id
        """
        file_hash, file_hash_metadata = hash
        path, from_address = from_address
        verb = Spoolverb(edition_num=edition_num)
        unsigned_tx = self.simple_spool_transaction(from_address,
                                                    [file_hash, prev_address, new_address],
                                                    op_return=verb.migrate,
                                                    min_confirmations=min_confirmations)

        signed_tx = self._t.sign_transaction(unsigned_tx, password)
        txid = self._t.push(signed_tx)
        return txid

    @dispatch
    def refill_main_wallet(self, from_address, to_address, nfees, ntokens, password, min_confirmations=6, sync=False):
        """
        Refill the Federation wallet with tokens and fees. This keeps the federation wallet clean.
        Dealing with exact values simplifies the transactions. No need to calculate change. Easier to keep track of the
        unspents and prevent double spends that would result in transactions being rejected by the bitcoin network.

        :param from_address: Refill wallet address. Refills the federation wallet with tokens and fees
        :param to_address: Federation wallet address
        :param nfees: Number of fees to transfer. Each fee is 10000 satoshi. Used to pay for the transactions
        :param ntokens: Number of tokens to transfer. Each token is 600 satoshi. Used to register hashes in the blockchain
        :param password: Password for the Refill wallet. Used to sign the transaction
        :param min_confirmations: Number of confirmations when chosing the inputs of the transaction. Defaults to 6
        :param sync: Perform the transaction in synchronous mode, the call to the function will block until there is at
        least on confirmation on the blockchain. Defaults to False
        :return: transaction id
        """
        path, from_address = from_address
        unsigned_tx = self._t.simple_transaction(from_address,
                                                 [(to_address, self.FEE)] * nfees + [(to_address, self.TOKEN)] * ntokens,
                                                 min_confirmations=min_confirmations)

        signed_tx = self._t.sign_transaction(unsigned_tx, password)
        txid = self._t.push(signed_tx)
        return txid

    @dispatch
    def refill(self, from_address, to_address, nfees, ntokens, password, min_confirmations=6, sync=False):
        """
        Refill wallets with the necessary fuel to perform spool transactions

        :param from_address: Federation wallet address. Fuels the wallets with tokens and fees. All transactions to wallets
                holding a particular piece should come from the Federation wallet
        :param to_address: Wallet address that needs to perform a spool transaction
        :param nfees: Number of fees to transfer. Each fee is 10000 satoshi. Used to pay for the transactions
        :param ntokens: Number of tokens to transfer. Each token is 600 satoshi. Used to register hashes in the blockchain
        :param password: Password for the Federation wallet. Used to sign the transaction
        :param min_confirmations: Number of confirmations when chosing the inputs of the transaction. Defaults to 6
        :param sync: Perform the transaction in synchronous mode, the call to the function will block until there is at
        least on confirmation on the blockchain. Defaults to False
        :return: transaction id
        """
        path, from_address = from_address
        verb = Spoolverb()
        # nfees + 1: nfees to refill plus one fee for the refill transaction itself
        inputs = self.select_inputs(from_address, nfees + 1, ntokens, min_confirmations=min_confirmations)
        outputs = [{'address': to_address, 'value': self.TOKEN}] * ntokens
        outputs += [{'address': to_address, 'value': self.FEE}] * nfees
        outputs += [{'script': self._t._op_return_hex(verb.fuel), 'value': 0}]
        unsigned_tx = self._t.build_transaction(inputs, outputs)
        signed_tx = self._t.sign_transaction(unsigned_tx, password, path=path)
        txid = self._t.push(signed_tx)
        return txid

    def simple_spool_transaction(self, from_address, to, op_return, min_confirmations=6):
        """
        Utililty function to create the spool transactions. Selects the inputs, encodes the op_return and
        constructs the transaction.

        :param from_address: Address originating the the transaction
        :param to: list of addresses to receive tokens (file_hash, file_hash_metadata, ...)
        :param op_return: String representation of the spoolverb, as returned by the properties of Spoolverb
        :param min_confirmations: Number of confirmations when chosing the inputs of the transaction. Defaults to 6
        :return: unsigned transaction
        """
        # list of addresses to send
        ntokens = len(to)
        nfees = self._t.estimate_fee(ntokens, 2) / self.FEE
        inputs = self.select_inputs(from_address, nfees, ntokens, min_confirmations=min_confirmations)
        # outputs
        outputs = [{'address': to_address, 'value': self.TOKEN} for to_address in to]
        outputs += [{'script': self._t._op_return_hex(op_return), 'value': 0}]
        # build transaction
        unsigned_tx = self._t.build_transaction(inputs, outputs)
        return unsigned_tx

    def select_inputs(self, address, nfees, ntokens, min_confirmations=6):
        # selects the inputs for the spool transaction
        unspents = self._t.get(address, min_confirmations=min_confirmations)['unspents']
        unspents = filter(lambda d: d not in self._spents.queue, unspents)
        if len(unspents) == 0:
            raise Exception("No spendable outputs found")

        fees = filter(lambda d: d['amount'] == self.FEE, unspents)[:nfees]
        tokens = filter(lambda d: d['amount'] == self.TOKEN, unspents)[:ntokens]
        if len(fees) != nfees or len(tokens) != ntokens:
            raise SpoolFundsError("Not enough outputs to spend. Refill your wallet")
        if self._spents.qsize() > 50 - (nfees + ntokens):
            [self._spents.get() for i in range(self._spents.qsize() + nfees + ntokens - 50)]
        [self._spents.put(fee) for fee in fees]
        [self._spents.put(token) for token in tokens]
        return fees + tokens
Example #49
0
def test_get_block_info_with_blockr(block_hash):
    from transactions import Transactions

    transactions = Transactions(testnet=True)
    block_data = transactions.get_block_info(block_hash)
    assert block_data
Example #50
0
 def test_that_rates_work(self):
     trans = Transactions(Rates(TEST_RATES), TEST_TRANSACTIONS)
     t = trans.total(sku = "DM1182", currency = "USD")
     self.assertEqual(t, Decimal("134.228"))
Example #51
0
    def make_rows(self):
        import dateutil
        def get_increments(ledger, account):
            ld = ledger[ledger.idx].get_date(posting=ledger_account_name, return_string=False) if ledger.idx < len(ledger) else dateutil.parser.parse("2099/01/01")
            ad = account[account.idx].get_date(posting=ledger_account_name, return_string=False) if account.idx < len(account) else dateutil.parser.parse("2099/01/01")
                
            lbump = abump = False
            if ledger.idx == len(ledger):
                lbump = True
            if account.idx == len(account):
                abump = True
            if ledger.idx < len(ledger) and account.idx < len(account) and self.total(ledger[ledger.idx])==self.total(account[account.idx]):
                abump = lbump = True
            if ld <= ad:
                lbump = True
            if ld >= ad:
                abump = True
            ledger.bump = False
            if ledger.idx < len(ledger): ledger.bump = lbump
            account.bump = False
            if account.idx < len(account): account.bump = abump
        uncleared = Transactions()
        ret = ""
        ledger_account_name = c['banks'][self.account.bank_name]['accounts'][self.account.name]['ledger-account'].lower()
        while self.ledger.idx < len(self.ledger) or self.account.idx < len(self.account):
            if self.ledger.idx < len(self.ledger):
                if (self.ledger[self.ledger.idx]['state'] != "cleared" and 
                    not "cleared" in [p['state'] for p in self.ledger[self.ledger.idx]['postings'] if p['account_name'].lower().startswith(ledger_account_name)]):
                   uncleared.append(self.ledger[self.ledger.idx])
                   self.ledger.idx += 1
                   continue
            get_increments(self.ledger, self.account)

            if self.ledger.bump:
                self.ledger.total.append(self.ledger.total[-1] + self.total(self.ledger[self.ledger.idx]))
            if self.account.bump:
                self.account.total.append(self.account.total[-1] + self.total(self.account[self.account.idx]))

            if True:
                # make table row
                line = "<tr><td align='right'>"
                if self.ledger.bump: line += '{0}<br />'
                line += "</td><td>"
                if self.account.bump: line += '{1}<br />'
                line += "</td></tr>"
                ret += line.format(self.ledger[self.ledger.idx] if self.ledger.idx<len(self.ledger) else "", 
                                   self.account[self.account.idx] if self.account.idx < len(self.account) else "")
                if self.ledger.total[-1] == self.account.total[-1]:
                    ret += ("<tr><td align='right'><font color='green'>{0}</font><br /></td><td><font color='green'>{1}</font></td></tr>".
                            format(self.ledger.total[-1], self.account.total[-1]))
                else:
                    if self.ledger.bump == self.account.bump == True:
                        ret += ("<tr><td align='right'><font color='blue'>{0}</font><br /></td><td><font color='blue'>{1}</font></td></tr>".
                                format(self.ledger.total[-1], self.account.total[-1]))
                    else:
                        ret += "<tr><td align='right'>{0}<br /></td><td>{1}</td></tr>".format(self.ledger.total[-1], self.account.total[-1])

            if self.ledger.bump: self.ledger.idx += 1
            if self.account.bump: self.account.idx += 1

            # TODO: print the uncleared transactions in an intelligent way at the end.
            #if len(self.ledger) >= self.ledger.idx and len(self.account) >= self.account.idx:
            #    if len(uncleared) > 0:
            #        for tx in uncleared:
            #            print "<tr><td align='right'>{0}</td></tr>".format(tx)
        return ret
Example #52
0
def searchByItemName():
    transactions = Transactions()

    return transactions.searchByItemName(retrieveRequestFor('itemName', request))
Example #53
0
def cook():
    transactions = Transactions()

    return transactions.cook(retrieveRequestFor('ingredients', request).split(","))
def test_transaction_creation_via_create():
    from transactions import Transactions
    trxs = Transactions(testnet=True)
    assert trxs.testnet is True
    simple_transaction = trxs.create(alice, (bob, 6))
    assert simple_transaction
Example #55
0
class TestSpool(unittest.TestCase):

    def setUp(self):
        try:
            # flag to run the tests
            test = os.environ['TEST_SPOOL']
            if test == '2':
                username = os.environ['TESTNET_USERNAME']
                password = os.environ['TESTNET_PASSWORD']
                host = os.environ['TESTNET_HOST']
                port = os.environ['TESTNET_PORT']
            self.refill_pass = os.environ['TEST_REFILL_PASS']
            self.federation_pass = os.environ['TEST_FEDERATION_PASS']
            self.refill_root = Wallet(self.refill_pass, testnet=True).root_address
            self.federation_root = Wallet(self.federation_pass, testnet=True).root_address
        except KeyError:
            raise unittest.SkipTest('TEST_REFILL_PASS and/or TEST_FEDERATION_PASS environment variables are not set.')

        # set TEST_SPOOL=2 to test with bitcoind
        if test == '2':
            print 'using bitcoind'
            self.t = Transactions(testnet=True, service='daemon', username=username, password=password, host=host, port=port)
            self.spool = Spool(testnet=True, service='daemon', username=username, password=password, host=host, port=port)
        else:
            print 'using blockr'
            self.t = Transactions(testnet=True)
            self.spool = Spool(testnet=True)

        self.user1_pass = self._get_pass()
        self.user2_pass = self._get_pass()
        self.user3_pass = self._get_pass()
        self.user1_root = Wallet(self.user1_pass, testnet=True).root_address
        self.user1_leaf = Wallet(self.user1_pass, testnet=True).address_from_path()
        self.user2_leaf = Wallet(self.user2_pass, testnet=True).address_from_path()
        self.user3_leaf = Wallet(self.user3_pass, testnet=True).address_from_path()
        self.file_hash = self._get_file_hash()

        print 'user1_pass: '******'user2_pass: '******'user3_pass: '******'user1_root: ', self.user1_root
        print 'user1_leaf: ', self.user1_leaf
        print 'user2_leaf: ', self.user2_leaf
        print 'user3_leaf: ', self.user3_leaf
        print 'file_hash :', self.file_hash

        self.spool._t.import_address(self.user1_root[1], "test",)
        self.spool._t.import_address(self.user1_leaf[1], "test",)
        self.spool._t.import_address(self.user2_leaf[1], "test",)
        self.spool._t.import_address(self.user3_leaf[1], "test",)
        self.spool._t.import_address(self.file_hash[0], "test",)
        self.spool._t.import_address(self.file_hash[1], "test",)


    def test_spool(self):
        # 1. Refill Federation wallet with necessary fuel and tokens
        print
        print 'Refilling Federation wallet with necessary fuel and tokens'
        txid = self.spool.refill_main_wallet(self.refill_root, self.federation_root[1], 7, 11, self.refill_pass,
                                             min_confirmations=1, sync=True)
        print txid

        # 2. user1 registers master edition
        print
        print 'user1 registers master edition'
        txid = self.spool.register(self.federation_root, self.user1_root[1], self.file_hash,
                                   self.federation_pass, 0, min_confirmations=1, sync=True)
        print txid

        tx = self.t.get(txid)
        verb = BlockchainSpider.check_script(tx['vouts'])
        self.assertEqual(verb, 'ASCRIBESPOOL01REGISTER0')

        # 3. user1 registers number of editions
        print
        print 'user1 registers number of editions'
        txid = self.spool.editions(self.federation_root, self.user1_root[1], self.file_hash,
                                   self.federation_pass, 10, min_confirmations=1, sync=True)
        print txid

        tx = self.t.get(txid)
        verb = BlockchainSpider.check_script(tx['vouts'])
        self.assertEqual(verb, 'ASCRIBESPOOL01EDITIONS10')

        # 4. user1 registers edition number 1
        print
        print 'user1 registers edition number 1'
        txid = self.spool.register(self.federation_root, self.user1_leaf[1], self.file_hash,
                                   self.federation_pass, 1, min_confirmations=1, sync=True)
        print txid

        tx = self.t.get(txid)
        verb = BlockchainSpider.check_script(tx['vouts'])
        self.assertEqual(verb, 'ASCRIBESPOOL01REGISTER1')

        # 5. Refill user1 wallet before transfer
        print
        print 'Refill user1 wallet before transfer'
        txid = self.spool.refill(self.federation_root, self.user1_leaf[1], 1, 1,
                                 self.federation_pass, min_confirmations=1, sync=True)
        print txid

        # 5. User1 transfers edition number 1 to user2
        print
        print 'User1 transfers edition number 1 to user 2'
        txid = self.spool.transfer(self.user1_leaf, self.user2_leaf[1], self.file_hash,
                                   self.user1_pass, 1, min_confirmations=1, sync=True)
        print txid

        tx = self.t.get(txid)
        verb = BlockchainSpider.check_script(tx['vouts'])
        self.assertEqual(verb, 'ASCRIBESPOOL01TRANSFER1')

        # 6. Refill user2 wallet before consign
        print
        print 'Refill user2 wallet before consign'
        txid = self.spool.refill(self.federation_root, self.user2_leaf[1], 1, 1,
                                 self.federation_pass, min_confirmations=1, sync=True)
        print txid

        # 6. user2 consigns edition number 1 to user3
        print
        print 'user2 consigns edition number 1 to user3'
        txid = self.spool.consign(self.user2_leaf, self.user3_leaf[1], self.file_hash,
                                  self.user2_pass, 1, min_confirmations=1, sync=True)
        print txid

        tx = self.t.get(txid)
        verb = BlockchainSpider.check_script(tx['vouts'])
        self.assertEqual(verb, 'ASCRIBESPOOL01CONSIGN1')

        # 7. Refill user3 wallet before unconsign
        print
        print 'Refill user3 wallet before unconsign'
        txid = self.spool.refill(self.federation_root, self.user3_leaf[1], 1, 1,
                                 self.federation_pass, min_confirmations=1, sync=True)
        print txid

        # 7. user3 unconsigns edition number 1 to user2
        print
        print 'user3 unconsigns edition number 1 back to user2'
        txid = self.spool.unconsign(self.user3_leaf, self.user2_leaf[1], self.file_hash,
                                    self.user3_pass, 1, min_confirmations=1, sync=True)
        print txid

        tx = self.t.get(txid)
        verb = BlockchainSpider.check_script(tx['vouts'])
        self.assertEqual(verb, 'ASCRIBESPOOL01UNCONSIGN1')

        # 8. Refill user2 wallet before loan
        print
        print 'Refill user2 wallet before loan'
        txid = self.spool.refill(self.federation_root, self.user2_leaf[1], 1, 1,
                                 self.federation_pass, min_confirmations=1, sync=True)
        print txid

        # 8. user2 loans edition number 1 to user3
        print
        print 'user2 loans edition number 1 to user3'
        txid = self.spool.loan(self.user2_leaf, self.user3_leaf[1], self.file_hash,
                               self.user2_pass, 1, '150522', '150523', min_confirmations=1, sync=True)
        print txid

        tx = self.t.get(txid)
        verb = BlockchainSpider.check_script(tx['vouts'])
        self.assertEqual(verb, 'ASCRIBESPOOL01LOAN1/150522150523')

    def _get_pass(self):
        return ''.join([random.choice(ascii_letters) for i in xrange(10)])

    def _get_file_hash(self):
        title = ''.join([random.choice(ascii_letters) for i in xrange(10)])
        with open('/tmp/test', 'w') as f:
            f.write(random._urandom(100))

        f = File('/tmp/test', testnet=True, title=title)
        return f.file_hash, f.file_hash_metadata