Ejemplo n.º 1
0
 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
Ejemplo n.º 2
0
 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)
Ejemplo n.º 3
0
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())