Example #1
0
    def __init__(self, private={}, public={}, private_seeds={}):
        # It is possible to distinguish between private and public seeds
        # based on the string content.  Consider modifying this function
        # to take merely one dict of seeds.  Trees should still be stored
        # separately.
        self.trees = {}
        self.private_trees = {}
        self.public_trees = {}

        def treegen(value, entropy=False):
            if entropy:
                # this method also takes a netcode parameter, but we don't care
                # what network pycoin thinks this node is, because we only use it
                # for key derivation.
                return BIP32Node.from_master_secret(unhexlify(value))
            else:
                # this method will infer a network from the header bytes. We
                # don't care right now for the same reason as above, but we will
                # if Gem's API stops returning 'xpub' as the pubkey header bytes
                # because if pycoin doesn't recognize a header it will error.
                return BIP32Node.from_hwif(value)

        for name, seed in iteritems(private):
            tree = treegen(seed)
            self.private_trees[name] = self.trees[name] = tree

        for name, seed in iteritems(private_seeds):
            tree = treegen(seed, True)
            self.private_trees[name] = self.trees[name] = tree

        for name, seed in iteritems(public):
            tree = BIP32Node.from_hwif(seed)
            self.public_trees[name] = self.trees[name] = tree
 def bip32node_for_slug(self, slug):
     c = self._exec_sql("select id, as_text from BIP32Key where slug=?", slug)
     r = c.fetchone()
     if r is None:
         return None
     bip32_node = BIP32Node.from_hwif(r[1])
     bip32_node.id = r[0]
     return bip32_node
Example #3
0
 def create_new_address(self):
     n = n_addresses.incr()
     bip32node = BIP32Node.from_hwif(xpub.get())
     subkey = bip32node.subkey(0).subkey(n)  # match electrum path
     new_address = subkey.address()
     addr_to_uid[new_address] = self.uid
     uid_to_addr[self.uid] = new_address
     all_addresses.add(new_address)
     return True
Example #4
0
 def treegen(value, entropy=False):
     if entropy:
         # this method also takes a netcode parameter, but we don't care
         # what network pycoin thinks this node is, because we only use it
         # for key derivation.
         return BIP32Node.from_master_secret(unhexlify(value))
     else:
         # this method will infer a network from the header bytes. We
         # don't care right now for the same reason as above, but we will
         # if Gem's API stops returning 'xpub' as the pubkey header bytes
         # because if pycoin doesn't recognize a header it will error.
         return BIP32Node.from_hwif(value)
 def evaluate_key_input(self, txt):
     self.ext_key_widget.clear()
     self.subkey_widget.clear()
     txt = str(txt)
     if not txt:
         return self.invalid_key_label.setVisible(False)
     # Variable substitution.
     elif txt.startswith('$'):
         return
     try:
         key = BIP32Node.from_hwif(txt)
     except Exception as e:
         self.invalid_key_label.setVisible(True)
     else:
         self.invalid_key_label.setVisible(False)
         self.ext_key_widget.set_key(key)
         self.derive_child()
     finally:
         self.ext_key_widget.mapper.setCurrentIndex(0)
Example #6
0
    def createTransaction(self, address, amount, keychain, fee="standard",
                          confirms=0):
        unspents_result = yield self.unspents()
        spendables = []
        p2sh = []
        chain_paths = []
        # Strip leading /
        keychain_path = keychain['path'][1:]
        for unspent in unspents_result["unspents"]:
            if unspent["confirmations"] < confirms:
                continue
            p2sh.append(h2b(unspent["redeemScript"]))
            spendable = Spendable(unspent["value"],
                                  h2b(unspent["script"]),
                                  h2b_rev(unspent["tx_hash"]),
                                  unspent["tx_output_n"])
            spendables.append(spendable)
            chain_paths.append(keychain_path + unspent['chainPath'])
        p2sh_lookup = build_p2sh_lookup(p2sh)
        address_result = yield self.createAddress(1)
        change = address_result["address"]
        tx = tx_utils.create_tx(spendables, [(address, amount), change], fee)

        # address_keys = [BIP32Node.from_hwif(keychain["xprv"]).subkey_for_path("0/0/0/0"),
        #                 BIP32Node.from_hwif(keychain["xprv"]).subkey_for_path(address_result['path'])]

        spendable_keys = [BIP32Node.from_hwif(keychain["xprv"]).subkey_for_path(path) for path in chain_paths]
        # all_keys = address_keys + spendable_keys

        hash160_lookup = build_hash160_lookup([key.secret_exponent() for key in spendable_keys])

        pprint(tx)

        tx.sign(hash160_lookup=hash160_lookup, p2sh_lookup=p2sh_lookup)

        pprint(tx)

        returnValue({'tx': tx.as_hex(),
                     'fee': tx.fee()})
    def derive_child(self):
        strkey = str(self.key_edit.text())
        subkey_path = str(self.subkey_path.text())
        subkey_path = subkey_path.replace("'", "H").replace("h", "H")

        # Don't clear if the user is typing a path.
        if subkey_path.endswith('/'):
            return

        self.subkey_widget.clear()
        if not strkey or not subkey_path:
            return

        try:
            ext_key = BIP32Node.from_hwif(strkey)
            result = ext_key.subkey_for_path(subkey_path)
        except Exception as e:
            self.invalid_subkey_label.setText(str(e))
            self.invalid_subkey_label.setVisible(True)
        else:
            self.invalid_subkey_label.setVisible(False)
            self.subkey_widget.set_key(result)
        finally:
            self.subkey_widget.mapper.setCurrentIndex(0)
Example #8
0
def main():
    if len(sys.argv) != 2:
        print("usage: %s bip32_key_file" % sys.argv[0])
        sys.exit(-1)
    with open(sys.argv[1], "r") as f:
        hwif = f.readline().strip()

    # turn the bip32 text into a BIP32Node object
    BIP32_KEY = BIP32Node.from_hwif(hwif)

    # create three sec_keys (these are public keys, streamed using the SEC format)

    SEC_0 = BIP32_KEY.subkey_for_path("0/0/0").sec()
    SEC_1 = BIP32_KEY.subkey_for_path("0/1/0").sec()
    SEC_2 = BIP32_KEY.subkey_for_path("0/2/0").sec()

    public_key_sec_list = [SEC_0, SEC_1, SEC_2]

    # create the 2-of-3 multisig script
    # any 2 signatures can release the funds
    pay_to_multisig_script = ScriptMultisig(2, public_key_sec_list).script()

    # create a "2-of-3" multisig address_for_multisig
    the_address = address_for_pay_to_script(pay_to_multisig_script)

    print("Here is your pay 2-of-3 address: %s" % the_address)

    print("Here is the pay 2-of-3 script: %s" % b2h(pay_to_multisig_script))
    print("The hex script should go into p2sh_lookup.hex")

    base_dir = os.path.dirname(sys.argv[1])
    print("The three WIFs are written into %s as wif0, wif1 and wif2" % base_dir)
    for i in range(3):
        wif = BIP32_KEY.subkey_for_path("0/%d/0" % i).wif()
        with open(os.path.join(base_dir, "wif%d" % i), "w") as f:
            f.write(wif)
Example #9
0
    def handle(self, *args, **options):

        # Maintain global metadata about the orders being settled
        global_settled_order_count = 0
        now = datetime.datetime.now(datetime.timezone.utc)

        # Add a unique id allowing us to trace through this pass at settling
        identifiers = {
            'trace_id': reporting.utils.generate_trace_id(),
        }

        errors = []

        # @TODO: grab a lock to ensure only one process can settle at a time
        # https://github.com/ .. /issues/134

        with connection.cursor() as cursor:
            cursor.execute('DROP TABLE IF EXISTS settle_temptrades')
            cursor.execute('''
                CREATE TABLE settle_temptrades (
                    id BIGSERIAL PRIMARY KEY NOT NULL,
                    currency CHARACTER VARYING(16),
                    volume BIGINT,
                    fee BIGINT,
                    wallet_id UUID REFERENCES wallet_wallet (id),
                    trade_id BIGINT REFERENCES trade_trade (id),
                    side BOOLEAN
                );''')

            # Step 1:
            # Loop through each coin type, settle money out:
            for coin_type in settings.COINS.keys():
                print("Settling %s (%s)..." %
                      (settings.COINS[coin_type]['name'], coin_type))
                coin_details = {
                    'type': coin_type,
                    'name': settings.COINS[coin_type]['name'],
                }
                reporting.utils.audit(message="initiating settling",
                                      details={
                                          'identifiers': identifiers,
                                          'coin_details': coin_details,
                                      })

                # Start by finding trades where the base currency for the sell
                # order side has an appropriate base currency.
                # @TODO: do we need to review sell_order_settled_in trades too?
                trades_out_sells = trade.models.Trade.objects.filter(sell_order__base_currency=coin_type) \
                    .filter(models.Q(sell_order_settled_out=trade.models.SETTLED_NONE) | models.Q(sell_order_settled_out=trade.models.SETTLED_VALID)) \
                    .order_by('created')

                for unsettled_trade in trades_out_sells:
                    global_settled_order_count += 1
                    from_user = unsettled_trade.sell_order.wallet.user.get()
                    order_fee = order.utils.get_fee(
                        order=unsettled_trade.sell_order,
                        volume=unsettled_trade.base_volume)

                    # Collect the temporary data we build for settling, and
                    # write it to the audit logs.
                    to_settle = []

                    # Record the money-out (sell) from this trade
                    new_settle = TempTrades(
                        currency=coin_type,
                        volume=unsettled_trade.base_volume * -1,
                        fee=0,  # no fee for money-out
                        side=False,  # sell order
                        wallet=unsettled_trade.sell_order.wallet,
                        trade=unsettled_trade,
                    )
                    new_settle.save()
                    to_settle.append(model_to_dict(new_settle))

                    # Record the money-in (buy) from this trade
                    to_user = unsettled_trade.buy_order.wallet.user.get()
                    money_in_wallet = wallet.models.Wallet.objects.get(
                        user=to_user,
                        currencycode=coin_type,
                    )
                    new_settle = TempTrades(
                        currency=coin_type,
                        volume=unsettled_trade.base_volume - order_fee,
                        fee=order_fee,
                        side=True,  # buy order
                        wallet=money_in_wallet,
                        trade=unsettled_trade,
                    )
                    new_settle.save()
                    to_settle.append(model_to_dict(new_settle))

                    # Be sure order and trade have sane timestamps
                    errors = self.validate_timestamps(
                        identifiers=identifiers,
                        now=now,
                        unsettled_trade=unsettled_trade,
                        errors=errors)

                    from_details = {
                        'user_id': from_user.id,
                        'wallet_id': unsettled_trade.sell_order.wallet.id,
                        'order_id': unsettled_trade.sell_order.id,
                        'trade_id': unsettled_trade.id,
                        'order_type': 'sell',
                        'cryptopair': unsettled_trade.cryptopair,
                        'pair_side': 'base',
                        'volume': unsettled_trade.base_volume,
                        'fee': order_fee,
                        'order_created': unsettled_trade.sell_order.created,
                        'trade_created': unsettled_trade.created,
                        'now': now,
                    }
                    to_details = {
                        'user_id': to_user.id,
                        'wallet_id': unsettled_trade.buy_order.wallet.id,
                        'order_id': unsettled_trade.buy_order.id,
                        'trade_id': unsettled_trade.id,
                        'order_type': 'buy',
                        'cryptopair': unsettled_trade.cryptopair,
                        'pair_side': 'base',
                        'volume': unsettled_trade.base_volume - order_fee,
                        'order_created': unsettled_trade.buy_order.created,
                        'trade_created': unsettled_trade.created,
                        'now': now,
                    }

                    reporting.utils.audit(message="settling details",
                                          details={
                                              'identifiers': identifiers,
                                              'coin_details': coin_details,
                                              'from_details': from_details,
                                              'to_details': to_details,
                                              'to_settle': to_settle,
                                              'errors': errors,
                                          })

                # Next, find trades where the base currency for the buy order side
                # has an appropriate quote currency.
                # @TODO do we need to review buy_order_settled_in trades too?
                trades_out_buys = trade.models.Trade.objects.filter(buy_order__quote_currency=coin_type) \
                    .filter(models.Q(buy_order_settled_out=trade.models.SETTLED_NONE) | models.Q(buy_order_settled_out=trade.models.SETTLED_VALID)) \
                    .order_by('created')

                for unsettled_trade in trades_out_buys:
                    global_settled_order_count += 1
                    from_user = unsettled_trade.buy_order.wallet.user.get()
                    order_fee = order.utils.get_fee(
                        order=unsettled_trade.buy_order,
                        volume=unsettled_trade.volume,
                    )

                    # Collect the temporary data we build for settling, and
                    # write it to the audit logs.
                    to_settle = []

                    # Record the money-out (buy) from this trade
                    new_settle = TempTrades(
                        currency=coin_type,
                        volume=unsettled_trade.volume * -1,
                        fee=0,  # no fee for money-out
                        side=True,  # buy order
                        wallet=unsettled_trade.buy_order.wallet,
                        trade=unsettled_trade,
                    )
                    new_settle.save()
                    to_settle.append(model_to_dict(new_settle))

                    # Record the money-in (sell) from this trade
                    to_user = unsettled_trade.sell_order.wallet.user.get()
                    money_in_wallet = wallet.models.Wallet.objects.get(
                        user=to_user,
                        currencycode=coin_type,
                    )
                    new_settle = TempTrades(
                        currency=coin_type,
                        volume=unsettled_trade.volume - order_fee,
                        fee=order_fee,
                        side=False,  # sell order
                        wallet=money_in_wallet,
                        trade=unsettled_trade,
                    )
                    new_settle.save()
                    to_settle.append(model_to_dict(new_settle))

                    # Be sure order and trade have sane timestamps
                    errors = self.validate_timestamps(
                        identifiers=identifiers,
                        now=now,
                        unsettled_trade=unsettled_trade,
                        errors=errors)

                    from_details = {
                        'user_id': from_user.id,
                        'wallet_id': unsettled_trade.buy_order.wallet.id,
                        'order_id': unsettled_trade.buy_order.id,
                        'trade_id': unsettled_trade.id,
                        'order_type': 'buy',
                        'cryptopair': unsettled_trade.cryptopair,
                        'pair_side': 'quote',
                        'volume': unsettled_trade.volume,
                        'fee': order_fee,
                        'order_created': unsettled_trade.buy_order.created,
                        'trade_created': unsettled_trade.created,
                        'now': now,
                    }
                    to_details = {
                        'user_id': to_user.id,
                        'wallet_id': unsettled_trade.sell_order.wallet.id,
                        'order_id': unsettled_trade.sell_order.id,
                        'trade_id': unsettled_trade.id,
                        'order_type': 'sell',
                        'pair_side': 'quote',
                        'volume': unsettled_trade.volume - order_fee,
                    }
                    reporting.utils.audit(message="settling details",
                                          details={
                                              'identifiers': identifiers,
                                              'coin_details': coin_details,
                                              'from_details': from_details,
                                              'to_details': to_details,
                                              'to_settle': to_settle,
                                              'errors': errors,
                                          })

                tmp_trades = TempTrades.objects.values('wallet') \
                    .filter(currency=coin_type) \
                    .annotate(total_volume=models.Sum('volume'), total_fee=models.Sum('fee'))

                for tmp_trade in tmp_trades:
                    user_wallet = wallet.models.Wallet.objects.get(
                        id=tmp_trade['wallet'])
                    balances = wallet.utils.get_balances(
                        identifiers=identifiers,
                        user_wallet=user_wallet,
                    )
                    # Confirm there's sufficient funds for this trade.
                    if tmp_trade['total_volume'] < 0:
                        balance_in = 0
                        balance_out = tmp_trade['total_volume'] * -1
                        #print("validating transfer of {} {} out of wallet {}".format(balance_out, user_wallet.currencycode, user_wallet.id))
                        if balances['blockchain'] < balance_out:
                            trades = self.get_trades_from_wallet(
                                user_wallet=user_wallet)
                            for error_trade in trades:
                                self.mark_as_error(
                                    identifiers=identifiers,
                                    unsettled_trade=error_trade.trade,
                                    order_side=error_trade.side,
                                )
                            errors.append(
                                "%d balance insufficient for %d trades" %
                                (balances['blockchain'], balance_out))
                            print("ERROR: insufficient funds")
                        #if tmp_trade['total_fee'] > 0:
                        #print("validating transfer of {} {} fee to exchange".format(tmp_trade['total_fee'], user_wallet.currencycode))
                    else:
                        balance_out = 0
                        balance_in = tmp_trade['total_volume']
                        #print("validating transfer of {} {} into wallet {}".format(balance_in, user_wallet.currencycode, user_wallet.id))
                        #print("validating transfer of {} {} fee to exchange".format(tmp_trade['total_fee'], user_wallet.currencycode))

                    coin_details = {
                        'type': user_wallet.currencycode,
                        'name':
                        settings.COINS[user_wallet.currencycode]['name'],
                    }
                    reporting.utils.audit(
                        message="settling balance confirmation",
                        details={
                            'identifiers': identifiers,
                            'coin_details': coin_details,
                            'aggregate_settle': tmp_trade,
                            'funds_out': {
                                'currency_code': user_wallet.currencycode,
                                'volume': balance_out,
                            },
                            'funds_in': {
                                'currency_code': user_wallet.currencycode,
                                'volume': balance_in,
                            },
                            'balances': balances,
                            'errors': errors,
                        })

            # Auditing completed, mark trades as pending indicating we're ready
            # to actually settle. Any trades that have previously been marked
            # with an error will stay as an error.
            all_trades_to_settle = TempTrades.objects.filter()
            for trade_to_settle in all_trades_to_settle:
                # This only succeeds if the trade doesn't have an error
                if trade_to_settle.volume > 0:
                    order_direction = 'in'
                else:
                    order_direction = 'out'
                self.mark_as_valid(identifiers=identifiers,
                                   unsettled_trade=trade_to_settle.trade,
                                   order_side=trade_to_settle.side,
                                   order_direction=order_direction)

            # Be sure there are no trades in an ERROR state.
            error_trades = trade.models.Trade.objects.filter(
                models.Q(buy_order_settled_in=trade.models.SETTLED_ERROR)
                | models.Q(buy_order_settled_out=trade.models.SETTLED_ERROR)
                | models.Q(sell_order_settled_in=trade.models.SETTLED_ERROR)
                | models.Q(sell_order_settled_out=trade.models.SETTLED_ERROR))
            error_count = 0
            for error_trade in error_trades:
                error_count += 1
            if error_count > 0:
                print("ERROR: aborting settling due to errors (%d)".format(
                    error_count))
                return (-1)
            else:
                print("no errors detected: creating blockchain transactions.")

            # Finally, create the actual blockchain transactions
            for coin_type in settings.COINS.keys():
                trades_to_settle = TempTrades.objects \
                    .filter(currency=coin_type)
                if len(trades_to_settle):
                    print(
                        "\nGenerating {} transaction(s)...".format(coin_type))
                    # @TODO: keep track of transaction size, split into multiple
                    # transactions if necessary.

                    validation = {
                        'exchange': 0,
                    }
                    audit_validation = {
                        'exchange': 0,
                    }
                    total = 0
                    for trade_to_settle in trades_to_settle:
                        if trade_to_settle.wallet.id in validation:
                            validation[trade_to_settle.wallet.
                                       id] += trade_to_settle.volume
                            audit_validation[trade_to_settle.wallet.id.
                                             hex] += trade_to_settle.volume
                        else:
                            validation[trade_to_settle.wallet.
                                       id] = trade_to_settle.volume
                            audit_validation[trade_to_settle.wallet.id.
                                             hex] = trade_to_settle.volume
                        validation['exchange'] += trade_to_settle.fee
                        audit_validation['exchange'] += trade_to_settle.fee

                        total += trade_to_settle.volume + trade_to_settle.fee
                    # funds in plus funds out must be zero
                    assert (total == 0)

                    reporting.utils.audit(
                        message="settling trades balance validation",
                        details={
                            'identifiers': identifiers,
                            'coin_details': coin_details,
                            'validation': audit_validation,
                            'validation_total': total,
                            'errors': errors,
                        })

                    # The funds going into blockchain wallets:
                    vin = {}
                    # The funds coming out of blockchain wallets:
                    vout = []

                    addresses_with_unspent = {}

                    funds_in = 0
                    funds_out = 0

                    #pprint(validation)
                    for wallet_id in validation:
                        # @TODO: use a valid address
                        if wallet_id == 'exchange':
                            exchange_address = address.utils.get_new_exchange_address(
                                self, currencycode=coin_type)
                            vin[exchange_address] = validation[wallet_id]
                            continue

                        user_wallet = wallet.models.Wallet.objects.get(
                            id=wallet_id)

                        # Handle funds-in
                        if validation[wallet_id] > 0:
                            # Generate a new address from this user's wallet
                            new_address, index = address.utils.get_new_address(
                                user_wallet=user_wallet, is_change=False)
                            # @TODO: make it configurable which address gets used -- for now, we use p2pkh
                            vin[new_address['p2pkh']] = validation[wallet_id]
                            reporting.utils.audit(
                                message="settling funds in new address",
                                details={
                                    'identifiers': identifiers,
                                    'coin_details': coin_details,
                                    'wallet_id': wallet_id,
                                    'wallet_funds_in': validation[wallet_id],
                                    'address': new_address['p2pkh'],
                                })

                        else:
                            value = validation[wallet_id] * -1
                            unspent, addresses, subtotal, success = wallet.utils.get_unspent_equal_or_greater(
                                user_wallet, value)
                            if (success == False):
                                # @TODO
                                # Something has gone terribly wrong: we already validated we had sufficient funds
                                print("ERROR: ALERT ALERT")
                                return -1
                            funds_out += subtotal
                            # We'll use this when we sign the transaction
                            addresses_with_unspent[wallet_id] = addresses
                            reporting.utils.audit(
                                message="settling funds out loading unspent",
                                details={
                                    'identifiers': identifiers,
                                    'coin_details': coin_details,
                                    'wallet_id': wallet_id,
                                    'wallet_funds_out': value,
                                    'unspent': {
                                        'vout': unspent,
                                        'addresses': list(addresses),
                                        'value': subtotal,
                                    }
                                })

                            # Assemble the vout array
                            for detail in unspent:
                                vout.append(detail)
                            # If the unspent has more funds than needed, send the change back to the user's wallet
                            if subtotal > value:
                                new_address, index = address.utils.get_new_address(
                                    user_wallet=user_wallet, is_change=True)
                                vin[new_address['p2pkh']] = subtotal - value
                                #print(" + CHANGE: {}".format(subtotal - value))
                                reporting.utils.audit(
                                    message="settling returning change to user",
                                    details={
                                        'identifiers': identifiers,
                                        'coin_details': coin_details,
                                        'wallet_id': wallet_id,
                                        'wallet_id': wallet_id,
                                        'wallet_funds_out': value,
                                        'unspent': {
                                            'vout': unspent,
                                            'addresses': list(addresses),
                                            'value': subtotal,
                                        },
                                        'change': {
                                            'address': new_address['p2pkh'],
                                            'value': subtotal - value,
                                        }
                                    })

                    # Validate our vin and vout
                    for receive_address in vin:
                        assert (vin[receive_address] > 0)
                        funds_in += vin[receive_address]

                    assert (funds_in == funds_out)

                    #print(" o vin: {}".format(vin))
                    #print(" o vout: {}".format(vout))

                    # @TODO: exchange configuration for how quickly we settle
                    # @TODO: exchange configuratoin for how we settle
                    fee, valid = wallet.utils.calculate_fee(
                        wallet=user_wallet,
                        vout=vout,
                        vin=vin,
                        number_of_blocks=18,
                        estimate_mode='CONSERVATIVE')

                    reporting.utils.audit(message="settling calculating fees",
                                          details={
                                              'identifiers':
                                              identifiers,
                                              'coin_details':
                                              coin_details,
                                              'funds_in':
                                              funds_in,
                                              'funds_out':
                                              funds_out,
                                              'vin':
                                              vin,
                                              'vout':
                                              vout,
                                              'exchange_fee':
                                              validation['exchange'],
                                              'network_fee':
                                              fee,
                                              'errors':
                                              errors,
                                          })

                    if fee > validation['exchange']:
                        print(" ! ERROR: Unable to settle {}, our fee of {} is less than network fee of {} ... skipping" \
                            .format(coin_type, validation['exchange'], fee))
                        # @TODO don't record this as settled, next time we try
                        # to settle it should still need to be settled
                        reporting.utils.audit(
                            message="settling insufficient exchange fee",
                            details={
                                'identifiers': identifiers,
                                'coin_details': coin_details,
                                'funds_in': funds_in,
                                'funds_out': funds_out,
                                'vin': vin,
                                'vout': vout,
                                'exchange_fee': validation['exchange'],
                                'network_fee': fee,
                                'errors': errors,
                            })
                        continue

                    # Subtract network fee from our profits
                    vin[exchange_address] -= fee
                    #print(" - subtracting network fee of {}, leaving {} for exchange fee".format(fee, vin[exchange_address]))

                    # Create actual transaction
                    final_vin = {}
                    for to_address in vin:
                        final_vin[
                            to_address] = wallet.utils.convert_to_decimal(
                                vin[to_address])
                    #print("final_vin: {}".format(final_vin))

                    reporting.utils.audit(
                        message="settling finalizing transaction",
                        details={
                            'identifiers': identifiers,
                            'coin_details': coin_details,
                            'final_vin': final_vin,
                            'vout': vout,
                            'final_exchange_fee': validation['exchange'],
                            'network_fee': fee,
                            'errors': errors,
                        })

                    raw_tx = wallet.utils.create_raw_transaction(
                        currencycode=coin_type, input=vout, output=final_vin)
                    #print("raw_tx: {}".format(raw_tx))
                    reporting.utils.audit(message="settling raw transaction",
                                          details={
                                              'identifiers':
                                              identifiers,
                                              'coin_details':
                                              coin_details,
                                              'vin':
                                              final_vin,
                                              'vout':
                                              vout,
                                              'exchange_fee':
                                              validation['exchange'],
                                              'network_fee':
                                              fee,
                                              'raw_transaction':
                                              raw_tx,
                                              'errors':
                                              errors,
                                          })

                    # @TODO Sign the raw transaction
                    private_keys = []
                    # Load wallet's private key, effectively unlocking it

                    #print("addresses_with_unspent: {}".format(addresses_with_unspent))

                    for wallet_id in validation:
                        # exchange only receives money, skip
                        if wallet_id == 'exchange':
                            continue

                        # Only get private keys if the user is sending funds
                        if validation[wallet_id] < 0:
                            # @TODO use remote secrets database for storing private keys
                            user_wallet = wallet.models.Wallet.objects.get(
                                id=wallet_id)
                            unlocked_account = BIP32Node.from_hwif(
                                user_wallet.private_key)

                            # Get WIF for all addresses we're sending from
                            for source_address in addresses_with_unspent[
                                    wallet_id]:
                                #print("to_address: %s" % to_address)
                                address_object = Address.objects.get(
                                    wallet=wallet_id, p2pkh=source_address)
                                #print("source_address({}) index({}) is_change({})".format(source_address, address_object.index, address_object.is_change))
                                if address_object.is_change:
                                    is_change = 1
                                else:
                                    is_change = 0
                                unlocked_address = unlocked_account.subkey_for_path(
                                    "%d/%s" %
                                    (is_change, address_object.index))
                                private_keys.append(unlocked_address.wif())

                    #print(private_keys)

                    if coin_type in ['BTC', 'XTN']:
                        # Starting with 0.17, bitcoind replaces the old sign RPC with a new one
                        signed_tx = wallet.utils.sign_raw_transaction_with_key(
                            currencycode=coin_type,
                            raw_tx=raw_tx,
                            private_keys=private_keys)
                    else:
                        signed_tx = wallet.utils.sign_raw_transaction(
                            currencycode=coin_type,
                            raw_tx=raw_tx,
                            output=[],
                            private_keys=private_keys)

                    #print("signed_tx: {}".format(signed_tx))
                    reporting.utils.audit(
                        message="settling signed transaction",
                        details={
                            'identifiers': identifiers,
                            'coin_details': coin_details,
                            'vin': final_vin,
                            'vout': vout,
                            'exchange_fee': validation['exchange'],
                            'network_fee': fee,
                            'raw_transaction': raw_tx,
                            'signed_transaction': signed_tx,
                            'errors': errors,
                        })

                    try:
                        if signed_tx['complete'] is False:
                            print("ERROR: failed to sign transaction")
                            print(signed_tx)
                        else:
                            # @TODO Send the transaction to the blockchain:
                            #  - submit tx to blockchain, audit resulting txid

                            # Update database: these trades are now pending inclusion on
                            # the blockchain.
                            for trade_to_settle in trades_to_settle:
                                if trade_to_settle.volume > 0:
                                    order_direction = 'in'
                                else:
                                    order_direction = 'out'
                                self.mark_as_pending(
                                    identifiers=identifiers,
                                    unsettled_trade=trade_to_settle.trade,
                                    order_side=trade_to_settle.side,
                                    order_direction=order_direction)
                    except:
                        print(
                            "ERROR: failed to sign transaction - no response from RPC call"
                        )

            cursor.execute('DROP TABLE settle_temptrades')

        self.stdout.write(
            self.style.SUCCESS('Successfully settled %d orders' %
                               global_settled_order_count))

        reporting.utils.audit(message="completed settling all coins",
                              details={
                                  'identifiers':
                                  identifiers,
                                  'global_settled_order_count':
                                  global_settled_order_count,
                              })
Example #10
0
 def coerce_base58(v):
     key = BIP32Node.from_hwif(v)
     return key
Example #11
0
def get_wallet_addresses(currencycode, mnemonic_seed, salt):
    m = Mnemonic('english')
    salted_mnemonic = m.to_seed(mnemonic_seed, passphrase=salt)

    try:
        path = [
            "44H",  # purpose (bip44)
            "%sH" % settings.COINS[currencycode]['bip44_index'],  # coin type
            "%sH" % settings.COINS[currencycode]
            ['account_index'],  # support multiple testnets
        ]
    except Exception as e:
        print("Unexpected error, invalid currencycode?: {}".format(e))
        return None, None

    root_wallet = BIP32Node.from_master_secret(master_secret=salted_mnemonic,
                                               netcode=currencycode)

    coin_account_key = root_wallet.subkey_for_path("/".join(path))
    coin_account_private_key = coin_account_key.wallet_key(as_private=True)

    external_addresses = []
    index = 0
    addresses_since_found_on_blockchain = 0
    look_for_address = True
    while look_for_address:
        coin_wallet = BIP32Node.from_hwif(coin_account_private_key)
        subkey = coin_wallet.subkey_for_path("0/%d" % index)
        new_address = subkey.bitcoin_address()
        external_addresses.append(new_address)
        if blockchain.utils.is_address_on_blockchain(
                currencycode=currencycode,
                address_to_check=new_address,
                confirmations=1):
            addresses_since_found_on_blockchain = 0
        else:
            addresses_since_found_on_blockchain += 1
        index += 1
        if addresses_since_found_on_blockchain >= 20:
            look_for_address = False

    change_addresses = []
    index = 0
    addresses_since_found_on_blockchain = 0
    look_for_address = True
    while look_for_address:
        coin_wallet = BIP32Node.from_hwif(coin_account_private_key)
        subkey = coin_wallet.subkey_for_path("1/%d" % index)
        new_address = subkey.bitcoin_address()
        change_addresses.append(new_address)
        if blockchain.utils.is_address_on_blockchain(
                currencycode=currencycode,
                address_to_check=new_address,
                confirmations=1):
            addresses_since_found_on_blockchain = 0
        else:
            addresses_since_found_on_blockchain += 1
        index += 1
        if addresses_since_found_on_blockchain >= 20:
            look_for_address = False

    return external_addresses, change_addresses
import sys
import subprocess
from pycoin.key import Key
from pycoin.key.electrum import ElectrumWallet
from pycoin.key.validate import netcode_and_type_for_data, netcode_and_type_for_text, is_address_valid, is_wif_valid, is_public_bip32_valid
from pycoin.key.BIP32Node import BIP32Node





mpk = sys.argv[1]
index = int(sys.argv[2])

#print(is_public_bip32_valid(MPK))

wallet = BIP32Node.from_hwif(mpk)

key = wallet.subkey(0)

subkey = key.subkey(index)
calculated_address = subkey.address()
print("%s" % calculated_address)
sys.exit(calculated_address)






Example #13
0
    def send(self, wallet_id, passcode, address, amount, message='', fee=10000):
        """
        Send bitcoins to address

        :param wallet_id: bitgo wallet id
        :param address: bitcoin address
        :param amount: btc amount in satoshis
        :return: boolean
        """
        wallet = self.get_wallet(wallet_id)
        if not wallet['spendingAccount']:
            raise NotSpendableWallet()

        if not wallet['isActive']:
            raise NotActiveWallet()

        if amount < 10000:
            raise Exception('amount to small')

        if wallet['confirmedBalance'] < amount:
            raise NotEnoughFunds('Not enough funds: balance %s amount %s' %
                                 (wallet['confirmedBalance'], amount)
            )

        change_address = self.create_address(wallet_id, chain=1)
        usableKeychain = False
        spendables = []
        chain_paths = []
        p2sh = []
        payables = [(address, amount)]
        keychain_path = ""

        for keychain in wallet['private']['keychains']:
            keychain_path = keychain['path'][1:]
            keychain = self.get_keychain(keychain['xpub'])
            if 'encryptedXprv' not in keychain:
                continue
            usableKeychain = True
            break

        if not usableKeychain:
            raise BitGoError("didn't found a spendable keychain")

        data = json.loads(keychain['encryptedXprv'])
        #add base64 paddings
        for k in ['iv', 'salt', 'ct']:
            data[k] = data[k] + "=="
        cipher = sjcl.SJCL()
        xprv = cipher.decrypt(data, passcode)

        unspents = self.get_unspents(wallet_id)
        total_value = 0
        for d in unspents['unspents'][::-1]:
            path = keychain_path + d['chainPath']
            chain_paths.append(path)
            p2sh.append(h2b(d["redeemScript"]))
            spendables.append(Spendable(d["value"],
                                  h2b(d["script"]),
                                  h2b_rev(d["tx_hash"]),
                                  d["tx_output_n"]))

            total_value += d['value']
            if total_value > amount:
                break

        if total_value > (amount + fee):
            #add a change address
            #TODO: create address
            payables.append(change_address)

        p2sh_lookup = build_p2sh_lookup(p2sh)

        spendable_keys = []

        priv_key = BIP32Node.from_hwif(xprv)

        spendable_keys = [priv_key.subkey_for_path(path) for path in chain_paths]

        hash160_lookup = build_hash160_lookup([key.secret_exponent() for key in spendable_keys])

        tx = create_tx(spendables, payables)

        tx.sign(hash160_lookup=hash160_lookup, p2sh_lookup=p2sh_lookup)

        r = requests.post(self.url + '/tx/send', {
                'tx': tx.as_hex(),
                'message': message
            }, headers={
                'Authorization': 'Bearer %s' % self.access_token,
            })

        return r.json()
Example #14
0
def test_bitcoind_cosigning(dev, bitcoind, start_sign, end_sign, import_ms_wallet, clear_ms, explora, try_sign, need_keypress, addr_style):
    # Make a P2SH wallet with local bitcoind as a co-signer (and simulator)
    # - send an receive various
    # - following text of <https://github.com/bitcoin/bitcoin/blob/master/doc/psbt.md>
    # - the constructed multisig walelt will only work for a single pubkey on core side
    # - before starting this test, have some funds already deposited to bitcoind testnet wallet
    from pycoin.encoding import sec_to_public_pair
    from binascii import a2b_hex
    import re

    if addr_style == 'legacy':
        addr_fmt = AF_P2SH
    elif addr_style == 'p2sh-segwit':
        addr_fmt = AF_P2WSH_P2SH
    elif addr_style == 'bech32':
        addr_fmt = AF_P2WSH
    
    try:
        addr, = bitcoind.getaddressesbylabel("sim-cosign").keys()
    except:
        addr = bitcoind.getnewaddress("sim-cosign")

    info = bitcoind.getaddressinfo(addr)
    #pprint(info)

    assert info['address'] == addr
    bc_xfp = swab32(int(info['hdmasterfingerprint'], 16))
    bc_deriv = info['hdkeypath']        # example: "m/0'/0'/3'"
    bc_pubkey = info['pubkey']          # 02f75ae81199559c4aa...

    pp = sec_to_public_pair(a2b_hex(bc_pubkey))

    # No means to export XPUB from bitcoind! Still. In 2019.
    # - this fake will only work for for one pubkey value, the first/topmost
    node = BIP32Node('XTN', b'\x23'*32, depth=len(bc_deriv.split('/'))-1,
                        parent_fingerprint=a2b_hex('%08x' % bc_xfp), public_pair=pp)

    keys = [
        (bc_xfp, None, node),
        (1130956047, None, BIP32Node.from_hwif('tpubD8NXmKsmWp3a3DXhbihAYbYLGaRNVdTnr6JoSxxfXYQcmwVtW2hv8QoDwng6JtEonmJoL3cNEwfd2cLXMpGezwZ2vL2dQ7259bueNKj9C8n')),     # simulator: m/45'
    ]

    M,N=2,2

    clear_ms()
    import_ms_wallet(M, N, keys=keys, accept=1, name="core-cosign")

    cc_deriv = "m/45'/55"
    cc_pubkey = B2A(BIP32Node.from_hwif(simulator_fixed_xprv).subkey_for_path(cc_deriv[2:]).sec())

    

    # NOTE: bitcoind doesn't seem to implement pubkey sorting. We have to do it.
    resp = bitcoind.addmultisigaddress(M, list(sorted([cc_pubkey, bc_pubkey])),
                                                'shared-addr-'+addr_style, addr_style)
    ms_addr = resp['address']
    bc_redeem = a2b_hex(resp['redeemScript'])

    assert bc_redeem[0] == 0x52

    def mapper(cosigner_idx):
        return list(str2ipath(cc_deriv if cosigner_idx else bc_deriv))

    scr, pubkeys, xfp_paths = make_redeem(M, keys, mapper)

    assert scr == bc_redeem

    # check Coldcard calcs right address to match
    got_addr = dev.send_recv(CCProtocolPacker.show_p2sh_address(
                                M, xfp_paths, scr, addr_fmt=addr_fmt), timeout=None)
    assert got_addr == ms_addr
    time.sleep(.1)
    need_keypress('x')      # clear screen / start over

    print(f"Will be signing an input from {ms_addr}")

    if xfp2str(bc_xfp) in ('5380D0ED', 'EDD08053'):
        # my own expected values
        assert ms_addr in ( '2NDT3ymKZc8iMfbWqsNd1kmZckcuhixT5U4',
                            '2N1hZJ5mazTX524GQTPKkCT4UFZn5Fqwdz6',
                            'tb1qpcv2rkc003p5v8lrglrr6lhz2jg8g4qa9vgtrgkt0p5rteae5xtqn6njw9')

    # Need some UTXO to sign
    #
    # - but bitcoind can't give me that (using listunspent) because it's only a watched addr??
    #
    did_fund = False
    while 1:
        rr = explora('address', ms_addr, 'utxo')
        pprint(rr)

        avail = []
        amt = 0
        for i in rr:
            txn = i['txid']
            vout = i['vout']
            avail.append( (txn, vout) )
            amt += i['value']

            # just use first UTXO available; save other for later tests
            break

        else:
            # doesn't need to confirm, but does need to reach public testnet/blockstream
            assert not amt and not avail

            if not did_fund:
                print(f"Sending some XTN to {ms_addr}  (wait)")
                bitcoind.sendtoaddress(ms_addr, 0.0001, 'fund testing')
                did_fund = True
            else:
                print(f"Still waiting ...")

            time.sleep(2)

        if amt: break

    ret_addr = bitcoind.getrawchangeaddress()

    ''' If you get insufficent funds, even tho we provide the UTXO (!!), do this:

            bitcoin-cli importaddress "2NDT3ymKZc8iMfbWqsNd1kmZckcuhixT5U4" true true

        Better method: always fund addresses for testing here from same wallet (ie.
        got from non-multisig to multisig on same bitcoin-qt instance).
        -> Now doing that, automated, above.
    '''
    resp = bitcoind.walletcreatefundedpsbt([dict(txid=t, vout=o) for t,o in avail],
               [{ret_addr: amt/1E8}], 0,
                {'subtractFeeFromOutputs': [0], 'includeWatching': True}, True)

    assert resp['changepos'] == -1
    psbt = b64decode(resp['psbt'])

    open('debug/funded.psbt', 'wb').write(psbt)

    # patch up the PSBT a little ... bitcoind doesn't know the path for the CC's key
    ex = BasicPSBT().parse(psbt)
    cxpk = a2b_hex(cc_pubkey)
    for i in ex.inputs:
        assert cxpk in i.bip32_paths, 'input not to be signed by CC?'
        i.bip32_paths[cxpk] = pack('<3I', keys[1][0], *str2ipath(cc_deriv))

    psbt = ex.as_bytes()

    open('debug/patched.psbt', 'wb').write(psbt)

    _, updated = try_sign(psbt, finalize=False)

    open('debug/cc-updated.psbt', 'wb').write(updated)

    # have bitcoind do the rest of the signing
    rr = bitcoind.walletprocesspsbt(b64encode(updated).decode('ascii'))
    pprint(rr)

    open('debug/bc-processed.psbt', 'wt').write(rr['psbt'])
    assert rr['complete']

    # finalize and send
    rr = bitcoind.finalizepsbt(rr['psbt'], True)
    open('debug/bc-final-txn.txn', 'wt').write(rr['hex'])
    assert rr['complete']

    txn_id = bitcoind.sendrawtransaction(rr['hex'])
    print(txn_id)
Example #15
0
 def decode_key(dct):
     if 'hwif' in dct:
         return BIP32Node.from_hwif(dct['hwif'])
     return dct
Example #16
0
    def send(self, wallet_id, passcode, address, amount, message='', fee=None, fan_unspend=10):
        """
        Send bitcoins to address

        :param wallet_id: bitgo wallet id
        :param address: bitcoin address
        :param amount: btc amount in satoshis
        :param split: create new outputs if needed
        :return: boolean
        """
        MINIMAL_FEE = 20000
        MINIMAL_SPLIT = 10000000
        MIN_UNSPENTS_FAN = 5

        wallet = self.get_wallet(wallet_id)
        if not wallet['spendingAccount']:
            raise NotSpendableWallet()

        if not wallet['isActive']:
            raise NotActiveWallet()

        if amount < 10000:
            raise Exception('amount to small')

        if wallet['confirmedBalance'] < amount:
            raise NotEnoughFunds('Not enough funds: balance %s amount %s' %
                                 (wallet['confirmedBalance'], amount)
            )

        change_address = self.create_address(wallet_id, chain=1)
        usableKeychain = False
        spendables = []
        chain_paths = []
        p2sh = []
        payables = [(address, amount)]
        keychain_path = ""

        for keychain in wallet['private']['keychains']:
            keychain_path = keychain['path'][1:]
            keychain = self.get_keychain(keychain['xpub'])
            if 'encryptedXprv' not in keychain:
                continue
            usableKeychain = True
            break

        if not usableKeychain:
            raise BitGoError("didn't found a spendable keychain")

        data = json.loads(keychain['encryptedXprv'])
        #add base64 paddings
        for k in ['iv', 'salt', 'ct']:
            data[k] = data[k] + "=="
        cipher = sjcl.SJCL()
        xprv = cipher.decrypt(data, passcode)

        unspents = self.get_unspents(wallet_id)['unspents']
        order_unspents = sorted(unspents, key=lambda k: k['confirmations'], reverse=True)

        total_value = 0
        for d in order_unspents:
            path = keychain_path + d['chainPath']
            chain_paths.append(path)
            p2sh.append(h2b(d["redeemScript"]))
            spendables.append(Spendable(d["value"],
                                  h2b(d["script"]),
                                  h2b_rev(d["tx_hash"]),
                                  d["tx_output_n"]))

            total_value += d['value']
            if total_value > amount:
                break

        # make many outputs?
        if len(order_unspents) < MIN_UNSPENTS_FAN and (total_value > (amount + MINIMAL_SPLIT)) and fan_unspend > 0:
            fee = self.calculate_fee(len(spendables), fan_unspend)
            value = (total_value - amount - fee) / fan_unspend
            for i in range(fan_unspend):
                payables.append((change_address, value))
        elif total_value > (amount + MINIMAL_FEE):
            # add a change address
            if fee is None:
                fee = self.calculate_fee(len(spendables), 2)
            value = total_value - amount - fee
            if value > 10000: #avoid dust
                payables.append((change_address, value))

        p2sh_lookup = build_p2sh_lookup(p2sh)

        spendable_keys = []

        priv_key = BIP32Node.from_hwif(xprv)

        spendable_keys = [priv_key.subkey_for_path(path) for path in chain_paths]

        hash160_lookup = build_hash160_lookup([key.secret_exponent() for key in spendable_keys])

        tx = create_tx(spendables, payables)

        tx.sign(hash160_lookup=hash160_lookup, p2sh_lookup=p2sh_lookup)

        r = requests.post(self.url + '/tx/send', {
                'tx': tx.as_hex(),
                'message': message
            }, headers={
                'Authorization': 'Bearer %s' % self.access_token,
            })

        return r.json()
Example #17
0
def get_new_address(user_wallet, is_change=False, get_all_addresses=False):
    # Be sure this is a valid currency supported by the exchange
    if user_wallet.currencycode in settings.COINS.keys():
        all_addresses = []
        last_active_address = 0

        # Use the wallet's public_key to generate addresses.
        wallet_public_key = BIP32Node.from_hwif(user_wallet.public_key)

        # External address
        if not is_change:
            wallet_base = wallet_public_key.subkey(i=0,
                                                   is_hardened=False,
                                                   as_private=False)
            index = user_wallet.last_external_index
        # Change address
        else:
            wallet_base = wallet_public_key.subkey(i=1,
                                                   is_hardened=False,
                                                   as_private=False)
            index = user_wallet.last_change_index

        if get_all_addresses:
            index = 0

        # If index is non-zero, then increment by one to generate a new unused
        # index.
        if index > 0:
            index += 1

        # Look for an unused address.
        # @TODO: https://github.com/ .. /issues/6 search through a gap of 20
        searching = True
        while searching:
            # Create addresses following BIP44
            # https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki
            address_key = wallet_base.subkey(i=index,
                                             is_hardened=False,
                                             as_private=False)
            new_address = {
                'index': index,
                'p2pkh': address_key.bitcoin_address()
            }
            # Generate bech32 for Bitcoin and Bitcoin Testnet
            # @TODO get working with LTC https://github.com/richardkiss/pycoin/issues/323
            if user_wallet.currencycode in ['BTC', 'XTN']:
                try:
                    script = ScriptPayToAddressWit(
                        b'\0',
                        address_key.hash160(use_uncompressed=False)).script()
                    new_address['p2sh_p2wpkh'] = address_for_pay_to_script(
                        script, netcode=user_wallet.currencycode)
                    new_address['bech32'] = address_for_pay_to_script_wit(
                        script, netcode=user_wallet.currencycode)
                except Exception as e:
                    #print(e)
                    new_address['p2sh_p2wpkh'] = None
                    new_address['bech32'] = None
            else:
                new_address['p2sh_p2wpkh'] = None
                new_address['bech32'] = None
            #print("coin: %s, p2pkh: %s, p2sh_p2wpkh: %s, bech32: %s" % (user_wallet.currencycode, p2pkh, p2sh_p2wpkh, bech32))

            if address_is_used(new_address, user_wallet):
                index += 1
                if get_all_addresses:
                    all_addresses.append(new_address)
            else:
                last_active_address += 1
                if get_all_addresses and last_active_address > 20:
                    searching = False
                else:
                    searching = False
                #print("%s address: %s" % (user_wallet.currencycode, p2pkh))
        if get_all_addresses:
            return all_addresses, index
        else:
            return new_address, index
    else:
        return False, False
Example #18
0
def main():
  parser = argparse.ArgumentParser(description="Process blinktrade withdrawals requests")

  parser.add_argument('-c',
                      "--config",
                      action="store",
                      dest="config",
                      help='Configuration file', type=str)

  arguments = parser.parse_args()

  candidates = [ os.path.join(site_config_dir('blinktrade'), 'blinktrade_withdrawer.ini'),
                 os.path.expanduser('~/.blinktrade/blinktrade_withdrawer.ini')]
  if arguments.config:
    candidates.append(arguments.config)

  config = ConfigParser.SafeConfigParser()
  config.read( candidates )

  password = getpass.getpass('password: '******'ws':
    should_connect_on_ssl = False
    blinktrade_port = 80

  db_engine = config.get("database", "sqlalchemy_engine") + ':///' +\
              os.path.expanduser(config.get("database", "sqlalchemy_connection_string"))
  engine = create_engine(db_engine, echo=config.getboolean('database', 'sqlalchmey_verbose'))
  Base.metadata.create_all(engine)

  factory = BlinkTradeClientFactory(blinktrade_url.geturl())
  factory.db_session                  = scoped_session(sessionmaker(bind=engine))
  factory.verbose                     = config.getboolean("blinktrade", "verbose")
  factory.blinktrade_broker_id        = config.get("blinktrade", "broker_id")
  factory.blinktrade_user             = config.get("blinktrade", "api_key")
  factory.blinktrade_password         = decrypt(password, unhexlify(config.get("blinktrade", "api_password")))
  factory.currencies                  = json.loads(config.get("blinktrade", "currencies"))
  factory.methods                     = json.loads(config.get("blinktrade", "methods"))
  factory.blocked_accounts            = json.loads(config.get("blinktrade", "blocked_accounts"))
  factory.mandrill_api                = mandrill_api

  if config.has_section('blockchain_info'):
    from blockchain_info import BlockchainInfoWithdrawalProtocol
    factory.blockchain_guid             = decrypt(password, unhexlify(config.get("blockchain_info", "guid")))
    factory.blockchain_main_password    = decrypt(password, unhexlify(config.get("blockchain_info", "main_password")))
    factory.blockchain_second_password  = decrypt(password, unhexlify(config.get("blockchain_info", "second_password")))
    factory.blockchain_api_key          = config.get("blockchain_info", "api_key")
    factory.from_address                = config.get("blockchain_info", "from_address")
    factory.note                        = config.get("blockchain_info", "note")
    factory.protocol = BlockchainInfoWithdrawalProtocol

  if config.has_section('blocktrail'):
    import blocktrail
    from mnemonic.mnemonic import Mnemonic
    from pycoin.key.BIP32Node import BIP32Node
    is_testnet = False
    if config.get("blocktrail", "testnet") == '1':
      is_testnet = True

    client = blocktrail.APIClient(api_key=config.get("blocktrail", "api_key"),
                                  api_secret=decrypt(password, unhexlify(config.get("blocktrail", "api_secret"))),
                                  network='BTC',
                                  testnet=is_testnet)
    data = client.get_wallet(config.get("blocktrail", "wallet_identifier"))

    primary_seed = Mnemonic.to_seed(data['primary_mnemonic'],  decrypt(password, unhexlify(config.get("blocktrail", "wallet_passphrase"))))
    primary_private_key = BIP32Node.from_master_secret(primary_seed, netcode='XTN' if client.testnet else 'BTC')
    backup_public_key = BIP32Node.from_hwif(data['backup_public_key'][0])
    checksum =  client.create_checksum(primary_private_key)
    if checksum != data['checksum']:
        raise Exception("Checksum [%s] does not match expected checksum [%s], " \
                        "most likely due to incorrect password" % (checksum, data['checksum']))

    blocktrail_public_keys = {}
    for v,k in data['blocktrail_public_keys']:
      if k in blocktrail_public_keys:
        blocktrail_public_keys[k].append(v)
      else:
        blocktrail_public_keys[k] = [v]

    key_index = data['key_index']

    wallet = blocktrail.wallet.Wallet(client=client,
                                      identifier= config.get("blocktrail", "wallet_identifier"),
                                      primary_mnemonic=data['primary_mnemonic'],
                                      primary_private_key=primary_private_key,
                                      backup_public_key=backup_public_key,
                                      blocktrail_public_keys=blocktrail_public_keys,
                                      key_index=key_index,
                                      testnet=client.testnet)


    from blocktrail_protocol import BlocktrailWithdrawalProtocol
    factory.blocktrail_wallet           = wallet
    factory.blocktrail_change_address   = config.get("blocktrail", "change_address")
    factory.protocol = BlocktrailWithdrawalProtocol


  if config.has_section('mailer'):
    from mailer_protocol import MailerWithdrawalProtocol
    factory.mandrill_apikey             = config.get("mailer", "mandrill_apikey")
    factory.mandrill_template_name      = config.get("mailer", "template_name")
    factory.mandrill_from_email         = config.get("mailer", "from_email")
    factory.mandrill_from_name          = config.get("mailer", "from_name")
    factory.mandrill_to_email           = config.get("mailer", "to_email")
    factory.mandrill_to_name            = config.get("mailer", "to_name")
    factory.mandrill_website            = config.get("mailer", "website")
    factory.protocol = MailerWithdrawalProtocol

  if should_connect_on_ssl:
    reactor.connectSSL( blinktrade_url.netloc ,  blinktrade_port , factory, ssl.ClientContextFactory() )
  else:
    reactor.connectTCP(blinktrade_url.netloc ,  blinktrade_port , factory )

  reactor.run()
Example #19
0
def test_sign_p2sh_example(set_master_key, sim_execfile, start_sign, end_sign,
                           decode_psbt_with_bitcoind, offer_ms_import,
                           need_keypress, clear_ms):
    # Use the private key given in BIP 174 and do similar signing
    # as the examples.

    # PROBLEM: we can't handle this, since we don't allow same cosigner key to be used
    # more than once and that check happens after we decide we can sign an input, and yet
    # no way to provide the right set of keys needed since 4 in total, etc, etc.
    # - code below nearly works tho
    raise pytest.skip('difficult example')

    # expect xfp=4F6A0CD9
    exk = 'tprv8ZgxMBicQKsPd9TeAdPADNnSyH9SSUUbTVeFszDE23Ki6TBB5nCefAdHkK8Fm3qMQR6sHwA56zqRmKmxnHk37JkiFzvncDqoKmPWubu7hDF'
    set_master_key(exk)

    # Peeked at PSBT to know the full, deep hardened path we'll need.
    # in1: 0'/0'/0' and 0'/0'/1'
    # in2: 0'/0'/3' and 0'/0'/2'

    config = "name: p2sh-example\npolicy: 2 of 2\n\n"
    n1 = BIP32Node.from_hwif(exk).subkey_for_path("0'/0'").hwif()
    n2 = BIP32Node.from_hwif(exk).subkey_for_path("0'/0'").hwif()
    xfp = '4F6A0CD9'
    config += f'{xfp}: {n1}\n{xfp}: {n2}\n'

    clear_ms()
    offer_ms_import(config)
    time.sleep(.1)
    need_keypress('y')

    psbt = a2b_hex(open('data/worked-unsigned.psbt', 'rb').read())

    # PROBLEM: revised BIP174 has p2sh multisig cases which we don't support yet.
    # - it has two signatures from same key on same input
    # - that's a rare case and not worth supporting in the firmware
    # - but we can do it in two passes
    # - the MS wallet is also hard, since dup xfp (same actual key) ... altho can
    #   provide different subkeys

    start_sign(psbt)
    part_signed = end_sign(True)

    open('debug/ex-signed-part.psbt', 'wb').write(part_signed)

    b4 = BasicPSBT().parse(psbt)
    aft = BasicPSBT().parse(part_signed)
    assert b4 != aft, "(partial) signing didn't change anything?"

    # NOTE: cannot handle combining multisig txn yet, so cannot finalize on-device
    start_sign(part_signed, finalize=False)
    signed = end_sign(True, finalize=False)

    open('debug/ex-signed.psbt', 'wb').write(signed)
    aft2 = BasicPSBT().parse(signed)

    decode = decode_psbt_with_bitcoind(signed)
    pprint(decode)

    mx_expect = BasicPSBT().parse(
        a2b_hex(open('data/worked-combined.psbt', 'rb').read()))
    assert aft2 == mx_expect

    expect = a2b_hex(open('data/worked-combined.psbt', 'rb').read())
    decode_ex = decode_psbt_with_bitcoind(expect)

    # NOTE: because we are using RFC6979, the exact bytes of the signatures should match

    for i in range(2):
        assert decode['inputs'][i]['partial_signatures'] == \
                    decode_ex['inputs'][i]['partial_signatures']

    if 0:
        import json, decimal

        def EncodeDecimal(o):
            if isinstance(o, decimal.Decimal):
                return float(round(o, 8))
            raise TypeError

        json.dump(decode,
                  open('debug/core-decode.json', 'wt'),
                  indent=2,
                  default=EncodeDecimal)
Example #20
0
    def send(self,
             wallet_id,
             passcode,
             address,
             amount,
             message='',
             fee=10000):
        """
        Send bitcoins to address

        :param wallet_id: bitgo wallet id
        :param address: bitcoin address
        :param amount: btc amount in satoshis
        :return: boolean
        """
        wallet = self.get_wallet(wallet_id)
        if not wallet['spendingAccount']:
            raise NotSpendableWallet()

        if not wallet['isActive']:
            raise NotActiveWallet()

        if amount < 10000:
            raise Exception('amount to small')

        if wallet['confirmedBalance'] < amount:
            raise NotEnoughFunds('Not enough funds: balance %s amount %s' %
                                 (wallet['confirmedBalance'], amount))

        change_address = self.create_address(wallet_id, chain=1)
        usableKeychain = False
        spendables = []
        chain_paths = []
        p2sh = []
        payables = [(address, amount)]
        keychain_path = ""

        for keychain in wallet['private']['keychains']:
            keychain_path = keychain['path'][1:]
            keychain = self.get_keychain(keychain['xpub'])
            if 'encryptedXprv' not in keychain:
                continue
            usableKeychain = True
            break

        if not usableKeychain:
            raise BitGoError("didn't found a spendable keychain")

        data = json.loads(keychain['encryptedXprv'])
        #add base64 paddings
        for k in ['iv', 'salt', 'ct']:
            data[k] = data[k] + "=="
        cipher = sjcl.SJCL()
        xprv = cipher.decrypt(data, passcode)

        unspents = self.get_unspents(wallet_id)
        total_value = 0
        for d in unspents['unspents'][::-1]:
            path = keychain_path + d['chainPath']
            chain_paths.append(path)
            p2sh.append(h2b(d["redeemScript"]))
            spendables.append(
                Spendable(d["value"], h2b(d["script"]), h2b_rev(d["tx_hash"]),
                          d["tx_output_n"]))

            total_value += d['value']
            if total_value > amount:
                break

        if total_value > (amount + fee):
            #add a change address
            #TODO: create address
            payables.append(change_address)

        p2sh_lookup = build_p2sh_lookup(p2sh)

        spendable_keys = []

        priv_key = BIP32Node.from_hwif(xprv)

        spendable_keys = [
            priv_key.subkey_for_path(path) for path in chain_paths
        ]

        hash160_lookup = build_hash160_lookup(
            [key.secret_exponent() for key in spendable_keys])

        tx = create_tx(spendables, payables)

        tx.sign(hash160_lookup=hash160_lookup, p2sh_lookup=p2sh_lookup)

        r = requests.post(self.url + '/tx/send', {
            'tx': tx.as_hex(),
            'message': message
        },
                          headers={
                              'Authorization': 'Bearer %s' % self.access_token,
                          })

        return r.json()
Example #21
0
def test_bip_vectors(mode, index, entropy, expect, set_encoded_secret, dev,
                     cap_menu, pick_menu_item, goto_home, cap_story,
                     need_keypress, microsd_path, settings_set, sim_eval,
                     sim_exec, reset_seed_words):

    set_encoded_secret(a2b_hex(EXAMPLE_XPRV))
    settings_set('chain', 'BTC')

    goto_home()
    pick_menu_item('Advanced')
    pick_menu_item('Derive Entropy')

    time.sleep(0.1)
    title, story = cap_story()

    assert 'seed value' in story
    assert 'other wallet systems' in story

    need_keypress('y')
    time.sleep(0.1)

    pick_menu_item(mode)

    if index is not None:
        time.sleep(0.1)
        for n in str(index):
            need_keypress(n)

    need_keypress('y')

    time.sleep(0.1)
    title, story = cap_story()

    assert f'Path Used (index={index}):' in story
    assert "m/83696968'/" in story
    assert f"/{index}'" in story

    if entropy is not None:
        assert f"Raw Entropy:\n{entropy}" in story

    do_import = False

    if 'words' in mode:
        num_words = int(mode.split()[0])
        assert f'Seed words ({num_words}):' in story
        assert f"m/83696968'/39'/0'/{num_words}'/{index}'" in story
        assert '\n 1: ' in story
        assert f'\n{num_words}: ' in story
        got = [
            ln[4:] for ln in story.split('\n') if len(ln) > 5 and ln[2] == ':'
        ]
        assert ' '.join(got) == expect
        do_import = 'words'

    elif 'XPRV' in mode:
        assert 'Derived XPRV:' in story
        assert f"m/83696968'/32'/{index}'" in story
        assert expect in story
        do_import = 'xprv'

    elif 'WIF' in mode:
        assert 'WIF (privkey)' in story
        assert f"m/83696968'/2'/{index}'" in story
        assert expect in story

    elif 'bytes hex' in mode:
        width = int(mode.split('-')[0])
        assert width in {32, 64}
        assert f'Hex ({width} bytes):' in story
        assert f"m/83696968'/128169'/{width}'/{index}'" in story
        assert expect in story

    else:
        raise ValueError(mode)

    # write to SD
    msg = story.split('Press', 1)[0]
    if 1:
        assert 'Press 1 to save' in story
        need_keypress('1')

        time.sleep(0.1)
        title, story = cap_story()

        assert title == 'Saved'
        fname = story.split('\n')[-1]
        need_keypress('y')

        time.sleep(0.1)
        title, story = cap_story()

        assert story.startswith(msg)

        path = microsd_path(fname)
        assert path.endswith('.txt')
        txt = open(path, 'rt').read()

        assert txt.strip() == msg.strip()

    if do_import:
        assert '2 to switch to derived secret' in story

        try:
            time.sleep(0.1)
            need_keypress('2')

            time.sleep(0.1)
            title, story = cap_story()
            assert 'New master key in effect' in story

            encoded = sim_exec(
                'from pincodes import pa; RV.write(repr(pa.fetch()))')
            print(encoded)
            assert 'Error' not in encoded
            encoded = eval(encoded)
            assert len(encoded) == 72

            marker = encoded[0]
            if do_import == 'words':
                assert marker & 0x80 == 0x80
                width = ((marker & 0x3) + 2) * 8
                assert width in {16, 24, 32}
                assert encoded[1:1 + width] == a2b_hex(entropy)
            elif do_import == 'xprv':
                assert marker == 0x01
                node = BIP32Node.from_hwif(expect)
                ch, pk = encoded[1:33], encoded[33:65]
                assert node.chain_code() == ch
                assert node.secret_exponent() == int(B2A(pk), 16)

        finally:
            # required cleanup
            reset_seed_words()

    else:
        assert '3 to view as QR code' in story

    need_keypress('x')
Example #22
0
def test_path_index(mode, pattern, index, set_encoded_secret, dev, cap_menu,
                    pick_menu_item, goto_home, cap_story, need_keypress,
                    cap_screen_qr, qr_quality_check):
    # Uses any key on Simulator; just checking for operation + entropy level

    goto_home()
    pick_menu_item('Advanced')
    pick_menu_item('Derive Entropy')

    time.sleep(0.1)
    title, story = cap_story()

    assert 'seed value' in story
    assert 'other wallet systems' in story

    need_keypress('y')
    time.sleep(0.1)

    pick_menu_item(mode)

    if index is not None:
        time.sleep(0.1)
        for n in str(index):
            need_keypress(n)

    need_keypress('y')

    time.sleep(0.1)
    title, story = cap_story()

    assert f'Path Used (index={index}):' in story
    assert "m/83696968'/" in story
    assert f"/{index}'" in story

    got = re.findall(pattern, story)[0]

    assert len(set(got)) >= 12

    global HISTORY
    assert got not in HISTORY
    HISTORY.add(got)

    if 'words' in mode:
        exp = Mnemonic('english').to_mnemonic(a2b_hex(got)).split()
        assert '\n'.join(f'{n+1:2d}: {w}' for n, w in enumerate(exp)) in story
    elif 'XPRV' in mode:
        node = BIP32Node.from_hwif(got)
        assert str(b2a_hex(node.chain_code()), 'ascii') in story
        assert hex(node.secret_exponent())[2:] in story
    elif 'WIF' in mode:
        key = Key.from_text(got)
        assert hex(key.secret_exponent())[2:] in story

    if index == 0:
        assert '3 to view as QR code' in story
        need_keypress('3')

        qr = cap_screen_qr().decode('ascii')

        if 'words' in mode:
            gw = qr.lower().split()
            assert gw == [i[0:4] for i in exp]

        elif 'hex' in mode:
            assert qr.lower() == got

        elif 'XPRV' in mode:
            assert qr == got

        elif 'WIF' in mode:
            assert qr == got
Example #23
0
    def post(self, request, format=None):
        # Verify that a wallet_id has been passed in
        wallet_id, success = wallet.utils.get_wallet_id(request)
        if success is not True:
            # If status code is set, then wallet_id is a JSON-formatted error: abort!
            return Response(wallet_id, status=success)

        # Be sure the wallet exists and the user has access to it.
        user_wallet, success = wallet.utils.get_wallet(
            user_id=self.request.user.id, wallet_id=wallet_id)
        if success is not True:
            return Response(user_wallet, status=success)

        # Be sure wallet contains supported currency.
        if user_wallet.currencycode not in settings.COINS.keys():
            status_code = status.HTTP_400_BAD_REQUEST
            data = {
                "status": "invalid data",
                "code": status_code,
                "debug": {
                    "request": self.request.data,
                    "symbol": user_wallet.currencycode,
                },
                "data": {},
            }
            return Response(data, status=status_code)

        passphrase, success = wallet.utils.get_passphrase(request)
        if success != True:
            # If status code is set, then passphrase is a JSON-formatted error: abort!
            return Response(passphrase, status=success)

        if not wallet.utils.valid_passphrase(self.request.user, passphrase):
            status_code = status.HTTP_400_BAD_REQUEST
            data = {
                "status": "invalid passphrase",
                "code": status_code,
                "debug": {
                    "passphrase": self.request.user.passphrase,
                    "request": self.request.data,
                },
                "data": {},
            }
            return Response(data, status=status_code)

        output, output_total, success = wallet.utils.get_output(
            request, user_wallet=user_wallet)
        if success != True:
            # If status code is set, then output is a JSON-formatted error: abort!
            return Response(output, status=success)

        number_of_blocks, estimate_mode, success = wallet.utils.get_priority(
            request)
        if success != True:
            # If status code is set, then number_of_blocks is a JSON-formatted error: abort!
            return Response(number_of_blocks, status=success)

        # STEP 1: find enough unspent to cover desired transaction
        total_to_send = output_total
        unspent, addresses, unspent_total, success = wallet.utils.get_unspent_equal_or_greater(
            user_wallet, total_to_send)
        if success is not True:
            # If success isn't true, unspent contains an error: abort
            return Response(unspent, status=success)

        # STEP 2: calculate fee and change
        #   https://bitcoin.org/en/developer-reference#estimatefee
        fee, valid = wallet.utils.calculate_fee(
            wallet=user_wallet,
            vout=unspent,
            vin=output,
            number_of_blocks=number_of_blocks,
            estimate_mode=estimate_mode)

        if not valid:
            status_code = status.HTTP_500_INTERNAL_SERVER_ERROR
            data = {
                "status": "fee error",
                "code": status_code,
                "debug": {
                    "request": self.request.data,
                    "fee": fee,
                    "unspent": unspent,
                    "output": output,
                    "number_of_blocks": number_of_blocks,
                    "estimate_mode": estimate_mode,
                },
                "data": {},
            }
            return Response(data, status=status_code)

        # Check if our unspent can cover our desired transaction plus the calculated fee,
        # if not re-request unspent this time including enough for the fee.
        if (output_total + fee) > unspent_total:
            print("fee grew, re-request unspent")
            # TODO: loop in case this requires more unspent and therefor a larger fee
            unspent, addresses, unspent_total, success = wallet.utils.get_unspent_equal_or_greater(
                user_wallet, total_to_send + fee)
            if success is not True:
                # If success isn't true, unspent contains an error: abort
                return Response(unspent, status=success)

        change = unspent_total - (total_to_send + fee)
        #print("addresses: %s, unspent: %s, change: %s" % (addresses, unspent, change))

        # STEP 3: create a raw transaction including unspent and destination address
        final_output = {}
        for out_address in output:
            # Build an array of outputs we are sending
            final_output[out_address] = wallet.utils.convert_to_decimal(
                output[out_address])
            # An address may have multiple outputs, use a set to only get each address once

        new_change_address, change_index = address.utils.get_new_address(
            user_wallet=user_wallet, is_change=True)
        final_output[new_change_address[
            'p2pkh']] = wallet.utils.convert_to_decimal(change)
        #pprint(final_output)
        raw_tx = wallet.utils.create_raw_transaction(
            currencycode=user_wallet.currencycode,
            input=unspent,
            output=final_output)
        #print("raw_tx: %s" % raw_tx)

        # STEP 4: sign the raw transaction
        # @TODO request private key from secrets database -- limit each wallet to only 1 private key
        private_keys = []
        # Load wallet's private key, effectively unlocking it
        unlocked_account = BIP32Node.from_hwif(user_wallet.private_key)
        # Get WIF for all addresses we're sending from
        for to_address in addresses:
            #print("to_address: %s" % to_address)
            loaded_address = Address.objects.filter(wallet=user_wallet.id,
                                                    p2pkh=to_address)
            for la in loaded_address:
                if la.is_change:
                    is_change = 1
                else:
                    is_change = 0
                unlocked_address = unlocked_account.subkey_for_path(
                    "%d/%s" % (is_change, la.index))
                #print("wif: %s subkey path: %d/%s" % (unlocked_address.wif(), is_change, la.index))
                private_keys.append(unlocked_address.wif())

        #print("private_keys:")
        #pprint(private_keys)

        if user_wallet.currencycode in ['BTC', 'XTN']:
            # Starting with 0.17, bitcoind replaces the old sign RPC with a new one
            signed_tx = wallet.utils.sign_raw_transaction_with_key(
                currencycode=user_wallet.currencycode,
                raw_tx=raw_tx,
                private_keys=private_keys)
            #print("signed_tx: %s" % signed_tx)
        else:
            signed_tx = wallet.utils.sign_raw_transaction(
                currencycode=user_wallet.currencycode,
                raw_tx=raw_tx,
                output=[],
                private_keys=private_keys)
            #print("signed_tx: %s" % signed_tx)

        try:
            if signed_tx['complete'] is False:
                '''
                response (decoded): {'result': {'hex': '02000000011db9fe34f5a8c4854c68ca73faa92b41f479df0a562d76c3e69fefdf325855100000000000ffffffff02c4090000000000001976a914df75177fb70c628dc9387456a36c5c7f8f472f4488acdb9ee60e000000001976a914bb48756d3b4ab3d383713af776d16abef9243b3d88ac00000000', 'complete': False, 'errors': [{'txid': '10555832dfef9fe6c3762d560adf79f4412ba9fa73ca684c85c4a8f534feb91d', 'vout': 0, 'witness': [], 'scriptSig': '', 'sequence': 4294967295, 'error': 'Unable to sign input, invalid stack size (possibly missing key)'}]}, 'error': None, 'id': 51929}
                '''
                status_code = status.HTTP_500_INTERNAL_SERVER_ERROR
                data = {
                    "status": "invalid transaction",
                    "code": status_code,
                    "debug": {
                        "request": self.request.data,
                        "fee": fee,
                        "signed_tx": signed_tx,
                        "output": final_output,
                        "spent": unspent,
                    },
                    "data": {},
                }
                return Response(data, status=status_code)
        except Exception as e:
            status_code = status.HTTP_500_INTERNAL_SERVER_ERROR
            data = {
                "status": "signing error",
                "code": status_code,
                "debug": {
                    "request": self.request.data,
                    "fee": fee,
                    "signed_tx": signed_tx,
                    "output": final_output,
                    "unspent": unspent,
                    "exception": str(e),
                },
                "data": {},
            }
            return Response(data, status=status_code)

        # STEP 5: send the signed transaction
        txid = wallet.utils.send_signed_transaction(
            currencycode=user_wallet.currencycode, signed_tx=signed_tx['hex'])

        if txid:
            # funds were sent, add the change address to our wallet
            wallet.utils.add_addresses_to_wallet(
                wallet_id=user_wallet.id,
                label='change',
                p2pkh=new_change_address['p2pkh'],
                p2sh_p2wpkh=None,
                bech32=None,
                index=change_index,
                is_change=True)
            status_message = "funds sent"
        else:
            status_message = "no funds sent"

        status_code = status.HTTP_200_OK
        data = {
            "status": status_message,
            "code": status_code,
            "debug": {},
            "data": {
                "txid": txid,
                "fee": fee,
                "output": final_output,
                "spent": unspent,
                "change_address": new_change_address['p2pkh'],
            },
        }
        return Response(data, status=status_code)
Example #24
0
def main():
    parser = argparse.ArgumentParser(
        description="Process blinktrade withdrawals requests")

    parser.add_argument('-c',
                        "--config",
                        action="store",
                        dest="config",
                        help='Configuration file',
                        type=str)

    arguments = parser.parse_args()

    candidates = [
        os.path.join(site_config_dir('blinktrade'),
                     'blinktrade_withdrawer.ini'),
        os.path.expanduser('~/.blinktrade/blinktrade_withdrawer.ini')
    ]
    if arguments.config:
        candidates.append(arguments.config)

    config = ConfigParser.SafeConfigParser()
    config.read(candidates)

    password = getpass.getpass('password: '******'ws':
        should_connect_on_ssl = False
        blinktrade_port = 80

    db_engine = config.get("database", "sqlalchemy_engine") + ':///' +\
                os.path.expanduser(config.get("database", "sqlalchemy_connection_string"))
    engine = create_engine(db_engine,
                           echo=config.getboolean('database',
                                                  'sqlalchmey_verbose'))
    Base.metadata.create_all(engine)

    factory = BlinkTradeClientFactory(blinktrade_url.geturl())
    factory.db_session = scoped_session(sessionmaker(bind=engine))
    factory.verbose = config.getboolean("blinktrade", "verbose")
    factory.blinktrade_broker_id = config.get("blinktrade", "broker_id")
    factory.blinktrade_user = config.get("blinktrade", "api_key")
    factory.blinktrade_password = decrypt(
        password, unhexlify(config.get("blinktrade", "api_password")))
    factory.currencies = json.loads(config.get("blinktrade", "currencies"))
    factory.methods = json.loads(config.get("blinktrade", "methods"))
    factory.blocked_accounts = json.loads(
        config.get("blinktrade", "blocked_accounts"))
    factory.mandrill_api = mandrill_api

    if config.has_section('blockchain_info'):
        from blockchain_info import BlockchainInfoWithdrawalProtocol
        factory.blockchain_guid = decrypt(
            password, unhexlify(config.get("blockchain_info", "guid")))
        factory.blockchain_main_password = decrypt(
            password, unhexlify(config.get("blockchain_info",
                                           "main_password")))
        factory.blockchain_second_password = decrypt(
            password,
            unhexlify(config.get("blockchain_info", "second_password")))
        factory.blockchain_api_key = config.get("blockchain_info", "api_key")
        factory.from_address = config.get("blockchain_info", "from_address")
        factory.note = config.get("blockchain_info", "note")
        factory.protocol = BlockchainInfoWithdrawalProtocol

    if config.has_section('blocktrail'):
        import blocktrail
        from mnemonic.mnemonic import Mnemonic
        from pycoin.key.BIP32Node import BIP32Node
        is_testnet = False
        if config.get("blocktrail", "testnet") == '1':
            is_testnet = True

        client = blocktrail.APIClient(
            api_key=config.get("blocktrail", "api_key"),
            api_secret=decrypt(
                password, unhexlify(config.get("blocktrail", "api_secret"))),
            network='BTC',
            testnet=is_testnet)
        data = client.get_wallet(config.get("blocktrail", "wallet_identifier"))

        primary_seed = Mnemonic.to_seed(
            data['primary_mnemonic'],
            decrypt(password,
                    unhexlify(config.get("blocktrail", "wallet_passphrase"))))
        primary_private_key = BIP32Node.from_master_secret(
            primary_seed, netcode='XTN' if client.testnet else 'BTC')
        backup_public_key = BIP32Node.from_hwif(data['backup_public_key'][0])
        checksum = client.create_checksum(primary_private_key)
        if checksum != data['checksum']:
            raise Exception("Checksum [%s] does not match expected checksum [%s], " \
                            "most likely due to incorrect password" % (checksum, data['checksum']))

        blocktrail_public_keys = {}
        for v, k in data['blocktrail_public_keys']:
            if k in blocktrail_public_keys:
                blocktrail_public_keys[k].append(v)
            else:
                blocktrail_public_keys[k] = [v]

        key_index = data['key_index']

        wallet = blocktrail.wallet.Wallet(
            client=client,
            identifier=config.get("blocktrail", "wallet_identifier"),
            primary_mnemonic=data['primary_mnemonic'],
            primary_private_key=primary_private_key,
            backup_public_key=backup_public_key,
            blocktrail_public_keys=blocktrail_public_keys,
            key_index=key_index,
            testnet=client.testnet)

        from blocktrail_protocol import BlocktrailWithdrawalProtocol
        factory.blocktrail_wallet = wallet
        factory.blocktrail_change_address = config.get("blocktrail",
                                                       "change_address")
        factory.protocol = BlocktrailWithdrawalProtocol

    if config.has_section('mailer'):
        from mailer_protocol import MailerWithdrawalProtocol
        factory.mandrill_apikey = config.get("mailer", "mandrill_apikey")
        factory.mandrill_template_name = config.get("mailer", "template_name")
        factory.mandrill_from_email = config.get("mailer", "from_email")
        factory.mandrill_from_name = config.get("mailer", "from_name")
        factory.mandrill_to_email = config.get("mailer", "to_email")
        factory.mandrill_to_name = config.get("mailer", "to_name")
        factory.mandrill_website = config.get("mailer", "website")
        factory.protocol = MailerWithdrawalProtocol

    if should_connect_on_ssl:
        reactor.connectSSL(blinktrade_url.netloc, blinktrade_port, factory,
                           ssl.ClientContextFactory())
    else:
        reactor.connectTCP(blinktrade_url.netloc, blinktrade_port, factory)

    reactor.run()
Example #25
0
    def doit(M, addr_fmt=None, do_import=True):
        passwords = ['Me', 'Myself', 'And I', '']

        if 0:
            # WORKING, but slow .. and it's constant data
            keys = []
            for pw in passwords:
                xfp = set_bip39_pw(pw)

                sk = dev.send_recv(CCProtocolPacker.get_xpub("m/45'"))
                node = BIP32Node.from_wallet_key(sk)

                keys.append((xfp, None, node))

            assert len(set(x for x, _, _ in keys)) == 4, keys
            pprint(keys)
        else:
            # Much, FASTER!
            assert dev.is_simulator
            keys = [
                (3503269483, None,
                 BIP32Node.from_hwif(
                     'tpubD9429UXFGCTKJ9NdiNK4rC5ygqSUkginycYHccqSg5gkmyQ7PZRHNjk99M6a6Y3NY8ctEUUJvCu6iCCui8Ju3xrHRu3Ez1CKB4ZFoRZDdP9'
                 )),
                (2389277556, None,
                 BIP32Node.from_hwif(
                     'tpubD97nVL37v5tWyMf9ofh5rznwhh1593WMRg6FT4o6MRJkKWANtwAMHYLrcJFsFmPfYbY1TE1LLQ4KBb84LBPt1ubvFwoosvMkcWJtMwvXgSc'
                 )),
                (3190206587, None,
                 BIP32Node.from_hwif(
                     'tpubD9ArfXowvGHnuECKdGXVKDMfZVGdephVWg8fWGWStH3VKHzT4ph3A4ZcgXWqFu1F5xGTfxncmrnf3sLC86dup2a8Kx7z3xQ3AgeNTQeFxPa'
                 )),
                (1130956047, None,
                 BIP32Node.from_hwif(
                     'tpubD8NXmKsmWp3a3DXhbihAYbYLGaRNVdTnr6JoSxxfXYQcmwVtW2hv8QoDwng6JtEonmJoL3cNEwfd2cLXMpGezwZ2vL2dQ7259bueNKj9C8n'
                 )),
            ]

        if do_import:
            # render as a file for import
            config = f"name: Myself-{M}\npolicy: {M} / 4\n\n"

            if addr_fmt:
                config += f'format: {addr_fmt.upper()}\n'

            config += '\n'.join('%s: %s' % (xfp2str(xfp), sk.hwif())
                                for xfp, _, sk in keys)
            #print(config)

            title, story = offer_ms_import(config)
            #print(story)

            # dont care if update or create; accept it.
            time.sleep(.1)
            need_keypress('y')

        def select_wallet(idx):
            # select to specific pw
            xfp = set_bip39_pw(passwords[idx])
            assert xfp == keys[idx][0]

        return (keys, select_wallet)
Example #26
0
 def decode_key(dct):
     if 'hwif' in dct:
         return BIP32Node.from_hwif(dct['hwif'])
     return dct