示例#1
0
 def on_close(self, ):
     super().on_close()
     self.stop_fusion_server()
     if self.registered_network_callback:
         self.registered_network_callback = False
         network = Network.get_instance()
         if network:
             network.unregister_callback(self.on_wallet_transaction)
     self.active = False
示例#2
0
class cashripQT(QWidget):
    def __init__(self, parent):
        super().__init__()
        #self.window = window
        self.parent = parent
        self.config = None
        self.title = 'CashRipQT'
        self.network = Network(None)
        self.network.start()
        cashrip.topDir = './cash_rip_data'
        if not os.path.isdir(cashrip.topDir):
            os.mkdir(cashrip.topDir)
        cashrip.contracts = cashrip.loadContracts()
        cashrip.multiWallets = cashrip.getMultiWallets()
        cashrip.startSyncWallets(cashrip.multiWallets, self.network)
        self.initUI()

    def initUI(self):
        QToolTip.setFont(QFont('SansSerif', 10))
        #self.setToolTip('This is a <b>QWidget</b> widget')
        self.buttons = QHBoxLayout()

        btn1 = QPushButton('Invite', self)
        btn1.setToolTip('Creates a new contract.')
        btn1.resize(btn1.sizeHint())
        btn1.clicked.connect(self.invite)
        self.buttons.addWidget(btn1)

        #btn.move(50, 50)
        btn2 = QPushButton('AcceptInvite', self)
        btn2.setToolTip('Input: partner\'s <b>x_pubkey</b>.')
        btn2.resize(btn2.sizeHint())
        btn2.clicked.connect(self.accInvite)
        self.buttons.addWidget(btn2)

        btn3 = QPushButton('CheckAddress', self)
        btn3.setToolTip(
            'Input: your partner\'s generated multisig <b>address</b> and <b>x_pubkey</b>. Also select the <b>contract</b> you used to invite your partner.'
        )
        btn3.resize(btn3.sizeHint())
        btn3.clicked.connect(self.checkAddress)
        self.buttons.addWidget(btn3)

        btn4 = QPushButton('RequestRelease', self)
        btn4.setToolTip(
            'Input:  BCH <b>address</b> to which the funds will be released. Also select your <b>contract</b> that contains the funds to be released.'
        )
        btn4.resize(btn4.sizeHint())
        btn4.clicked.connect(self.requestRelease)
        self.buttons.addWidget(btn4)

        btn5 = QPushButton('Release', self)
        btn5.setToolTip(
            'Input: <b>hex code</b> sent by your partner. Also select your <b>contract</b> that contains the funds to be released.'
        )
        btn5.resize(btn5.sizeHint())
        btn5.clicked.connect(self.release)
        self.buttons.addWidget(btn5)

        btn6 = QPushButton('Delete Contract', self)
        btn6.setToolTip(
            'Delete selected <b>contract</b>. Do not delete any contract that still contains funds as you will then not be able to release those funds in the future.'
        )
        btn6.resize(btn6.sizeHint())
        btn6.clicked.connect(self.delContract)
        self.buttons.addWidget(btn6)

        self.table = cashRipList(self)
        self.table.setSelectionBehavior(QAbstractItemView.SelectRows)
        #self.table.itemClicked.connect(self.table_click)
        #self.table.setColumnCount(4)
        #self.table.setHorizontalHeaderLabels(("Address;Confirmed;Unconfirmed;x_pubkey").split(";"))
        #self.table.setColumnWidth(3,230)
        #self.table.horizontalHeaderItem().setTextAlignment(Qt.AlignHCenter)
        self.table.update()

        #listWidget.currentItemChanged.connect(self.item_click)
        self.textArea = QLabel(
            'Please select the contract you wish to use above.\nContract information (x_pubkey or transaction hex) goes in the box below.'
        )
        #self.textArea.setText('Please select the contract you wish to use above.\nContract information (x_pubkey or transaction hex) goes in the box below.')
        self.textBox = QPlainTextEdit(self)
        self.textBox.setPlainText('')

        self.addressBoxArea = QHBoxLayout()
        self.addressBox = QLineEdit(self)
        self.addrLabel = QLabel("Address:")
        self.addressBoxArea.addWidget(self.addrLabel)
        self.addressBoxArea.addWidget(self.addressBox)

        # Add box layout, add table to box layout and add box layout to widget
        self.layout = QVBoxLayout()
        self.layout.addWidget(self.table)
        #layout.addStretch(1)
        self.layout.addWidget(self.textArea)
        self.layout.addWidget(self.textBox)
        self.layout.addLayout(self.addressBoxArea)
        self.layout.addLayout(self.buttons)
        self.setLayout(self.layout)

        #self.layout.move(100,100)
        #self.listWidget.show()
        self.setWindowTitle('Cash Rip')
        #self.setGeometry(self.left, self.top, self.width, self.height)
        self.resize(self.sizeHint())
        self.center()
        self.show()

    def center(self):
        qr = self.frameGeometry()
        cp = QDesktopWidget().availableGeometry().center()
        qr.moveCenter(cp)
        self.move(qr.topLeft())

    def table_click(self, item):
        pass
        #print(item.text())
        #print(self.currentRow())
        #print(self.table.currentRow())

    def getCurrentContract(self):
        item = self.table.currentItem()
        if item:
            return int(item.text(0))
        else:
            self.textBox.setPlainText(
                "Please select a contract above, or create a new one via Invite or Accept."
            )
            return None

    #@pyqtSlot()
    #def run_update(self):
    #    self.table.update()

    def invite(self):
        self.textBox.setPlainText("Please wait . . .")
        self.textBox.repaint()
        wallet, contract = cashrip.genContractWallet()
        contract["label"] = "buyer"
        cashrip.updateContracts()
        self.parent.update()
        self.textBox.setPlainText(
            "Give this x_pubkey to the other party:\n{}".format(
                contract['my_x_pubkey']))

    def accInvite(self):
        xpub = self.textBox.document().toPlainText()
        if xpub[:2] != "ff" or len(xpub) < 100:
            #self.textBox.setStyleSheet("background-color: rgb(255, 0, 0);")
            self.textBox.setPlainText(
                "Please enter your partner's x_pubkey into this textbox before clicking AcceptInvite."
            )
            return
        self.textBox.setPlainText("Please wait . . .")
        self.textBox.repaint()
        wallet, contract = cashrip.genContractWallet()
        contract["label"] = "merchant"
        cashrip.updateContracts()
        idx = len(cashrip.contracts) - 1
        try:
            contract = cashrip.create_multisig_addr(idx, xpub)
            self.textBox.setPlainText(
                "Your x_pubkey: {}\nYour multisig address: {}\nPlease share your x_pubkey and multisig address with your partner."
                .format(contract["my_x_pubkey"], contract["address"]))
            self.parent.update()
        except:
            self.textBox.setPlainText(
                "Something was wrong with the x_pubkey you pasted.")
            cashrip.delContract(idx)
            self.parent.update()

        if self.textBox.document().toPlainText()[:4] == "Your":
            #print("we here")
            cashrip.startSyncMultiWallet(idx, self.network)

    def checkAddress(self):
        xpub = self.textBox.document().toPlainText()
        if xpub[:2] != "ff" or len(xpub) < 100:
            self.textBox.setPlainText(
                "Please enter your partner's x_pubkey into this textbox before clicking CheckAddress."
            )
            return
        addrOrig = self.addressBox.text()
        try:
            addr = Address.from_string(addrOrig)
        except:
            self.addressBox.setText(
                "Please enter multisig address here before clicking CheckAddress."
            )
            return
        if addr.kind != Address.ADDR_P2SH:
            self.addressBox.setText(
                "The address you entered was not a multisig address.")
            return
        currentContract = self.getCurrentContract()
        if currentContract != None:
            if "address" in cashrip.contracts[currentContract]:
                self.textBox.setPlainText(
                    "This contract already has an address. Maybe you selected the wrong contract?"
                )
                return
            if cashrip.contracts[currentContract]["my_x_pubkey"] == xpub:
                self.textBox.setPlainText(
                    "You entered your own x_pubkey, not your partner's.")
                return
            try:
                self.textBox.setPlainText("Please wait . . .")
                self.textBox.repaint()
                contract = cashrip.create_multisig_addr(
                    currentContract, xpub, False)
            except:
                self.textBox.setPlainText(
                    "Something was wrong with the x_pubkey you pasted.")
                return
            if contract["address"] == addr:
                self.textBox.setPlainText(
                    "Success. You and your partner generated the same address. You can now send funds to {}"
                    .format(addrOrig))
                cashrip.startSyncMultiWallet(currentContract, self.network)
            else:
                self.textBox.setPlainText(
                    "Something went wrong. You and your partner generated different addresses. Please double-check the x_pubkeys that you have sent to each other."
                )
                os.remove(contract['addrWalletFile'])
                del contract["addrWalletFile"]
                del contract["address"]
                del contract["partner_addr"]
                del contract["partner_x_pubkey"]
                del contract["partner_pubkey"]
                del contract["gen_by_me"]
                del contract["redeemScript"]
                cashrip.updateContracts()
                cashrip.multiWallets[currentContract] = None
            self.parent.update()

    def requestRelease(self):
        addr = self.addressBox.text()
        try:
            addr = Address.from_string(addr)
        except:
            self.addressBox.setText(
                "Please enter address here before clicking RequestRelease.")
            return
        currentContract = self.getCurrentContract()
        if currentContract != None:
            self.textBox.setPlainText("Please wait . . .")
            self.textBox.repaint()
            try:
                tx = cashrip.maketx_from_multisig(currentContract, addr,
                                                  self.network)
            except Exception as e:
                self.textBox.setPlainText(str(e))
                return
            self.textBox.setPlainText(
                "Send this transaction hex to your partner. He needs it to release your funds:\n{}"
                .format(tx['hex']))

    def release(self):
        txhex = self.textBox.document().toPlainText()
        if len(txhex) < 150:
            self.textBox.setPlainText(
                "Please enter the transaction hex into this box before hitting Release."
            )
            return
        currentContract = self.getCurrentContract()
        if currentContract != None:
            try:
                self.textBox.setPlainText("Please wait . . .")
                self.textBox.repaint()
                sent = cashrip.sign_broadcast_tx_from_partner(
                    txhex, currentContract, self.network)
                if sent:
                    self.textBox.setPlainText(
                        "Transaction was broadcast to the network.")
                else:
                    self.textBox.setPlainText(
                        "Transaction was not broadcast. Either you selected the wrong contract or the transaction hex did not contain a valid signature."
                    )
            except:
                self.addressBox.setText(
                    "Something went wrong. Maybe the hex value was invalid.")

    def delContract(self):
        self.textBox.setPlainText('')
        currentContract = self.getCurrentContract()
        #print(currentContract)
        if currentContract != None:
            curItem = self.table.currentItem()
            balC = curItem.text(3)
            balU = curItem.text(4)
            if curItem.text(2)[:4] != 'Wait' and (balC != "0.0"
                                                  or balU != "0.0"):
                buttonReply = QMessageBox.question(
                    self, 'Confirmation',
                    "Are you sure you want to delete Contract #{}? It contains funds and you will be unable to release them in the future."
                    .format(currentContract), QMessageBox.Yes | QMessageBox.No,
                    QMessageBox.No)
            else:
                buttonReply = QMessageBox.question(
                    self, 'Confirmation',
                    "Are you sure you want to delete Contract #{}?".format(
                        currentContract), QMessageBox.Yes | QMessageBox.No,
                    QMessageBox.No)
            if buttonReply == QMessageBox.Yes:
                cashrip.delContract(currentContract)
                #self.table.update()
                self.parent.update()
            else:
                return
示例#3
0
    def run(self, ):
        # this gets called roughly every 0.1 s in the Plugins thread; downclock it to 5 s.
        run_iter = self._run_iter + 1
        if run_iter < 50:
            self._run_iter = run_iter
            return
        else:
            self._run_iter = 0

        if not self.active:
            return

        dont_start_fusions = False

        network = Network.get_instance()
        if network and network.is_connected():
            self.t_last_net_ok = time.monotonic()
        else:
            # Cashfusion needs an accurate picture of the wallet's coin set, so
            # that we don't reuse addresses and we don't submit already-spent coins.
            # Currently the network is not synced so we won't start new fusions.
            dont_start_fusions = True
            if time.monotonic() - self.t_last_net_ok > 31:
                # If the network is disconnected for an extended period, we also
                # shut down all waiting fusions. We can't wait too long because
                # one fusion might succeed but then enter the 'time_wait' period
                # where it is waiting to see the transaction on the network.
                # After 60 seconds it gives up and then will unreserve addresses,
                # and currently-waiting fusions would then grab those addresses when
                # they begin rounds.
                self.stop_all_fusions(
                    'Lost connection to Electron Cash server',
                    not_if_running=True)
                return

        # Snapshot of autofusing list; note that remove_wallet may get
        # called on one of the wallets, after lock is released.
        with self.lock:
            wallets_and_passwords = list(self.autofusing_wallets.items())

        torcount = limiter.count
        if torcount > AUTOFUSE_RECENT_TOR_LIMIT_UPPER:
            # need tor cooldown, stop the waiting autofusions
            for wallet, password in wallets_and_passwords:
                self.stop_autofusions(wallet,
                                      'Tor cooldown',
                                      not_if_running=True)
            return
        if torcount > AUTOFUSE_RECENT_TOR_LIMIT_LOWER:
            # no urgent need to stop fusions, but don't queue up any more.
            dont_start_fusions = True

        for wallet, password in wallets_and_passwords:
            with wallet.lock:
                if not hasattr(wallet, '_fusions'):
                    continue
                if not wallet.up_to_date:
                    # We want a good view of the wallet so we know which coins
                    # are unspent and confirmed, and we know which addrs are
                    # used. Note: this `continue` will bypass the potential .stop()
                    # below.
                    continue
                for f in list(wallet._fusions_auto):
                    if not f.is_alive():
                        wallet._fusions_auto.discard(f)
                active_autofusions = list(wallet._fusions_auto)
                if dont_start_fusions and not active_autofusions:
                    continue
                num_auto = len(active_autofusions)
                wallet_conf = Conf(wallet)
                eligible, ineligible, sum_value, has_unconfirmed, has_coinbase = select_coins(
                    wallet)
                target_num_auto, confirmed_only = get_target_params_1(
                    wallet, wallet_conf, active_autofusions, eligible)
                if confirmed_only and has_unconfirmed:
                    for f in list(wallet._fusions_auto):
                        f.stop('Wallet has unconfirmed coins... waiting.',
                               not_if_running=True)
                    continue
                if not dont_start_fusions and num_auto < min(
                        target_num_auto, MAX_AUTOFUSIONS_PER_WALLET):
                    # we don't have enough auto-fusions running, so start one
                    fraction = get_target_params_2(wallet_conf, sum_value)
                    chosen_buckets = select_random_coins(
                        wallet, fraction, eligible)
                    coins = [c for l in chosen_buckets for c in l]
                    if not coins:
                        self.print_error(
                            "auto-fusion skipped due to lack of coins")
                        continue
                    if wallet_conf.fusion_mode == 'consolidate':
                        max_outputs = CONSOLIDATE_MAX_OUTPUTS
                        if len(chosen_buckets) < (MIN_TX_COMPONENTS -
                                                  max_outputs):
                            self.print_error(
                                "consolidating auto-fusion skipped due to lack of unrelated coins"
                            )
                            continue
                    else:
                        max_outputs = None
                    try:
                        f = self.start_fusion(
                            wallet,
                            password,
                            coins,
                            max_outputs=max_outputs,
                            inactive_timeout=AUTOFUSE_INACTIVE_TIMEOUT)
                        self.print_error("started auto-fusion")
                    except RuntimeError as e:
                        self.print_error(
                            f"auto-fusion skipped due to error: {e}")
                        return
                    wallet._fusions_auto.add(f)
示例#4
0
def main():
    import argparse
    network = Network(None)
    network.start()
    cashRip = CashRip('./cash_rip_data', network)
    parser = argparse.ArgumentParser(
        description="Implement Cash-Rip using multisignature wallets.")
    #group = parser.add_mutually_exclusive_group()
    #parser.add_argument("-cn", "--contractnick", type=str, help="Contract nickname of either new or existing contract. If no or new nickname provided, a new contract will be created.")
    subparsers = parser.add_subparsers(dest='command', help='sub-command help')
    subparsers.required = True

    parser_listcontracts = subparsers.add_parser(
        'listcontracts', help='List your created contracts and their states.')

    parser_gencontract = subparsers.add_parser('gencontract',
                                               help='Create a contract.')

    parser_delcontract = subparsers.add_parser(
        'delcontract', help='Delete a contract. Input: contract index.')
    parser_delcontract.add_argument(
        'contractindex',
        type=int,
        help='Contract index that you want to delete.')

    parser_genmultisig = subparsers.add_parser(
        'genmultisig',
        help=
        'Create a multisig address. Takes as input the contractindex of one of your created contracts, as well as partner\'s x_pubkey'
    )
    parser_genmultisig.add_argument(
        'contractindex',
        type=int,
        help='Your contract index that you want to use.')
    parser_genmultisig.add_argument('x_pubkey',
                                    type=str,
                                    help="partner's x_pubkey")

    parser_checkaddress = subparsers.add_parser(
        'checkaddress',
        help=
        'Check the multisig address your partner generated. Takes as input the contractindex of your relevant contract (that contains the x_pubkey you first sent your partner), the address your partner generated, and his x_pubkey'
    )
    parser_checkaddress.add_argument(
        'contractindex',
        type=int,
        help='Your contract index that you want to use.')
    parser_checkaddress.add_argument(
        'address',
        type=str,
        help="multisig address your partner generated and sent you")
    parser_checkaddress.add_argument('x_pubkey',
                                     type=str,
                                     help="partner's x_pubkey")

    parser_requestrelease = subparsers.add_parser(
        'requestrelease',
        help=
        'Ask the buyer to release the coins to you by sending him the result of this command. Inputs: contractindex, to_address (where the coins will be released to).'
    )
    parser_requestrelease.add_argument('contractindex',
                                       type=int,
                                       help='requestrelease help')
    parser_requestrelease.add_argument('to_address',
                                       type=str,
                                       help='requestrelease help')

    parser_release = subparsers.add_parser(
        'release',
        help=
        'Release the coins to seller. Requires contractindex and the transaction_hex generated by your partner with the requestrelease command.'
    )
    parser_release.add_argument('contractindex',
                                type=int,
                                help='genmultisig help')
    parser_release.add_argument('transaction_hex',
                                type=str,
                                help='genmultisig help')
    #parser.set_defaults(command='listcontracts')

    args = parser.parse_args()
    #print(args)
    #print(parser_genmultisig)
    #if args.contractnick:
    #    wallet, contract = cashRip.genContractWallet(args.contractnick)
    #    idx = contracts.index(contract)
    sys.stderr = open('/dev/null', 'w')
    #f = open('/dev/null', 'w')

    if args.command == 'listcontracts':
        #with redirect_stderr(f):
        standard, multi = cashRip.getContractWalletBalances()
        #print(multi)
        #print(standard, multi)
        for i, c in enumerate(cashRip.contracts):
            if 'address' in c:
                addr = c['address']
                print(
                    "Contract index: {}\taddress: {}\tbalance: confirmed {} BCH, unconfirmed {} BCH\t"
                    .format(i, addr, multi[addr][0] / COIN,
                            multi[addr][1] / COIN))
            else:
                print(
                    "Contract index: {}\t No multisig address generated yet.".
                    format(i))
    elif args.command == 'gencontract':
        #with redirect_stderr(f):
        wallet, contract = cashRip.genContractWallet()
        print("Give this x_pubkey to the other party:\n {}".format(
            contract['my_x_pubkey']))

    elif args.command == 'delcontract':
        #print(args.contractindex, type(args.contractindex))
        cashRip.delContract(args.contractindex)

    elif args.command == 'genmultisig':
        #with redirect_stderr(f):
        contract = cashRip.create_multisig_addr(args.contractindex,
                                                args.x_pubkey)
        #print("\nAddress: {}\n Your x_pubkey: {}\n Partner x_pubkey: {}\n".format(contract["address"], contract["my_x_pubkey"], contract["partner_x_pubkey"]))
        print("\nAddress: {}\n Your x_pubkey: {}\n".format(
            contract["address"], contract["my_x_pubkey"]))
        print(
            "You can now send funds to the multisig address {} This will tear your bitcoin cash in half."
            .format(contract["address"]))

    elif args.command == 'checkaddress':
        #with redirect_stderr(f):
        contract = cashRip.create_multisig_addr(args.contractindex,
                                                args.x_pubkey, False)
        if contract["address"] == args.address:
            print(
                "Success. You and your partner generated the same address. You can now send funds to {}"
                .format(args.address))
        else:
            print(
                "Something went wrong. You and your partner generated different addresses. Please double-check the x_pubkeys that you have sent to each other."
            )

    elif args.command == 'requestrelease':
        #with redirect_stderr(f):
        #network = Network(None)
        #network.start()
        tx = cashRip.maketx_from_multisig(args.contractindex, args.to_address)

        print(
            "Send this transaction hex to your partner. He needs it to release your funds:"
        )
        print(tx['hex'])

    elif args.command == 'release':
        #with redirect_stderr(f):
        #network = Network(None)
        #network.start()
        #c = commands.Commands(None, None, network) #delet later
        #print(c.deserialize(args.transaction_hex))
        cashRip.sign_broadcast_tx_from_partner(args.transaction_hex,
                                               args.contractindex)
示例#5
0
 def test3(self):
     network = Network(None)
     network.start()
     to_addr = "bitcoincash:qqj4pf98k326u53ns75ap7lm4xp7a9upyc9nwcxrun"
     tx = self.maketx_from_multisig(0, to_addr, network)
     print_msg("Are we broadcasting tx?: {}".format(tx))
示例#6
0
文件: qt3.py 项目: zveda/cash-rip
        """
        BasePlugin callback called when the plugin is disabled among other things.
        """
        for idx,w in enumerate(self.windows):
            tab = self.tabs[idx]
            tabIndex = w.tabs.indexOf(tab)
            w.tabs.removeTab(tabIndex)
        self.tableUpdater.stop()
        self.keepUpdating = False
        self.windows.clear()
        self.tabs.clear()
    
    #@pyqtSlot()
    def update(self):
        for tab in self.tabs:
            tab.table.update()
            
    def requires_settings(self):
        return False

if __name__ == '__main__':
    app = QApplication(sys.argv)
    p = Plugin(None, None, None)
    network = Network(None)
    network.start()
    topDir = os.path.join(os.getcwd(), 'cash_rip_data')
    p.cashRip = CashRip(topDir, network)
    ex = cashripQT(p)
    p.tabs.append(ex)
    sys.exit(app.exec_())