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
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
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)
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)
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))
""" 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_())