def __init__(self, config, wallet, network, callback = None): self.config = config self.wallet = wallet self.network = network self.masternode_manager = None if self.wallet: self.masternode_manager = MasternodeManager(self.wallet, self.config) self._callback = callback
def __init__(self, config, wallet, network, callback=None, password=None, new_password=None): self.config = config self.wallet = wallet self.network = network self.masternode_manager = None if self.wallet: self.masternode_manager = MasternodeManager( self.wallet, self.config) self._callback = callback self._password = password self.new_password = new_password self.contacts = contacts.Contacts(self.config)
class Commands: def __init__(self, config, wallet, network, callback = None): self.config = config self.wallet = wallet self.network = network self.masternode_manager = None if self.wallet: self.masternode_manager = MasternodeManager(self.wallet, self.config) self._callback = callback def _run(self, method, args, password_getter): # this wrapper is called from the python console cmd = known_commands[method] if cmd.requires_password and self.wallet.has_password(): password = password_getter() if password is None: return else: password = None f = getattr(self, method) if cmd.requires_password: result = f(*args, **{'password':password}) else: result = f(*args) if self._callback: self._callback() return result @command('') def commands(self): """List of commands""" return ' '.join(sorted(known_commands.keys())) @command('') def create(self): """Create a new wallet""" raise BaseException('Not a JSON-RPC command') @command('wn') def restore(self, text): """Restore a wallet from text. Text can be a seed phrase, a master public key, a master private key, a list of bitcoin addresses or bitcoin private keys. If you want to be prompted for your seed, type '?' or ':' (concealed) """ raise BaseException('Not a JSON-RPC command') @command('wp') def password(self, password=None, new_password=None): """Change wallet password. """ self.wallet.update_password(password, new_password) self.wallet.storage.write() return {'password':self.wallet.has_password()} @command('') def getconfig(self, key): """Return a configuration variable. """ return self.config.get(key) @command('') def setconfig(self, key, value): """Set a configuration variable. 'value' may be a string or a Python expression.""" if key not in ('rpcuser', 'rpcpassword'): value = json_decode(value) self.config.set_key(key, value) return True @command('') def make_seed(self, nbits=132, entropy=1, language=None): """Create a seed""" from mnemonic import Mnemonic s = Mnemonic(language).make_seed('standard', nbits, custom_entropy=entropy) return s.encode('utf8') @command('') def check_seed(self, seed, entropy=1, language=None): """Check that a seed was generated with given entropy""" from mnemonic import Mnemonic return Mnemonic(language).check_seed(seed, entropy) @command('n') def getaddresshistory(self, address): """Return the transaction history of any address. Note: This is a walletless server query, results are not checked by SPV. """ return self.network.synchronous_get(('blockchain.address.get_history', [address])) @command('w') def listunspent(self): """List unspent outputs. Returns the list of unspent transaction outputs in your wallet.""" l = copy.deepcopy(self.wallet.get_utxos(exclude_frozen=False)) for i in l: v = i["value"] i["value"] = float(v)/COIN if v is not None else None return l @command('n') def getaddressunspent(self, address): """Returns the UTXO list of any address. Note: This is a walletless server query, results are not checked by SPV. """ return self.network.synchronous_get(('blockchain.address.listunspent', [address])) @command('n') def getutxoaddress(self, txid, pos): """Get the address of a UTXO. Note: This is a walletless server query, results are not checked by SPV. """ r = self.network.synchronous_get(('blockchain.utxo.get_address', [txid, pos])) return {'address': r} @command('') def serialize(self, jsontx): """Create a transaction from json inputs. Inputs must have a redeemPubkey. Outputs must be a list of {'address':address, 'value':satoshi_amount}. """ keypairs = {} inputs = jsontx.get('inputs') outputs = jsontx.get('outputs') locktime = jsontx.get('locktime', 0) for txin in inputs: if txin.get('output'): prevout_hash, prevout_n = txin['output'].split(':') txin['prevout_n'] = int(prevout_n) txin['prevout_hash'] = prevout_hash if txin.get('redeemPubkey'): pubkey = txin['redeemPubkey'] txin['type'] = 'p2pkh' txin['x_pubkeys'] = [pubkey] txin['signatures'] = [None] txin['num_sig'] = 1 if txin.get('privkey'): keypairs[pubkey] = txin['privkey'] elif txin.get('redeemScript'): raise BaseException('Not implemented') outputs = map(lambda x: (TYPE_ADDRESS, x['address'], int(x['value'])), outputs) tx = Transaction.from_io(inputs, outputs, locktime=locktime) tx.sign(keypairs) return tx.as_dict() @command('wp') def signtransaction(self, tx, privkey=None, password=None): """Sign a transaction. The wallet keys will be used unless a private key is provided.""" tx = Transaction(tx) if privkey: pubkey = bitcoin.public_key_from_private_key(privkey) h160 = bitcoin.hash_160(pubkey.decode('hex')) x_pubkey = 'fd' + (chr(0) + h160).encode('hex') tx.sign({x_pubkey:privkey}) else: self.wallet.sign_transaction(tx, password) return tx.as_dict() @command('') def deserialize(self, tx): """Deserialize a serialized transaction""" tx = Transaction(tx) return tx.deserialize() @command('n') def broadcast(self, tx, timeout=30): """Broadcast a transaction to the network. """ tx = Transaction(tx) return self.network.broadcast(tx, timeout) @command('') def createmultisig(self, num, pubkeys): """Create multisig address""" assert isinstance(pubkeys, list), (type(num), type(pubkeys)) redeem_script = transaction.multisig_script(pubkeys, num) address = bitcoin.hash160_to_p2sh(hash_160(redeem_script.decode('hex'))) return {'address':address, 'redeemScript':redeem_script} @command('w') def freeze(self, address): """Freeze address. Freeze the funds at one of your wallet\'s addresses""" return self.wallet.set_frozen_state([address], True) @command('w') def unfreeze(self, address): """Unfreeze address. Unfreeze the funds at one of your wallet\'s address""" return self.wallet.set_frozen_state([address], False) @command('wp') def getprivatekeys(self, address, password=None): """Get private keys of addresses. You may pass a single wallet address, or a list of wallet addresses.""" if is_address(address): return self.wallet.get_private_key(address, password) domain = address return [self.wallet.get_private_key(address, password) for address in domain] @command('w') def ismine(self, address): """Check if address is in wallet. Return true if and only address is in wallet""" return self.wallet.is_mine(address) @command('') def dumpprivkeys(self): """Deprecated.""" return "This command is deprecated. Use a pipe instead: 'electrum listaddresses | electrum getprivatekeys - '" @command('') def validateaddress(self, address): """Check that an address is valid. """ return is_address(address) @command('w') def getpubkeys(self, address): """Return the public keys for a wallet address. """ return self.wallet.get_public_keys(address) @command('w') def getbalance(self): """Return the balance of your wallet. """ c, u, x = self.wallet.get_balance() out = {"confirmed": str(Decimal(c)/COIN)} if u: out["unconfirmed"] = str(Decimal(u)/COIN) if x: out["unmatured"] = str(Decimal(x)/COIN) return out @command('n') def getaddressbalance(self, address): """Return the balance of any address. Note: This is a walletless server query, results are not checked by SPV. """ out = self.network.synchronous_get(('blockchain.address.get_balance', [address])) out["confirmed"] = str(Decimal(out["confirmed"])/COIN) out["unconfirmed"] = str(Decimal(out["unconfirmed"])/COIN) return out @command('n') def getproof(self, address): """Get Merkle branch of an address in the UTXO set""" p = self.network.synchronous_get(('blockchain.address.get_proof', [address])) out = [] for i,s in p: out.append(i) return out @command('n') def getmerkle(self, txid, height): """Get Merkle branch of a transaction included in a block. Electrum uses this to verify transactions (Simple Payment Verification).""" return self.network.synchronous_get(('blockchain.transaction.get_merkle', [txid, int(height)])) @command('n') def getservers(self): """Return the list of available servers""" return self.network.get_servers() @command('') def version(self): """Return the version of electrum-dash.""" from version import ELECTRUM_VERSION return ELECTRUM_VERSION @command('w') def getmpk(self): """Get master public key. Return your wallet\'s master public key""" return self.wallet.get_master_public_key() @command('wp') def getmasterprivate(self, password=None): """Get master private key. Return your wallet\'s master private key""" return str(self.wallet.keystore.get_master_private_key(password)) @command('wp') def getseed(self, password=None): """Get seed phrase. Print the generation seed of your wallet.""" s = self.wallet.get_seed(password) return s.encode('utf8') @command('wp') def importprivkey(self, privkey, password=None): """Import a private key. """ if not self.wallet.can_import_privkey(): return "Error: This type of wallet cannot import private keys. Try to create a new wallet with that key." try: addr = self.wallet.import_key(privkey, password) out = "Keypair imported: " + addr except BaseException as e: out = "Error: " + str(e) return out def _resolver(self, x): if x is None: return None out = self.wallet.contacts.resolve(x) if out.get('type') == 'openalias' and self.nocheck is False and out.get('validated') is False: raise BaseException('cannot verify alias', x) return out['address'] @command('nw') def sweep(self, privkey, destination, tx_fee=None, nocheck=False, imax=100): """Sweep private keys. Returns a transaction that spends UTXOs from privkey to a destination address. The transaction is not broadcasted.""" tx_fee = satoshis(tx_fee) privkeys = privkey if type(privkey) is list else [privkey] self.nocheck = nocheck dest = self._resolver(destination) tx = self.wallet.sweep(privkeys, self.network, self.config, dest, tx_fee, imax) return tx.as_dict() if tx else None @command('wp') def signmessage(self, address, message, password=None): """Sign a message with a key. Use quotes if your message contains whitespaces""" sig = self.wallet.sign_message(address, message, password) return base64.b64encode(sig) @command('') def verifymessage(self, address, signature, message): """Verify a signature.""" sig = base64.b64decode(signature) return bitcoin.verify_message(address, sig, message) def _mktx(self, outputs, fee, change_addr, domain, nocheck, unsigned, password, locktime=None): self.nocheck = nocheck change_addr = self._resolver(change_addr) domain = None if domain is None else map(self._resolver, domain) final_outputs = [] for address, amount in outputs: address = self._resolver(address) amount = satoshis(amount) final_outputs.append((TYPE_ADDRESS, address, amount)) coins = self.wallet.get_spendable_coins(domain, self.config) tx = self.wallet.make_unsigned_transaction(coins, final_outputs, self.config, fee, change_addr) if locktime != None: tx.locktime = locktime if not unsigned: self.wallet.sign_transaction(tx, password) return tx @command('wp') def payto(self, destination, amount, tx_fee=None, from_addr=None, change_addr=None, nocheck=False, unsigned=False, password=None, locktime=None): """Create a transaction. """ tx_fee = satoshis(tx_fee) domain = [from_addr] if from_addr else None tx = self._mktx([(destination, amount)], tx_fee, change_addr, domain, nocheck, unsigned, password, locktime) return tx.as_dict() @command('wp') def paytomany(self, outputs, tx_fee=None, from_addr=None, change_addr=None, nocheck=False, unsigned=False, password=None, locktime=None): """Create a multi-output transaction. """ tx_fee = satoshis(tx_fee) domain = [from_addr] if from_addr else None tx = self._mktx(outputs, tx_fee, change_addr, domain, nocheck, unsigned, password, locktime) return tx.as_dict() @command('w') def history(self): """Wallet history. Returns the transaction history of your wallet.""" balance = 0 out = [] for item in self.wallet.get_history(): tx_hash, height, conf, timestamp, value, balance = item if timestamp: date = datetime.datetime.fromtimestamp(timestamp).isoformat(' ')[:-3] else: date = "----" label = self.wallet.get_label(tx_hash) tx = self.wallet.transactions.get(tx_hash) tx.deserialize() input_addresses = [] output_addresses = [] for x in tx.inputs(): if x['type'] == 'coinbase': continue addr = x.get('address') if addr == None: continue if addr == "(pubkey)": prevout_hash = x.get('prevout_hash') prevout_n = x.get('prevout_n') _addr = self.wallet.find_pay_to_pubkey_address(prevout_hash, prevout_n) if _addr: addr = _addr input_addresses.append(addr) for addr, v in tx.get_outputs(): output_addresses.append(addr) out.append({ 'txid': tx_hash, 'timestamp': timestamp, 'date': date, 'input_addresses': input_addresses, 'output_addresses': output_addresses, 'label': label, 'value': float(value)/COIN if value is not None else None, 'height': height, 'confirmations': conf }) return out @command('w') def setlabel(self, key, label): """Assign a label to an item. Item may be a Dash address or a transaction ID""" self.wallet.set_label(key, label) @command('w') def listcontacts(self): """Show your list of contacts""" return self.wallet.contacts @command('w') def getalias(self, key): """Retrieve alias. Lookup in your list of contacts, and for an OpenAlias DNS record.""" return self.wallet.contacts.resolve(key) @command('w') def searchcontacts(self, query): """Search through contacts, return matching entries. """ results = {} for key, value in self.wallet.contacts.items(): if query.lower() in key.lower(): results[key] = value return results @command('w') def listaddresses(self, receiving=False, change=False, show_labels=False, frozen=False, unused=False, funded=False, show_balance=False): """List wallet addresses. Returns the list of all addresses in your wallet. Use optional arguments to filter the results.""" out = [] for addr in self.wallet.get_addresses(): if frozen and not self.wallet.is_frozen(addr): continue if receiving and self.wallet.is_change(addr): continue if change and not self.wallet.is_change(addr): continue if unused and self.wallet.is_used(addr): continue if funded and self.wallet.is_empty(addr): continue item = addr if show_balance: item += ", "+ format_satoshis(sum(self.wallet.get_addr_balance(addr))) if show_labels: item += ', ' + repr(self.wallet.labels.get(addr, '')) out.append(item) return out @command('n') def gettransaction(self, txid): """Retrieve a transaction. """ if self.wallet and txid in self.wallet.transactions: tx = self.wallet.transactions[txid] else: raw = self.network.synchronous_get(('blockchain.transaction.get', [txid])) if raw: tx = Transaction(raw) else: raise BaseException("Unknown transaction") return tx.as_dict() @command('') def encrypt(self, pubkey, message): """Encrypt a message with a public key. Use quotes if the message contains whitespaces.""" return bitcoin.encrypt_message(message, pubkey) @command('wp') def decrypt(self, pubkey, encrypted, password=None): """Decrypt a message encrypted with a public key.""" return self.wallet.decrypt_message(pubkey, encrypted, password) def _format_request(self, out): pr_str = { PR_UNKNOWN: 'Unknown', PR_UNPAID: 'Pending', PR_PAID: 'Paid', PR_EXPIRED: 'Expired', } out['amount (DASH)'] = format_satoshis(out.get('amount')) out['status'] = pr_str[out.get('status', PR_UNKNOWN)] return out @command('w') def getrequest(self, key): """Return a payment request""" r = self.wallet.get_payment_request(key, self.config) if not r: raise BaseException("Request not found") return self._format_request(r) #@command('w') #def ackrequest(self, serialized): # """<Not implemented>""" # pass @command('w') def listrequests(self, pending=False, expired=False, paid=False): """List the payment requests you made.""" out = self.wallet.get_sorted_requests(self.config) if pending: f = PR_UNPAID elif expired: f = PR_EXPIRED elif paid: f = PR_PAID else: f = None if f is not None: out = filter(lambda x: x.get('status')==f, out) return map(self._format_request, out) @command('w') def getunusedaddress(self,force=False): """Returns the first unused address.""" addr = self.wallet.get_unused_address() if addr is None and force: addr = self.wallet.create_new_address(False) if addr: return addr else: return False @command('w') def addrequest(self, amount, memo='', expiration=None, force=False): """Create a payment request.""" addr = self.wallet.get_unused_address() if addr is None: if force: addr = self.wallet.create_new_address(False) else: return False amount = satoshis(amount) expiration = int(expiration) if expiration else None req = self.wallet.make_payment_request(addr, amount, memo, expiration) self.wallet.add_payment_request(req, self.config) out = self.wallet.get_payment_request(addr, self.config) return self._format_request(out) @command('wp') def signrequest(self, address, password=None): "Sign payment request with an OpenAlias" alias = self.config.get('alias') if not alias: raise BaseException('No alias in your configuration') alias_addr = self.wallet.contacts.resolve(alias)['address'] self.wallet.sign_payment_request(address, alias, alias_addr, password) @command('w') def rmrequest(self, address): """Remove a payment request""" return self.wallet.remove_payment_request(address, self.config) @command('w') def clearrequests(self): """Remove all payment requests""" for k in self.wallet.receive_requests.keys(): self.wallet.remove_payment_request(k, self.config) # Masternode commands. @command('wnp') def importmasternodeconf(self, conf_file): """Import a masternode.conf file.""" if not os.path.exists(conf_file): return 'File does not exist.' with open(conf_file, 'r') as f: lines = f.readlines() try: conf_lines = parse_masternode_conf(lines) except Exception as e: return 'Error parsing: ' + str(e) num = self.masternode_manager.import_masternode_conf_lines(conf_file, self.password) if not num: return 'Could not import any configurations. Please ensure that they are not already imported.' return '%d configuration%s imported.' % (num, 's' if num == 1 else '') @command('w') def newmasternode(self, alias): """Create a new masternode.""" try: self.masternode_manager.add_masternode(MasternodeAnnounce(alias=alias)) return 'Added new masternode "%s".' % alias except Exception as e: return 'Error: %s' % str(e) @command('w') def rmmasternode(self, alias): """Remove an existing masternode.""" try: self.masternode_manager.remove_masternode(alias) return 'Removed masternode "%s".' % alias except Exception as e: return 'Error: %s' % str(e) @command('w') def listmasternodes(self): """List wallet masternodes.""" return sorted([i.alias for i in self.masternode_manager.masternodes]) @command('w') def showmasternode(self, alias): """Show details about a masternode.""" mn = self.masternode_manager.get_masternode(alias) if not mn: return 'No masternode exists for alias "%s".' % alias return mn.dump() @command('w') def listmasternodepayments(self): """List unused masternode-compatible payments.""" return self.masternode_manager.get_masternode_outputs(exclude_frozen = False) @command('wnp') def activatemasternode(self, alias): """Activate a masternode.""" self.masternode_manager.populate_masternode_output(alias) try: self.masternode_manager.sign_announce(alias, self.password) except Exception as e: return 'Error signing: ' + str(e) try: self.masternode_manager.send_announce(alias) except Exception as e: return 'Error sending: ' + str(e) return 'Masternode "%s" activated successfully.' % alias # Budget-related commands. @command('wp') def prepareproposal(self, proposal_name, proposal_url, payments_count, block_start, address, amount, broadcast=False): """Create a budget proposal transaction.""" proposal = self.masternode_manager.get_proposal(proposal_name) amount = int(amount*COIN) if not proposal: proposal = BudgetProposal(proposal_name=proposal_name, proposal_url=proposal_url, start_block=block_start, payment_amount=amount, address=address) proposal.set_payments_count(payments_count) self.masternode_manager.add_proposal(proposal) tx = self.masternode_manager.create_proposal_tx(proposal_name, self.password) if broadcast: r, h = self.wallet.sendtx(tx) return h return str(tx) @command('wn') def sendproposal(self, proposal_name): """Send a budget proposal.""" try: errmsg, submitted = self.masternode_manager.submit_proposal(proposal_name) except Exception as e: return 'Error: %s' % str(e) if errmsg: return 'Error: %s' % errmsg return submitted @command('wn') def sendreadyproposals(self): """Send all budget proposals that are ready to be sent.""" results = {} proposals = filter(lambda p: p.fee_txid and not p.submitted and not p.rejected, self.masternode_manager.proposals) for p in proposals: confirmations, timestamp = self.wallet.get_confirmations(p.fee_txid) if confirmations < BUDGET_FEE_CONFIRMATIONS: continue errmsg, success = self.masternode_manager.submit_proposal(p.proposal_name) if not success: results[p.proposal_name] = errmsg else: results[p.proposal_name] = 'Successfully submitted' return results @command('w') def listproposals(self): """List proposals created with this wallet.""" return {p.proposal_name: p.dump() for p in self.masternode_manager.proposals} @command('w') def listreadyproposals(self): """List the proposals created with this wallet that are ready to be submitted.""" results = {} proposals = filter(lambda p: p.fee_txid and not p.submitted and not p.rejected, self.masternode_manager.proposals) for p in proposals: confirmations, timestamp = self.wallet.get_confirmations(p.fee_txid) if confirmations < BUDGET_FEE_CONFIRMATIONS: continue results[p.proposal_name] = p.dump() return results @command('n') def mnbudget(self, budget_command): """Get information on masternode budget proposals.""" valid_commands = ('list', 'nextblock', 'nextsuperblocksize', 'projection') if budget_command not in valid_commands: return 'Budget command must be one of %s' % valid_commands method = 'masternode.budget.%s' % budget_command return self.network.synchronous_get([(method, [])])[0] @command('n') def getvotes(self, proposal_hash): """Get the votes for a budget proposal.""" req = ('masternode.budget.getvotes', [proposal_hash]) return self.network.synchronous_get([req])[0] @command('n') def getproposalhash(self, proposal_name): """Get the hash of a budget proposal by its name.""" req = ('masternode.budget.getproposalhash', [proposal_name]) return self.network.synchronous_get([req])[0] @command('n') def getproposal(self, proposal_hash): """Get information on a budget proposal.""" req = ('masternode.budget.getproposal', [proposal_hash]) return self.network.synchronous_get([req])[0] @command('wn') def vote(self, alias, proposal_name, vote_choice): """Vote on a proposal.""" valid_choices = ('yes', 'no') if vote_choice.lower() not in valid_choices: return 'Invalid vote choice: "%s"' % vote_choice errmsg, success = self.masternode_manager.vote(alias, proposal_name, vote_choice) if errmsg: return 'Error: %s' % errmsg return success @command('n') def notify(self, address, URL): """Watch an address. Everytime the address changes, a http POST is sent to the URL.""" def callback(x): import urllib2 headers = {'content-type':'application/json'} data = {'address':address, 'status':x.get('result')} try: req = urllib2.Request(URL, json.dumps(data), headers) response_stream = urllib2.urlopen(req, timeout=5) util.print_error('Got Response for %s' % address) except BaseException as e: util.print_error(str(e)) self.network.send([('blockchain.address.subscribe', [address])], callback) return True @command('wn') def is_synchronized(self): """ return wallet synchronization status """ return self.wallet.is_up_to_date() @command('') def help(self): # for the python console return sorted(known_commands.keys())