diff --git a/.DS_Store b/.DS_Store
new file mode 100644
index 00000000..e29464d9
Binary files /dev/null and b/.DS_Store differ
diff --git a/.gitignore b/.gitignore
index d8994c6e..41b88eee 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,4 +8,4 @@ local*.cfg
 build/
 htmlcov/
 python_bitcoinlib.egg-info/
-dist/
+dist/
\ No newline at end of file
diff --git a/bitcoin/core/__init__.py b/bitcoin/core/__init__.py
index 272bec5c..08ca1842 100644
--- a/bitcoin/core/__init__.py
+++ b/bitcoin/core/__init__.py
@@ -308,7 +308,7 @@ class CMutableTxOut(CTxOut):
 
     @classmethod
     def from_txout(cls, txout):
-        """Create a fullly mutable copy of an existing TxOut"""
+        """Create a fully mutable copy of an existing TxOut"""
         return cls(txout.nValue, txout.scriptPubKey)
 
 
diff --git a/bitcoin/core/script.py b/bitcoin/core/script.py
index 46b83bd7..a8449a3b 100644
--- a/bitcoin/core/script.py
+++ b/bitcoin/core/script.py
@@ -375,12 +375,14 @@ def __new__(cls, n):
 
 OPCODES_BY_NAME = {
     'OP_0': OP_0,
+    'OP_FALSE': OP_0,
     'OP_PUSHDATA1': OP_PUSHDATA1,
     'OP_PUSHDATA2': OP_PUSHDATA2,
     'OP_PUSHDATA4': OP_PUSHDATA4,
     'OP_1NEGATE': OP_1NEGATE,
     'OP_RESERVED': OP_RESERVED,
     'OP_1': OP_1,
+    'OP_TRUE': OP_1,
     'OP_2': OP_2,
     'OP_3': OP_3,
     'OP_4': OP_4,
diff --git a/bitcoin/rpc.py b/bitcoin/rpc.py
index 51f24ac9..fad62328 100644
--- a/bitcoin/rpc.py
+++ b/bitcoin/rpc.py
@@ -37,15 +37,21 @@
 import os
 import platform
 import sys
+import warnings
 try:
     import urllib.parse as urlparse
 except ImportError:
     import urlparse
+if sys.version > '3':
+    from io import BytesIO as _BytesIO
+else:
+    from cStringIO import StringIO as _BytesIO
 
 import bitcoin
-from bitcoin.core import COIN, x, lx, b2lx, CBlock, CBlockHeader, CTransaction, COutPoint, CTxOut
+from bitcoin.core import COIN, x, lx, b2lx, CBlock, CBlockHeader, CTransaction, COutPoint, CTxOut, CTxIn
 from bitcoin.core.script import CScript
-from bitcoin.wallet import CBitcoinAddress, CBitcoinSecret
+from bitcoin.wallet import CBitcoinAddress, CBitcoinSecret, CBitcoinAddressError
+from bitcoin.core.key import CPubKey
 
 DEFAULT_USER_AGENT = "AuthServiceProxy/0.1"
 
@@ -115,6 +121,8 @@ class InWarmupError(JSONRPCError):
     RPC_ERROR_CODE = -28
 
 
+
+
 class BaseProxy(object):
     """Base JSON-RPC proxy class. Contains only private methods; do not use
     directly."""
@@ -360,84 +368,10 @@ def call(self, service_name, *args):
         """Call an RPC method by name and raw (JSON encodable) arguments"""
         return self._call(service_name, *args)
 
-    def dumpprivkey(self, addr):
-        """Return the private key matching an address
-        """
-        r = self._call('dumpprivkey', str(addr))
-
-        return CBitcoinSecret(r)
-
-    def fundrawtransaction(self, tx, include_watching=False):
-        """Add inputs to a transaction until it has enough in value to meet its out value.
-
-        include_watching - Also select inputs which are watch only
-
-        Returns dict:
-
-        {'tx':        Resulting tx,
-         'fee':       Fee the resulting transaction pays,
-         'changepos': Position of added change output, or -1,
-        }
-        """
-        hextx = hexlify(tx.serialize())
-        r = self._call('fundrawtransaction', hextx, include_watching)
-
-        r['tx'] = CTransaction.deserialize(unhexlify(r['hex']))
-        del r['hex']
-
-        r['fee'] = int(r['fee'] * COIN)
-
-        return r
-
-    def generate(self, numblocks):
-        """
-        DEPRECATED (will be removed in bitcoin-core v0.19)
-        
-        Mine blocks immediately (before the RPC call returns)
-
-        numblocks - How many blocks are generated immediately.
-
-        Returns iterable of block hashes generated.
-        """
-        r = self._call('generate', numblocks)
-        return (lx(blk_hash) for blk_hash in r)
-    
-    def generatetoaddress(self, numblocks, addr):
-        """Mine blocks immediately (before the RPC call returns) and
-        allocate block reward to passed address. Replaces deprecated 
-        "generate(self,numblocks)" method.
-
-        numblocks - How many blocks are generated immediately.
-        addr     - Address to receive block reward (CBitcoinAddress instance)
-
-        Returns iterable of block hashes generated.
-        """
-        r = self._call('generatetoaddress', numblocks, str(addr))
-        return (lx(blk_hash) for blk_hash in r)
-
-    def getaccountaddress(self, account=None):
-        """Return the current Bitcoin address for receiving payments to this
-        account."""
-        r = self._call('getaccountaddress', account)
-        return CBitcoinAddress(r)
-
-    def getbalance(self, account='*', minconf=1, include_watchonly=False):
-        """Get the balance
-
-        account - The selected account. Defaults to "*" for entire wallet. It
-        may be the default account using "".
-
-        minconf - Only include transactions confirmed at least this many times.
-        (default=1)
-
-        include_watchonly - Also include balance in watch-only addresses (see 'importaddress')
-        (default=False)
-        """
-        r = self._call('getbalance', account, minconf, include_watchonly)
-        return int(r*COIN)
+    # == Blockchain ==
 
     def getbestblockhash(self):
-        """Return hash of best (tip) block in longest block chain."""
+        """Return hash of best block in longest block chain."""
         return lx(self._call('getbestblockhash'))
 
     def getblockheader(self, block_hash, verbose=False):
@@ -449,11 +383,12 @@ def getblockheader(self, block_hash, verbose=False):
 
         Raises IndexError if block_hash is not valid.
         """
-        try:
-            block_hash = b2lx(block_hash)
-        except TypeError:
-            raise TypeError('%s.getblockheader(): block_hash must be bytes; got %r instance' %
-                    (self.__class__.__name__, block_hash.__class__))
+        if not isinstance(block_hash, str):
+            try:
+                block_hash = b2lx(block_hash)
+            except TypeError:
+                raise TypeError('%s.getblockheader(): block_hash must be bytes or str; got %r instance' %
+                        (self.__class__.__name__, block_hash.__class__))
         try:
             r = self._call('getblockheader', block_hash, verbose)
         except InvalidAddressOrKeyError as ex:
@@ -472,17 +407,45 @@ def getblockheader(self, block_hash, verbose=False):
         else:
             return CBlockHeader.deserialize(unhexlify(r))
 
+    def getblockchaininfo(self):
+        """Return a JSON object containing blockchaininfo"""
+        return self._call('getblockchaininfo')
+
+    #Untested. Coudln't find valid filter_type? 
+    def getblockfilter(self, block_hash, filter_type="basic"):
+        """
+        Return a JSON object containing filter data and header data
+        Default filter_type must be changed
+        #UNTESTED
+        """
+        if not isinstance(block_hash, str):
+            try:
+                block_hash = b2lx(block_hash)
+            except TypeError:
+                raise TypeError('%s.getblock(): block_hash must be bytes; got %r instance' %
+                        (self.__class__.__name__, block_hash.__class__))
+
+        try:
+            r = self._call('getblockfilter', block_hash, filter_type)
+        except InvalidAddressOrKeyError as ex:
+            raise IndexError('%s.getblockfilter(): %s (%d)' %
+                    (self.__class__.__name__, ex.error['message'], ex.error['code']))
+        except JSONRPCError as ex:
+            raise IndexError('%s.getblockfilter(): %s (%d)' %
+                    (self.__class__.__name__, ex.error['message'], ex.error['code']))
+        return r
 
     def getblock(self, block_hash):
         """Get block <block_hash>
 
         Raises IndexError if block_hash is not valid.
         """
-        try:
-            block_hash = b2lx(block_hash)
-        except TypeError:
-            raise TypeError('%s.getblock(): block_hash must be bytes; got %r instance' %
-                    (self.__class__.__name__, block_hash.__class__))
+        if not isinstance(block_hash, str):
+            try:
+                block_hash = b2lx(block_hash)
+            except TypeError:
+                raise TypeError('%s.getblock(): block_hash must be bytes or str; got %r instance' %
+                        (self.__class__.__name__, block_hash.__class__))
         try:
             # With this change ( https://github.com/bitcoin/bitcoin/commit/96c850c20913b191cff9f66fedbb68812b1a41ea#diff-a0c8f511d90e83aa9b5857e819ced344 ),
             # bitcoin core's rpc takes 0/1/2 instead of true/false as the 2nd argument which specifies verbosity, since v0.15.0.
@@ -508,41 +471,82 @@ def getblockhash(self, height):
             raise IndexError('%s.getblockhash(): %s (%d)' %
                     (self.__class__.__name__, ex.error['message'], ex.error['code']))
 
-    def getinfo(self):
-        """Return a JSON object containing various state info"""
-        r = self._call('getinfo')
-        if 'balance' in r:
-            r['balance'] = int(r['balance'] * COIN)
-        if 'paytxfee' in r:
-            r['paytxfee'] = int(r['paytxfee'] * COIN)
+    def getblockstats(self, hash_or_height, *args):
+        # On clients before PR #17831, passing hash as bytes will result in Block not found
+        """Return a JSON object containing block stats"""
+
+        if isinstance(hash_or_height, bytes): 
+            hval = b2lx(hash_or_height)
+        else: #int or str of block_hash or height
+            hval = hash_or_height
+        try:
+            r = self._call('getblockstats', hval, args)
+        except (InvalidAddressOrKeyError, InvalidParameterError) as ex:
+            raise IndexError('%s.getblockstats(): %s (%d)' %
+                    (self.__class__.__name__, ex.error['message'], ex.error['code']))
         return r
 
-    def getmininginfo(self):
-        """Return a JSON object containing mining-related information"""
-        return self._call('getmininginfo')
+    def getchaintips(self):
+        """Returns JSON object with info on all current tips:"""
+        return self._call('getchaintips')
 
-    def getnewaddress(self, account=None):
-        """Return a new Bitcoin address for receiving payments.
+    def getchaintxstats(self, nblocks=None, block_hash=None):
+        """Compute stats about transactions in chain"""
+        if block_hash is not None:
+            if not isinstance(block_hash, str):
+                block_hash = b2lx(block_hash)
+        return self._call('getchaintxstats', nblocks, block_hash)
 
-        If account is not None, it is added to the address book so payments
-        received with the address will be credited to account.
-        """
-        r = None
-        if account is not None:
-            r = self._call('getnewaddress', account)
-        else:
-            r = self._call('getnewaddress')
+    def getdifficulty(self):
+        return self._call('getdifficulty')
 
-        return CBitcoinAddress(r)
+    def getmempoolancestors(self, txid, verbose=False):
+        """Returns a list of txids for ancestor transactions"""
+        if not isinstance(txid, str):
+            try:
+                txid = b2lx(txid)
+            except TypeError:
+                raise TypeError("%s.getmempoolancestors(): txid must be bytes or str")
+        try:
+            r = self._call('getmempoolancestors', txid, verbose)
+        except InvalidAddressOrKeyError as ex:
+            raise IndexError('%s.getmempoolancestors(): %s (%d)' %
+                    (self.__class__.__name__, ex.error['message'], ex.error['code']))
+        return r
 
-    def getrawchangeaddress(self):
-        """Returns a new Bitcoin address, for receiving change.
+    def getmempooldescendants(self, txid, verbose=False):
+        """Returns a list of txids for descendant transactions"""
+        if not isinstance(txid, str):
+            try:
+                txid = b2lx(txid)
+            except TypeError:
+                raise TypeError("%s.getmempooldescendants(): txid must be bytes or str")
+        try:
+            r = self._call('getmempooldescendants', txid, verbose)
+        except InvalidAddressOrKeyError as ex:
+            raise IndexError('%s.getmempooldescendants(): %s (%d)' %
+                    (self.__class__.__name__, ex.error['message'], ex.error['code']))
+        return r
 
-        This is for use with raw transactions, NOT normal use.
-        """
-        r = self._call('getrawchangeaddress')
-        return CBitcoinAddress(r)
+    def getmempoolentry(self, txid):
+        """Returns a JSON object for mempool transaction"""
+        if not isinstance(txid, str):
+            try:
+                txid = b2lx(txid)
+            except TypeError:
+                raise TypeError("%s.getmempoolentry(): txid must be bytes or str")
+        try:
+            r = self._call('getmempoolentry', txid)
+        except InvalidAddressOrKeyError as ex:
+            raise IndexError('%s.getmempoolentry(): %s (%d)' %
+                    (self.__class__.__name__, ex.error['message'], ex.error['code']))
+        return r
+
+    def getmempoolinfo(self):
+        """Returns a JSON object of mempool info"""
+        return self._call('getmempoolinfo')
 
+    #Untested
     def getrawmempool(self, verbose=False):
         """Return the mempool"""
         if verbose:
@@ -553,111 +557,932 @@ def getrawmempool(self, verbose=False):
             r = [lx(txid) for txid in r]
             return r
 
-    def getrawtransaction(self, txid, verbose=False):
-        """Return transaction with hash txid
-
-        Raises IndexError if transaction not found.
-
-        verbose - If true a dict is returned instead with additional
-        information on the transaction.
+    def gettxout(self, outpoint, includemempool=True):
+        """Return details about an unspent transaction output.
+        outpoint - COutPoint or tuple (<txid>, n)
+        Raises IndexError if outpoint is not found or was spent.
 
-        Note that if all txouts are spent and the transaction index is not
-        enabled the transaction may not be available.
+        includemempool - Include mempool txouts
         """
-        try:
-            r = self._call('getrawtransaction', b2lx(txid), 1 if verbose else 0)
-        except InvalidAddressOrKeyError as ex:
-            raise IndexError('%s.getrawtransaction(): %s (%d)' %
-                    (self.__class__.__name__, ex.error['message'], ex.error['code']))
-        if verbose:
-            r['tx'] = CTransaction.deserialize(unhexlify(r['hex']))
-            del r['hex']
-            del r['txid']
-            del r['version']
-            del r['locktime']
-            del r['vin']
-            del r['vout']
-            r['blockhash'] = lx(r['blockhash']) if 'blockhash' in r else None
+        if isinstance(outpoint, COutPoint): 
+            r = self._call('gettxout', b2lx(outpoint.hash), outpoint.n, includemempool)
         else:
-            r = CTransaction.deserialize(unhexlify(r))
+            r = self._call('gettxout', outpoint[0], outpoint[1], includemempool)
+        if r is None:
+            raise IndexError('%s.gettxout(): unspent txout %r not found' % (self.__class__.__name__, outpoint))
 
+        r['txout'] = CTxOut(int(r['value'] * COIN),
+                            CScript(unhexlify(r['scriptPubKey']['hex'])))
+        del r['value']
+        del r['scriptPubKey']
+        r['bestblock'] = lx(r['bestblock'])
         return r
 
-    def getreceivedbyaddress(self, addr, minconf=1):
-        """Return total amount received by given a (wallet) address
+    def gettxoutproof(self, txids, block_hash=None):
+        """Returns a hex string object of proof of inclusion in block"""
+        if not isinstance(txids[0], str):
+            txids = [b2lx(txid) for txid in txids]
+        if not isinstance(block_hash, str):
+            block_hash = b2lx(block_hash)
+        return self._call('gettxoutproof', txids, block_hash)
 
-        Get the amount received by <address> in transactions with at least
-        [minconf] confirmations.
+    def gettxoutsetinfo(self):
+        """Returns JSON object about utxo set"""
+        # This call will probably time out on a mediocre machine
+        return self._call('gettxoutsetinfo')
 
-        Works only for addresses in the local wallet; other addresses will
-        always show zero.
+    #Untested
+    def preciousblock(self, block_hash):
+        """Marks a block as precious. No return"""
+        if not isinstance(block_hash, str):
+            block_hash = b2lx(block_hash)
+        self._call('preciousblock', block_hash)
+
+    def pruneblockchain(self, height):
+        """Prune blockchain to height. No return"""
+        self._call('pruneblockchain', height)
+
+    #Untested
+    def savemempool(self):
+        """Save full mempool to disk. Will fail until
+        Previous dump is loaded."""
+        self._call('savemempool')
+
+    def scantxoutset(self, action, objects):
+        """Scans current utxo set
+        Actions: "start", "abort", "status"
+        objects: 
+        (json array, required) Array of scan objects
+        Every scan object is either a string descriptor or an object
+        """
+        return self._call('scantxoutset', action, objects)
 
-        addr    - The address. (CBitcoinAddress instance)
+    def verifychain(self, checklevel=3, nblocks=6):
+        """Returns a bool upon verifying chain 
+        Checklevel - thoroughness of verification (0-4)
+        nblocks - number of blocks to check (0=all)
+        """
+        return self._call('verifychain', checklevel, nblocks)
 
-        minconf - Only include transactions confirmed at least this many times.
-        (default=1)
+    def verifytxoutproof(self, proof):
+        """Verifies txoutproof.
+        returns txid if verified
+        returns [] on fail
         """
-        r = self._call('getreceivedbyaddress', str(addr), minconf)
-        return int(r * COIN)
+        #Had several timeouts on this function. Might be natural
+        if not isinstance(proof,str):
+            proof = proof.hex()
+        r = self._call('verifytxoutproof', proof)
+        return [lx(txid) for txid in r]
+
+    # == Control ==
+    def getmemoryinfo(self, mode=None):
+        """Returns a JSON object of memory usage stats:
+        Modes: "stats", "mallocinfo"""
+        return self._call('getmemoryinfo', mode)
+
+    def getrpcinfo(self):
+        """Returns a JSON object of rpc info"""
+        return self._call('getrpcinfo')
+
+    def help(self, command=""):
+        """Return Help Text"""
+        return self._call('help', command)
+
+    #Breaks connection with node. Bitcoin Core still thinks it's
+    #Running but all commands (from this client and from cmd line)
+    #stop working
+    # def stop(self):
+    #     """Stops bitcoind"""
+    #     self._call('stop')
+    
+    def uptime(self):
+        """Returns int of uptime"""
+        return self._call('uptime')
 
-    def gettransaction(self, txid):
-        """Get detailed information about in-wallet transaction txid
+    def logging(self, includes=None, excludes=None):
+        """Returns a JSON object of log info"""
+        return self._call('logging', includes, excludes)
 
-        Raises IndexError if transaction not found in the wallet.
+    # == Generating ==
+    def generate(self, numblocks):
+        """
+        DEPRECATED (will be removed in bitcoin-core v0.19)
+        
+        Mine blocks immediately (before the RPC call returns)
 
-        FIXME: Returned data types are not yet converted.
+        numblocks - How many blocks are generated immediately.
+
+        Returns iterable of block hashes generated.
         """
-        try:
-            r = self._call('gettransaction', b2lx(txid))
-        except InvalidAddressOrKeyError as ex:
-            raise IndexError('%s.getrawtransaction(): %s (%d)' %
-                    (self.__class__.__name__, ex.error['message'], ex.error['code']))
-        return r
+        r = self._call('generate', numblocks)
+        return (lx(blk_hash) for blk_hash in r)
 
-    def gettxout(self, outpoint, includemempool=True):
-        """Return details about an unspent transaction output.
+    def generatetoaddress(self, numblocks, addr):
+        """Mine blocks immediately (before the RPC call returns) and
+        allocate block reward to passed address. Replaces deprecated 
+        "generate(self,numblocks)" method.
 
-        Raises IndexError if outpoint is not found or was spent.
+        numblocks - How many blocks are generated immediately.
+        addr     - Address to receive block reward (CBitcoinAddress instance)
 
-        includemempool - Include mempool txouts
+        Returns iterable of block hashes generated.
         """
-        r = self._call('gettxout', b2lx(outpoint.hash), outpoint.n, includemempool)
+        r = self._call('generatetoaddress', numblocks, str(addr))
+        return (lx(blk_hash) for blk_hash in r)
 
-        if r is None:
-            raise IndexError('%s.gettxout(): unspent txout %r not found' % (self.__class__.__name__, outpoint))
+    # == Mining ==
+    # ALL MINING untested
+    def getblocktemplate(self, template_request=None):
+        """Returns a JSON object for a blocktemplate with which to mine:
+        template_request:
+        {
+           "mode": "str",       (string, optional) This must be set to "template", "proposal" (see BIP 23), or omitted
+           "capabilities": [    (json array, optional) A list of strings
+             "support",         (string) client side supported feature, 'longpoll', 'coinbasetxn', 'coinbasevalue', 'proposal', 'serverlist', 'workid'
+             ...
+           ],
+           "rules": [           (json array, required) A list of strings
+             "support",         (string) client side supported softfork deployment
+             ...
+           ],
+        }
+        Result: JSON
 
-        r['txout'] = CTxOut(int(r['value'] * COIN),
-                            CScript(unhexlify(r['scriptPubKey']['hex'])))
-        del r['value']
-        del r['scriptPubKey']
-        r['bestblock'] = lx(r['bestblock'])
-        return r
+        """
+        return self._call('getblocktemplate', template_request)
 
-    def importaddress(self, addr, label='', rescan=True):
-        """Adds an address or pubkey to wallet without the associated privkey."""
-        addr = str(addr)
+    def getmininginfo(self):
+        """Return a JSON object containing mining-related information"""
+        return self._call('getmininginfo')
 
-        r = self._call('importaddress', addr, label, rescan)
-        return r
+    def getnetworkhashps(self, nblocks=None, height=None):
+        """Returns a int estimate of hashrate at block height
+        measured since nblocks (default=120)
+        """
+        return self._call('getnetworkhashps', nblocks, height)
 
-    def listunspent(self, minconf=0, maxconf=9999999, addrs=None):
-        """Return unspent transaction outputs in wallet
+    def prioritisetransaction(self, txid, fee_delta, dummy=""):
+        """Returns true. Prioritises transaction for mining"""
+        if not isinstance(txid, str):
+            txid = b2lx(txid)
+        return self._call('prioritisetransaction', txid, dummy, fee_delta)
 
-        Outputs will have between minconf and maxconf (inclusive)
-        confirmations, optionally filtered to only include txouts paid to
-        addresses in addrs.
+    def submitblock(self, block, params=None):
+        """Submit a new block to the network.
+
+        params is optional and is currently ignored by bitcoind. See
+        https://en.bitcoin.it/wiki/BIP_0022 for full specification.
         """
-        r = None
-        if addrs is None:
-            r = self._call('listunspent', minconf, maxconf)
+        if not isinstance(block, str):
+            hexblock = block
         else:
-            addrs = [str(addr) for addr in addrs]
-            r = self._call('listunspent', minconf, maxconf, addrs)
+            hexblock = hexlify(block.serialize())
+        if params is not None:
+            return self._call('submitblock', hexblock, params)
+        else:
+            return self._call('submitblock', hexblock)
 
-        r2 = []
-        for unspent in r:
-            unspent['outpoint'] = COutPoint(lx(unspent['txid']), unspent['vout'])
-            del unspent['txid']
+    def submitheader(self, hexdata):
+        """Submit block to the network."""
+        try:
+            r = self._call('submitblock', hex_data)
+        except VerifyError as ex:
+            raise VerifyError('%s.submitheader() - Invalid Header: %s (%d)' %
+                    (self.__class__.__name__, ex.error['message'], ex.error['code']))
+        return r
+
+    # == Network ==
+    def _addnode(self, node, arg):
+        r = self._call('addnode', node, arg)
+        return r
+
+    def addnode(self, node):
+        return self._addnode(node, 'add')
+
+    def addnodeonetry(self, node):
+        return self._addnode(node, 'onetry')
+
+    def removenode(self, node):
+        return self._addnode(node, 'remove')
+
+    def clearbanned(self):
+        """Clear list of banned IPs"""
+        self._call('clearbanned')
+
+    def disconnectnode(self, address="", nodeid=None):
+        """Disconnect from node
+        1. address    (string, optional, default=fallback to nodeid) The IP address/port of the node
+        2. nodeid     (numeric, optional, default=fallback to address) The node ID (see getpeerinfo for node IDs)
+        """
+        self._call('disconnectnode', address, nodeid)
+
+    def getaddednodeinfo(self, nodeid=None):
+        """Returns a JSON object of added nodes (excluding onetry added nodes)"""
+        return self._call('getaddednodeinfo',  nodeid)
+
+    def getconnectioncount(self):
+        """Return int of connection count"""
+        return self._call('getconnectioncount')
+
+    def getnettotals(self):
+        """Returns a JSON object of net totals"""
+        return self._call('getnettotals')
+
+    def getnetworkinfo(self):
+        """Returns a JSON object of network info"""
+        return self._call('getnetworkinfo')
+
+    def getnodeaddresses(self, count=None):
+        """Returns a JSON object of node addresses"""
+        return self._call('getnodeaddresses', count)
+
+    def getpeerinfo(self):
+        """Returns a JSON object of peer info"""
+        return self._call('getpeerinfo')
+
+    def listbanned(self):
+        """Returns a JSON object of banned peers"""
+        return self._call('listbanned')
+
+    def ping(self):
+        """Ping all connections and record ping time in 
+        getpeerinfo
+        """
+        return self._call('ping')
+
+    def setban(self, subnet, command, bantime=None, absolute=None):
+        """Add or remove nodes from banlist"""
+        return self._call('setban', subnet, command, bantime, absolute)
+
+    def setnetworkactive(self, state):
+        """Enable/Disable all p2p connections"""
+        return self._call('setnetworkactive', state)
+
+    # == Rawtransactions ==
+    # PSBT
+    def analyzepsbt(self, psbt_b64):
+        #TODO create PSBT object to pass instead of psbt_b64
+        """Return a JSON object of PSBT"""
+        return self._call('analyzepsbt', psbt_b64)
+
+    def combinepsbt(self, psbt_b64s):
+        #is passing a list the best way?
+        #TODO when PSBT object exists, decode this.
+        """Return a base64 encoded PSBT"""
+        return self._call('combinepsbt', psbt_b64s)
+
+    def converttopsbt(self, tx, permitsigdata=None, iswitness=None):
+        """Returns a base64 encoded PSBT"""
+        if not isinstance(tx, str):
+            tx = hexlify(tx.serialize())
+        return self._call('converttopsbt', tx, permitsigdata, iswitness)
+
+    # Python API is different from RPC API: data
+    def createpsbt(self, vins, vouts, data="", locktime=0, replaceable=False):
+        """Returns a base64-encoded PSBT object
+        This is probably not the best implementation,
+        but no existing object is suitable for vin or vout.
+        vins - list of CTxIn or {"txid": "hex","vout": n,"sequence": n}
+        vouts - list of CTxOut or {"address": amount},
+        data - hex data NOT JSON
+        """
+        if isinstance(vins[0], CTxIn):
+            ins = []
+            for i in vins:
+                txid = b2lx(i.prevout.hash)
+                vout = i.prevout.n
+                sequence = i.nSequence
+                ins.append({"txid": txid, "vout": vout, "sequence": sequence})
+            vins = ins
+        if isinstance(vouts[0], COutPoint):
+            outs = []
+            for o in vouts:
+                try:
+                    addr = CBitcoinAddress.from_scriptPubKey(o.scriptPubKey)
+                    amount = o.nValue
+                    outs.append({str(addr): amount/COIN})
+                except CBitcoinAddressError:
+                    raise CBitcoinAddressError("Invalid output: %s" % repr(o))
+            vouts = outs
+        if data:
+            vouts.append({"data": data})
+        return self._call('createpsbt', vins, vouts, locktime, replaceable)
+
+    def decodepsbt(self, psbt_b64):
+        """Returns a JSON object of PSBT.
+            Should return a PSBT object when created.
+        """
+        return self._call('decodepsbt', psbt_b64)
+
+    def finalizepsbt(self, psbt_b64, extract=None):
+        """Returns an extracted transaction hex or a PSBT, depending on
+        extract
+        {
+          "psbt" : "value",          
+          "hex" : "value",           
+          "complete" : true|false,   
+          ]
+        }
+        """
+        r = self._call('finalizepsbt', psbt_b64, extract)
+        if extract:
+            r = CTransaction.deserialize(unhexlify(r))
+        else:
+            r['tx'] = CTransaction.deserialize(unhexlify(r['hex']))
+            del r['hex']
+        return r
+
+    def joinpsbts(self, psbt_b64s):
+        """Return a base64-encoded PSBT"""
+        return self._call('joinpsbts', psbt_b64s)
+
+    def utxoupdatepsbt(self, psbt_b64, data):
+        """Return base64-encoded PSBT"""
+        return self._call('utxoupdatepsbt', psbt_b64, data)
+
+    #RAW TX
+    def combinerawtransaction(self, hextxs):
+        """Return raw hex of combined transaction"""
+        if not isinstance(hextxs[0], str):
+            hextxs = [hexlify(tx.serialize()) for tx in hextxs]
+        return self._call('combinerawtransaction', hextxs)
+
+    def createrawtransaction(self, vins, vouts, locktime=0, replaceable=False):
+        """Returns a Transaction Object
+        Again object should be created to allow vins and vouts
+        """
+        r = self._call('createrawtransactions', vins, vouts, locktime, replaceable)
+        return CTransaction.deserialize(unhexlify(r))
+
+    def getrawtransaction(self, txid, verbose=False, block_hash=None):
+        """Return transaction with hash txid
+
+        Raises IndexError if transaction not found.
+
+        verbose - If true a dict is returned instead with additional
+        information on the transaction.
+
+        Note that if all txouts are spent and the transaction index is not
+        enabled the transaction may not be available.
+        """
+        #Timeout issues depending on tx / machine
+        if not isinstance(txid, str):
+            txid = b2lx(txid)
+        if not isinstance(block_hash, str):
+            block_hash = b2lx(block_hash)
+        try:
+            r = self._call('getrawtransaction', txid, 1 if verbose else 0, block_hash)
+        except InvalidAddressOrKeyError as ex:
+            raise IndexError('%s.getrawtransaction(): %s (%d)' %
+                    (self.__class__.__name__, ex.error['message'], ex.error['code']))
+        if verbose:
+            r['tx'] = CTransaction.deserialize(unhexlify(r['hex']))
+            del r['hex']
+            del r['txid']
+            del r['version']
+            del r['locktime']
+            del r['vin']
+            del r['vout']
+            r['blockhash'] = lx(r['blockhash']) if 'blockhash' in r else None
+        else:
+            r = CTransaction.deserialize(unhexlify(r))
+
+        return r
+
+    def sendrawtransaction(self, tx, allowhighfees=False):
+        """Submit transaction to local node and network.
+
+        allowhighfees - Allow even if fees are unreasonably high.
+        """
+        hextx = hexlify(tx.serialize())
+        r = None
+        if allowhighfees:
+            r = self._call('sendrawtransaction', hextx, True)
+        else:
+            r = self._call('sendrawtransaction', hextx)
+        return lx(r)
+
+    def sendrawtransactionv0_19(self, tx, maxfeerate=None):
+        """Submit transaction to local node and network.
+
+        maxfeerate - numeric or string for max fee rate
+        """
+        if not isinstance(tx, str):
+            tx = hexlify(tx.serialize())
+        r = self._call('sendrawtransaction', tx, maxfeerate)
+        return lx(r)
+
+    def signrawtransaction(self, tx, *args):
+        """Sign inputs for transaction
+
+        FIXME: implement options
+        """
+        hextx = hexlify(tx.serialize())
+        r = self._call('signrawtransaction', hextx, *args)
+        r['tx'] = CTransaction.deserialize(unhexlify(r['hex']))
+        del r['hex']
+        return r
+
+    def decoderawtransaction(self, hex_data, iswitness=None):
+        """Return a JSON object representing the transaction"""
+        return self._call('decoderawtransaction', hex_data, iswitness)
+
+    def decodescript(self, hex_data):
+        """Returns a JSON object with script info"""
+        return self._call('decodescript', hex_data)
+
+    def fundrawtransaction(self, tx, include_watching=False):
+        """Add inputs to a transaction until it has enough in value to meet its out value.
+
+        include_watching - Also select inputs which are watch only
+
+        Returns dict:
+
+        {'tx':        Resulting tx,
+         'fee':       Fee the resulting transaction pays,
+         'changepos': Position of added change output, or -1,
+        }
+        """
+        hextx = hexlify(tx.serialize())
+        r = self._call('fundrawtransaction', hextx, include_watching)
+
+        r['tx'] = CTransaction.deserialize(unhexlify(r['hex']))
+        del r['hex']
+
+        r['fee'] = int(r['fee'] * COIN)
+
+        return r
+
+    def fundrawtransactionv0_19(self, tx, options=None, iswitness=None):
+        """
+        Options - a JSON dictionary of options. if True is passed, watch-only is included.
+
+        Returns a dict:   
+        {'tx':        Resulting tx
+         'fee':       Fee the resulting transaction pays,
+         'changepos': Position of added change output, or -1,
+        }
+        """
+        if not isinstance(tx, str):
+            tx = hexlify(tx.serialize())
+        r = self._call('fundrawtransaction', tx, options, iswitness)
+        r['tx'] = CTransaction.deserialize(unhexlify(r['hex']))
+        del r['hex']
+        r['fee'] = int(r['fee'] * COIN) # BTC -> sats
+        return r
+
+    def signrawtransactionwithkey(self, tx, privkeys, prevtxs=None, sighashtype=None):
+        """Return a transaction object
+        privkeys - list of CBitcoinSecret objects or list of base58-encoded privkeys (str)
+        prevtxs - JSON object containing info
+        sighashtype - numeric sighashtype default=SIGHASH_ALL
+        """
+        if not isinstance(tx, str):
+            tx = hexlify(tx.serialize())
+        if isinstance(privkeys[0], CBitcoinSecret):
+            privkeys = [str(sk) for sk in privkeys]
+        elif isinstance(privkeys[0], bytes):
+            privkeys = [sk.hex() for sk in privkeys]
+        r = self._call('signrawtransactionwithkey', privkeys, prevtxs, )
+        r['tx'] = CTransaction.deserialize(unhexlify(r['hex']))
+        del r['hex']
+        return r
+
+    def testmempoolaccept(self, txs, maxfeerate=None):
+        """Return a JSON object of each transaction's acceptance info"""
+        if not isinstance(txs[0],str):
+            txs = [hexlify(tx.serialize()) for tx in txs]
+        return self._call('testmempoolaccept', txs, maxfeerate)
+
+    # == Util ==
+
+    def validateaddress(self, address):
+        """Return information about an address"""
+        r = self._call('validateaddress', str(address))
+        if r['isvalid']:
+            r['address'] = CBitcoinAddress(r['address'])
+        if 'pubkey' in r:
+            r['pubkey'] = unhexlify(r['pubkey'])
+        return r
+
+    def createmultisig(self, nrequired, keys, address_type=None):
+        """Return a json object with the address and redeemScript
+        nrequired - int required sigs
+        keys - list of keys as str or CPubKey
+        address_type - Options are "legacy", "p2sh-segwit", and "bech32"
+
+        return:
+        {
+          "address": CBitcoinAddress,
+          "redeemScript": CScript
+        }
+
+        """
+        if not isinstance(keys[0], str):
+            keys = [str(k) for k in keys]
+        r = self._call('createmultisig', nrequired, keys, address_type)
+        # PLEASE CHECK
+        redeemScript = CScript.fromhex(r['redeemScript'])
+        r['redeemScript'] = redeemScript
+        r['address'] = CBitcoinAddress.from_scriptPubKey(redeemScript.to_p2sh_scriptPubKey())
+        return r
+    
+    def deriveaddresses(self, descriptor, _range=None):
+        """Returns addresses from descriptor
+
+        """
+        #TODO Descriptors need Implementing
+        return self._call('deriveaddresses', descriptor, _range)
+    
+    def estimatesmartfee(self, conf_target, estimate_mode=None):
+        """Returns a JSON object with feerate, errors, and block estimate
+        conf_target - attempted number of blocks from current tip to place tx
+        estimate_mode:
+        "UNSET"
+        "ECONOMICAL"            
+        default="CONSERVATIVE"
+        """
+        return self._call('estimatesmartfee', conf_target, estimate_mode)
+
+    def getdescriptorinfo(self, descriptor):
+        """Returns a JSON object with info about the descriptor:
+        {
+          "descriptor" : "desc",         (string) The descriptor in canonical form, without private keys
+          "checksum" : "chksum",         (string) The checksum for the input descriptor
+          "isrange" : true|false,        (boolean) Whether the descriptor is ranged
+          "issolvable" : true|false,     (boolean) Whether the descriptor is solvable
+          "hasprivatekeys" : true|false, (boolean) Whether the input descriptor contained at least one private key
+        }
+        """
+        return self._call('getdescriptorinfo', descriptor)
+
+    def signmessagewithprivkey(self, privkey, message):
+        """Return signature of signed message
+        WARNING: only works with legacy keys. Not P2SH or SegWit
+        """
+        #TODO THIS SHOULD BE TURNED INTO DERSignature object
+        return self._call('signmessagewithprivkey', str(privkey), message)
+        
+    def verifymessage(self, address, signature, message):
+        """Return true/false if message signature is valid"""
+        return self._call('verifymessage', str(address), str(signature), message)
+
+    # == Wallet ==
+    def abandontransaction(self, txid):
+        """Marks in-wallet transaction as abandoned, allowing utxos to be 'respent'"""
+        self._call('abandontransaction', b2lx(txid))
+
+    def abortrescan(self):
+        """Aborts wallet rescan triggered by an RPC call (ie. privkey)"""
+        self._call('abortrescan')
+
+    def addmultisigaddress(self, nrequired, keys, label=None, address_type=None):
+        """Add a NON-watch-only multisig address to the wallet. Requires new backup."""
+        #Works for both addresses and pubkeys, but hex() vs str() is annoying.
+        #TODO see if CPubKey.__str__() is used elsewhere or can be changed.
+        if isinstance(keys[0], CBitcoinAddress): 
+            keys = [str(k) for k in keys]
+        elif isinstance(keys[0], (CPubKey, bytes)):  
+            keys = [k.hex() for k in keys]
+        r = self._call('addmultisigaddress', nrequired, keys, label, address_type)
+        r['address'] = CBitcoinAddress(r['address'])
+        r['redeemScript'] = CScript.fromhex(r['redeemScript'])
+        return r
+
+    def backupwallet(self, destination):
+        """copies current wallet file to destination
+        destination - path to directory with or without filename
+        """
+        self._call('backupwallet', destination)
+
+    def bumpfee(self, txid, options=None):
+        """Bump fee of transation in mempool"""
+        if not isinstance(txid, str):
+            txid = b2lx(txid)
+        return self._call('bumpfee', txid, options)
+
+    def createwallet(self, wallet_name, disable_priv_keys=None, blank=None, passphrase=None, avoid_reuse=None ):
+        """Create a new Wallet 
+        wallet_name - name
+        disable_priv_keys - watch_only, default=False
+        blank - create a blank wallet with no seed or keys
+        passphrase - encrypt wallet with passphrase
+        avoid_reuse - segregate reused and clean coins. Better privacy
+        Return a JSON object about the new wallet
+        """
+        return self._call('createwallet', wallet_name, disable_priv_keys, blank, passphrase, avoid_reuse)
+
+    def dumpprivkey(self, addr):
+        """Return the private key matching an address
+        """
+        r = self._call('dumpprivkey', str(addr))
+        return CBitcoinSecret(r)
+    
+    def dumpwallet(self, filename):
+        """Dump all wallet keys and imported keys to a file.
+        NO OVERWRITING ALLOWED
+        returns a JSON object with full absolute path
+        """
+        self._call('dumpwallet', filename)
+
+    def encryptwallet(self, passphrase):
+        """
+        Encrypts wallet for the first time.
+        This passphrase will be required for all signing after call.
+        """
+        self._call('encryptwallet', passphrase)
+
+    def getaddressesbylabel(self, label):
+        """Return a JSON object with addresses as keys"""
+        # Convert to CBitcoinAddress? 
+        # not converting addresses makes the dict searchable.
+        return self._call('getaddressbylabel', label)
+
+    def getaccountaddress(self, account=None):
+        """Return the current Bitcoin address for receiving payments to this
+        account."""
+        r = self._call('getaccountaddress', account)
+        return CBitcoinAddress(r)
+
+    def getaddressinfo(self, address):
+        """Return a JSON object of info about address"""
+        address = str(address)
+        r = self._call('getaddressinfo', address)
+        if r['isscript']:
+            if r['script'] == 'scripthash':
+                r['redeemScript'] = CScript.fromhex(r['hex'])
+                # Keeping with previous style. why not CPubKey?
+                r['pubkey'] = unhexlify(r['pubkey']) 
+                # PERHAPS ALSO CHANGE ScriptPubKey to CScript?
+        return r
+
+    def getbalance(self, account='*', minconf=1, include_watchonly=False):
+        """Get the balance
+
+        account - The selected account. Defaults to "*" for entire wallet. It
+        may be the default account using "".
+
+        minconf - Only include transactions confirmed at least this many times.
+        (default=1)
+
+        include_watchonly - Also include balance in watch-only addresses (see 'importaddress')
+        (default=False)
+        """
+        r = self._call('getbalance', account, minconf, include_watchonly)
+        return int(r*COIN)
+
+    def getbalances(self):
+        """Returns a JSON object of balances of all wallets and imported keys
+        All balances shown in sats
+        """
+        r = self._call('getbalances')
+        for k in r['mine'].keys():
+            r['mine'][k] = int(r['mine'][k]* COIN)
+        if 'watchonly' in r:
+            for k in r['watchonly'].keys():
+                r['watchonly'][k] = int(r['watchonly'][k]* COIN)
+        return r
+
+    def getnewaddress(self, account=None, address_type=None):
+        """Return a new Bitcoin address for receiving payments.
+
+        If account is not None, it is added to the address book so payments
+        received with the address will be credited to account.
+
+        address_type:
+        "legacy"
+        """
+        r = None
+        if account is not None or address_type is not None:
+            r = self._call('getnewaddress', account, address_type)
+        else:
+            r = self._call('getnewaddress')
+
+        return CBitcoinAddress(r)
+
+    def getrawchangeaddress(self):
+        """Returns a new Bitcoin address, for receiving change.
+
+        This is for use with raw transactions, NOT normal use.
+        """
+        r = self._call('getrawchangeaddress')
+        return CBitcoinAddress(r)
+
+    def getreceivedbyaddress(self, addr, minconf=1):
+        """Return total amount received by given a (wallet) address
+
+        Get the amount received by <address> in transactions with at least
+        [minconf] confirmations.
+
+        Works only for addresses in the local wallet; other addresses will
+        always show zero.
+
+        addr    - The address. (CBitcoinAddress instance)
+
+        minconf - Only include transactions confirmed at least this many times.
+        (default=1)
+        """
+        r = self._call('getreceivedbyaddress', str(addr), minconf)
+        return int(r * COIN)
+
+    def gettransaction(self, txid):
+        """Get detailed information about in-wallet transaction txid
+
+        Raises IndexError if transaction not found in the wallet.
+
+        FIXME: Returned data types are not yet converted.
+        """
+        try:
+            r = self._call('gettransaction', b2lx(txid))
+        except InvalidAddressOrKeyError as ex:
+            raise IndexError('%s.getrawtransaction(): %s (%d)' %
+                    (self.__class__.__name__, ex.error['message'], ex.error['code']))
+        return r
+
+    def getunconfirmedbalance(self):
+        """Deprecated in v0.19.0.1"""
+        r = None
+        try:
+            r = int(self._call('getunconfirmedbalance') * COIN)
+            return r
+        except:
+            raise DeprecationWarning("Use %s.getbalances().mine.untrusted_pending" % self.__class__.__name__)
+        
+    def getwalletinfo(self):
+        """Returns a JSON with wallet info
+        Results vary by version.
+        """
+        r = self._call('getwalletinfo')
+        r['paytxfee'] = int(r['paytxfee']*COIN)
+        try: # Deprecated
+            r['balance'] = int(r['balance']*COIN)
+            r['unconfirmed_balance'] = int(r['unconfirmed_balance']*COIN)
+            r['immature_balance'] = int(r['immature_balance']*COIN)
+        except KeyError:
+            pass
+        return r
+    
+    #TODO ADD P2SH arg. This will cause JSONRPCError on older versions
+    def importaddress(self, addr, label='', rescan=True):
+        """Adds an address or pubkey to wallet without the associated privkey."""
+        
+        addr = str(addr)
+
+        r = self._call('importaddress', addr, label, rescan)
+        return r
+
+    #Since Options is only rescan (bool), change this API?
+    def importmulti(self, requests, options=None):
+        """Import several pubkeys, privkeys, or scripts
+        requests - a JSON object
+        options - a JSON object
+        return a JSON
+        """
+        # The Requests JSON is so large, I decided not 
+        # to allow CObjects in the JSON. 
+        # TODO Fix this?
+        return self._call('importmulti', requests, options)
+
+    def importprivkey(self, privkey, label=None, rescan=True):
+        """Import a privkey and optionally rescan"""
+        self._call('importprivkey', str(privkey), label, rescan)
+
+    def importprunedfunds(self, tx, txout_proof):
+        """Import a transaction. Address must already be in wallet.
+        User must import subsequent transactions or rescan
+
+        #TODO should txout_proof be an obj?
+        """
+        if not isinstance(tx, str):
+            tx = hexlify(tx.serialize())
+        return self._call('importprunedfunds', tx, txout_proof)
+
+    def importpubkey(self, pubkey, label=None, rescan=None):
+        """Import pubkey as watchonly"""
+        if not isinstance(pubkey, str):
+            pubkey = pubkey.hex()
+        self._call('importpubkey', pubkey, label, rescan)
+
+    def importwallet(self, filename):
+        """Import wallet by filename"""
+        self._call('importwallet')
+
+    def keypoolrefill(self, new_size=100):
+        """Add more keys to keypool
+        new_size - int total size of pool after call
+        """
+        self._call('keypoolrefill')
+
+    def listaddressgroupings(self):
+        """Lists groups of addresses which have common ownership
+        exposed by joint use
+        Returns a JSON object with list of address groupings (lists)
+        """
+        # Make into address or leave readable/searchable?
+        return self._call('listaddressgroupings')
+
+    def listlabels(self, purpose=None):
+        """List all labels that are assigned to addresses with specific purposes"""
+        return self._call('listlabels')
+
+    def listlockunspent(self):
+        """Returns list of temporarily unspendable outputs."""
+        r = self._call('listlockunspent')
+        for unspent in r:
+            unspent['outpoint'] = COutPoint(lx(unspent['txid']), unspent['vout'])
+            del unspent['txid']
+            del unspent['vout']
+        return r
+
+    def listreceivedbyaddress(self, minconf=1, include_empty=None, include_watchonly=None, address_filter=None):
+        """List balances by receiving address
+        Return a JSON of address infos
+        """
+        r = self._call('listreceivedbyaddress', minconf, include_empty, include_watchonly, address_filer)
+        for recd in r:
+            recd['address'] = CBitcoinAddress(recd['address'])
+            recd['amount'] = int(recd['amount']*COIN)
+            recd['txid'] = [lx(txid) for txid in recd['txid']]
+        return r
+
+    def listreceivedbylabel(self, minconf=1, include_empty=False, include_watchonly=None):
+        """List balances by label
+        Return a JSON of address infos
+        """
+        r = self._call('listreceivedbylabel', minconf, include_empty, include_watchonly)
+        for recd in r:
+            recd['address'] = CBitcoinAddress(recd['address'])
+            recd['amount'] = int(recd['amount']*COIN)
+            #listreceivedbylabel doesn't return TXIDs. 
+            # I will be PR'ing Core to change this in Future.
+            #recd['txid'] = [lx(txid) for txid in recd['txid']]
+        return r
+
+    def listsinceblock(self, block_hash=None, conf_target=1, include_watchonly=None, include_removed=True): 
+        """List balances since block (determined by block_hash)
+        """
+        r = self._call('listsinceblock', block_hash, conf_target, include_watchonly, include_removed)
+        for tx in r['transactions']:
+            tx['address'] = CBitcoinAddress(tx['address'])
+            tx['amount'] = int(tx['amount']*COIN)
+            if 'fee' in tx:
+                tx['fee'] = int(tx['fee']*COIN)
+            tx['outpoint'] = COutPoint(lx(tx['txid']), tx['vout'])
+            del tx['txid']
+            del tx['vout']
+        if 'removed' in r: # Only present if include_removed
+            for tx in r['removed']:
+                tx['address'] = CBitcoinAddress(tx['address'])
+                tx['amount'] = int(tx['amount']*COIN)
+                if 'fee' in tx:
+                    tx['fee'] = int(tx['fee']*COIN)
+                tx['outpoint'] = COutPoint(lx(tx['txid']), tx['vout'])
+                del tx['txid']
+                del tx['vout']
+        return r
+
+    def listtransactions(self, label=None, count=None, skip=None, include_watchonly=None):
+        """List all transactions"""
+        r = self._call('listtransaction', label, count, skip, include_watchonly)
+        for tx in r['transactions']:
+            tx['address'] = CBitcoinAddress(tx['address'])
+            tx['amount'] = int(tx['amount']*COIN)
+            if 'fee' in tx:
+                tx['fee'] = int(tx['fee']*COIN)
+            tx['outpoint'] = COutPoint(lx(tx['txid']), tx['vout'])
+            del tx['txid']
+            del tx['vout']
+        if 'removed' in r: # Only present if include_removed
+            for tx in r['removed']:
+                tx['address'] = CBitcoinAddress(tx['address'])
+                tx['amount'] = int(tx['amount']*COIN)
+                if 'fee' in tx:
+                    tx['fee'] = int(tx['fee']*COIN)
+                tx['outpoint'] = COutPoint(lx(tx['txid']), tx['vout'])
+                del tx['txid']
+                del tx['vout']
+        return r
+
+    #TODO add include_unsafe, query_options
+    def listunspent(self, minconf=0, maxconf=9999999, addrs=None):
+        """Return unspent transaction outputs in wallet
+
+        Outputs will have between minconf and maxconf (inclusive)
+        confirmations, optionally filtered to only include txouts paid to
+        addresses in addrs.
+        """
+        r = None
+        if addrs is None:
+            r = self._call('listunspent', minconf, maxconf)
+        else:
+            addrs = [str(addr) for addr in addrs]
+            r = self._call('listunspent', minconf, maxconf, addrs)
+
+        r2 = []
+        for unspent in r:
+            unspent['outpoint'] = COutPoint(lx(unspent['txid']), unspent['vout'])
+            del unspent['txid']
             del unspent['vout']
 
             # address isn't always available as Bitcoin Core allows scripts w/o
@@ -672,25 +1497,39 @@ def listunspent(self, minconf=0, maxconf=9999999, addrs=None):
             r2.append(unspent)
         return r2
 
+    def listwalletdir(self):
+        """Return a JSON object of wallets in wallet directory"""
+        return self._call('listwalletdir')
+
+    def listwallets(self):
+        """Return a list of currently loaded wallets"""
+        return self._call('listwallets')
+
+    def loadwallet(self, filename):
+        """Load a wallet from filename or directory name
+        Returns a JSON object of result
+        """
+        return self._call('loadwallet', filename)
+
     def lockunspent(self, unlock, outpoints):
         """Lock or unlock outpoints"""
         json_outpoints = [{'txid':b2lx(outpoint.hash), 'vout':outpoint.n}
                           for outpoint in outpoints]
         return self._call('lockunspent', unlock, json_outpoints)
 
-    def sendrawtransaction(self, tx, allowhighfees=False):
-        """Submit transaction to local node and network.
+    def removeprunedfunds(self, txid):
+        """Remove pruned utxos from wallet"""
+        if not isinstance(txid, str):
+            txid = b2lx(txid)
+        self._call('removeprunedfunds', txid)
 
-        allowhighfees - Allow even if fees are unreasonably high.
+    def rescanblockchain(self, start_height=0, stop_height=None):
+        """Begin rescan of blockchain
+        Return a JSON object of result
         """
-        hextx = hexlify(tx.serialize())
-        r = None
-        if allowhighfees:
-            r = self._call('sendrawtransaction', hextx, True)
-        else:
-            r = self._call('sendrawtransaction', hextx)
-        return lx(r)
+        return self._call('rescanblockchain')
 
+    #TODO API updates for sendmany and sendtoaddress
     def sendmany(self, fromaccount, payments, minconf=1, comment='', subtractfeefromamount=[]):
         """Send amount to given addresses.
 
@@ -708,16 +1547,43 @@ def sendtoaddress(self, addr, amount, comment='', commentto='', subtractfeefroma
         r = self._call('sendtoaddress', addr, amount, comment, commentto, subtractfeefromamount)
         return lx(r)
 
-    def signrawtransaction(self, tx, *args):
-        """Sign inputs for transaction
+    def sethdseed(self, newkeypool=True, seed=None):
+        """Set HD Seed of Wallet
+        newkeypool - bool flush old unused addresses, including change addresses
+        seed - WIF Private Key. random seed if none
+        """
+        self._call('sethdseed', newkeypool, str(seed))
 
-        FIXME: implement options
+    def setlabel(self, address, label):
+        """Apply a label to an existing address"""
+        self._call('setlabel', str(address), label)
+
+    def settxfee(self, amount):
+        """Set fee for transactions of this wallet
+        amount - int sats/Bytes
+        return bool of success
         """
-        hextx = hexlify(tx.serialize())
-        r = self._call('signrawtransaction', hextx, *args)
-        r['tx'] = CTransaction.deserialize(unhexlify(r['hex']))
-        del r['hex']
-        return r
+        # Convert from sats/B to BTC/kB
+        amount = (amount/COIN)*1000
+        return self._call('settxfee', amount)
+
+    def setwalletflag(self, flag, value=True):
+        """Change state of a given flag for a wallet
+        flag - options: "avoid_reuse" 
+        value - bool new value for flag
+
+        returns a JSON objection with flag and new value
+        """
+        return self._call('setwalletflag', flag, value)
+
+    def signmessage(self, address, message):
+        """Sign a message using privkey associated with given address
+        address - CBitcoinAddress or str
+        message - full message to be signed
+        return signature in base64
+        #TODO convert base64 to DERSignature obj
+        """
+        return self._call('signmessage', str(address), message)
 
     def signrawtransactionwithwallet(self, tx, *args):
         """Sign inputs for transaction
@@ -731,27 +1597,51 @@ def signrawtransactionwithwallet(self, tx, *args):
         del r['hex']
         return r
 
-    def submitblock(self, block, params=None):
-        """Submit a new block to the network.
+    def unloadwallet(self, wallet_name=None):
+        """Unload wallet"""
+        self._call('unloadwallet')
 
-        params is optional and is currently ignored by bitcoind. See
-        https://en.bitcoin.it/wiki/BIP_0022 for full specification.
-        """
-        hexblock = hexlify(block.serialize())
-        if params is not None:
-            return self._call('submitblock', hexblock, params)
-        else:
-            return self._call('submitblock', hexblock)
+    # Python API is different from RPC API: data
+    def walletcreatefundedpsbt(self, vins, vouts, data=None, locktime=0, options=None, bip32derivs=None):
+        """Create funded PSBT from wallet funds
+        vins - a list of CTxIn
+        vouts - a list of CTxOut
+        locktime - raw locktime (block height or unix timestamp)
+        options - a JSON object
+        bip32derivs - bool include BIP32 paths in PSBT
 
-    def validateaddress(self, address):
-        """Return information about an address"""
-        r = self._call('validateaddress', str(address))
-        if r['isvalid']:
-            r['address'] = CBitcoinAddress(r['address'])
-        if 'pubkey' in r:
-            r['pubkey'] = unhexlify(r['pubkey'])
+        returns a JSON object with base64-encoded PSBT, fee, and changepos
+        """
+        if isinstance(vins[0], CTxIn):
+            ins = []
+            for i in vins:
+                txid = b2lx(i.prevout.hash)
+                vout = i.prevout.n
+                sequence = i.nSequence
+                ins.append({"txid": txid, "vout": vout, "sequence": sequence})
+            vins = ins #Allow for JSON to be passed directly
+        if isinstance(vouts[0], CTxOut):
+            outs = []
+            for o in vouts:
+                try:
+                    addr = CBitcoinAddress.from_scriptPubKey(o.scriptPubKey)
+                    amount = o.nValue
+                    outs.append({str(addr): amount/COIN})
+                except CBitcoinAddressError:
+                    raise CBitcoinAddressError("Invalid output: %s" % repr(o))
+            vouts = outs
+        if data:
+            vouts.append({"data": data})
+        #TODO allow for addresses in options
+            
+        r = self._call('walletcreatefundedpsbt', vins, vouts, locktime, options, bip32derivs)
+        r['fee'] = int(r['fee'] * COIN)
         return r
 
+    def walletlock(self):
+        """locks wallet. Password will be required for future signing"""
+        self._call('walletlock')
+
     def unlockwallet(self, password, timeout=60):
         """Stores the wallet decryption key in memory for 'timeout' seconds.
 
@@ -761,20 +1651,38 @@ def unlockwallet(self, password, timeout=60):
         (default=60)
         """
         r = self._call('walletpassphrase', password, timeout)
+        #FIXME as of v0.19.0.1 no return
         return r
 
-    def _addnode(self, node, arg):
-        r = self._call('addnode', node, arg)
-        return r
+    def walletpassphrase(self, password, timeout=60):
+        """Same as unlockwallet"""
+        return self.unlockwallet(password, timeout)
 
-    def addnode(self, node):
-        return self._addnode(node, 'add')
+    def walletpassphrasechange(self, oldpassphrase, newpassphrase):
+        """Change passphrase from oldpassphrase to newpassphrase"""
+        self._call('walletpassphrasechange')
 
-    def addnodeonetry(self, node):
-        return self._addnode(node, 'onetry')
+    def walletprocesspsbt(self, psbt, sign=True, sighashtype=None, bip32derivs=None):
+        """Process base64-encoded PSBT, add info and sign vins that belong to this wallet
+        Return a base64-encoded PSBT
+        """
+        return self._call('walletprocesspsbt', psbt, sign, sighashtype, bip32derivs)
+
+    def getinfo(self):
+        """Return a JSON object containing various state info"""
+        try:
+            r = self._call('getinfo')
+            if 'balance' in r:
+                r['balance'] = int(r['balance'] * COIN)
+            if 'paytxfee' in r:
+                r['paytxfee'] = int(r['paytxfee'] * COIN)
+            return r
+        except:
+            warnings.warn(
+                "getinfo is deprecated from version 0.16.0 use getnetworkinfo instead", DeprecationWarning
+            )
+    
 
-    def removenode(self, node):
-        return self._addnode(node, 'remove')
 
 __all__ = (
     'JSONRPCError',
diff --git a/bitcoin/tests/test_rpc.py b/bitcoin/tests/test_rpc.py
index 07d1f964..cb97f8fc 100644
--- a/bitcoin/tests/test_rpc.py
+++ b/bitcoin/tests/test_rpc.py
@@ -13,24 +13,175 @@
 
 import unittest
 
-from bitcoin.rpc import Proxy
+import bitcoin.rpc
+from bitcoin.core import CBlock, CBlockHeader, lx, b2lx, COutPoint
+from bitcoin.core.script import CScript
+from bitcoin.wallet import CBitcoinAddress, CBitcoinSecret
+
+def is_active():
+    """
+    Proxy raises ValueError if cookie file not found
+    #FIXME is there a better way of doing this?
+    """
+    try:
+        p = bitcoin.rpc.Proxy()
+        p.help()
+        return True
+    except ValueError: 
+        return False
 
 class Test_RPC(unittest.TestCase):
+    _IS_ACTIVE = is_active()
     # Tests disabled, see discussion below.
     # "Looks like your unit tests won't work if Bitcoin Core isn't running;
     # maybe they in turn need to check that and disable the test if core isn't available?"
     # https://github.com/petertodd/python-bitcoinlib/pull/10
-    pass
-
-#    def test_can_validate(self):
-#        working_address = '1CB2fxLGAZEzgaY4pjr4ndeDWJiz3D3AT7'
-#        p = Proxy()
-#        r = p.validateAddress(working_address)
-#        self.assertEqual(r['address'], working_address)
-#        self.assertEqual(r['isvalid'], True)
-#
-#    def test_cannot_validate(self):
-#        non_working_address = 'LTatMHrYyHcxhxrY27AqFN53bT4TauR86h'
-#        p = Proxy()
-#        r = p.validateAddress(non_working_address)
-#        self.assertEqual(r['isvalid'], False)
+    # Sachin Meier: "I've changed it so each test checks against the _IS_ACTIVE variable "
+    #pass
+
+    def test_getbestblockhash_and_header(self):
+        if self._IS_ACTIVE:
+            proxy = bitcoin.rpc.Proxy()
+            blockhash = proxy.getbestblockhash()
+            header = proxy.getblockheader(blockhash)
+            self.assertTrue(isinstance(header, CBlockHeader))
+        else:
+            pass
+
+    def test_getblock(self):
+        if self._IS_ACTIVE:
+            proxy = bitcoin.rpc.Proxy()
+            blockhash = proxy.getbestblockhash()
+            # Test from bytes
+            block1 = proxy.getblock(blockhash)
+            self.assertTrue(isinstance(block1, CBlock))
+            # Test from str
+            block2 = proxy.getblock("0000000000000000000b4b0daf89eac9d84138fc900b8c473d4da70742e93dd0")
+            self.assertTrue(isinstance(block2, CBlock))
+        else:
+            pass
+
+    def test_getblockcount_et_al(self):
+        # This test could possibly false-fail if new blocks arrive. 
+        # Highly unlikely since they're quick calls
+        if self._IS_ACTIVE:
+            proxy = bitcoin.rpc.Proxy()
+            blockhash = proxy.getbestblockhash()
+            height_from_hash = proxy.getblockstats(blockhash)["height"]
+            height_from_count = proxy.getblockcount()
+            height_from_chaintips = proxy.getchaintips()[0]["height"]
+            height_from_chaintxstats = proxy.getchaintxstats()["window_final_block_height"]
+            self.assertEqual(height_from_count, height_from_hash)
+            self.assertEqual(height_from_chaintips, height_from_chaintxstats)
+            self.assertEqual(height_from_chaintips, height_from_count)
+        else:
+            pass
+
+    def test_txoutproofs(self):
+        if self._IS_ACTIVE:
+            proxy = bitcoin.rpc.Proxy()
+            blockhash = "0000000000000000000317612505ebdbe2686856535903bb0a05d4629670d518"
+            c_txid = "468564cfeba24ae321ee142e8786a53005f33051222e42f06fb2e9f048d0dba5"
+            c_proof = "00e0ff3749d01e6bebeb55a3dc983f194a1e232dc7149aff308d0d000000000000000000e331c7b03923f8b98074c7abeb10f609804ea18a53389b310c560c555b5c7d90ac8f315ff8b41017108dcd6a2c0b00000da53b7cc71139618dee5368d2075cd50badb97b0b4e4ca07b3ff749006280ff05a5dbd048f0e9b26ff0422e225130f30530a586872e14ee21e34aa2ebcf648546a03eab6796b5ff607266d66cf75d10454ac8370f0630c02395da56e8a3f9bc07b5adf47993ba6e33a625a7243c87111a93b592627efe4d6a1c3385685e8b0ae37e05b93e0de10db4c82466baf83da8c32a599fe2b6cace3c7a1b0b59e591071a656f93a19c08a4cc93d95e511220db284c72da6669355aa49226d61287e6048166e87bf93847a39f1c7552088c1831aabb4a6f29aaaa951eaafeaca21aea982068a2a51ff1088df84cdb7d5cdfbad8f91f2f75f45403d78b0fee2e68fdf5f076e8cff72482184a62b37e5af25b9227f27bedd3ebef27d01b0f99e0c456922f8fd16ad36445ca52bde44e42b145803130a420feb6fc0d8d9f2b9e12954ad8ea537ea843e7bddad228f7a754df0bf4337361f6bde81304d9cae789adaafd7b607ac49e422c5b01b3b859f777bb86f69e4047b9fe9752db5822becaa579b0066dbfaced20ab383ea8caa113437564dbcef9c04f224b352364baaddd7bfdb517383a04ff2f0000"
+            proof = proxy.gettxoutproof([c_txid], blockhash)
+            txid = b2lx(proxy.verifytxoutproof(proof)[0])
+            self.assertEqual(c_txid, txid) # CHECK THAT TXID comes out well. maybe hex it
+            self.assertEqual(c_proof, proof)
+        else:
+            pass
+
+    def test_can_validate(self):
+        if self._IS_ACTIVE:
+            p = bitcoin.rpc.Proxy()
+            working_address = '1CB2fxLGAZEzgaY4pjr4ndeDWJiz3D3AT7'
+            r = p.validateaddress(working_address)
+            self.assertEqual(str(r['address']), working_address)
+            self.assertEqual(r['isvalid'], True)
+        else:
+            pass
+
+    def test_cannot_validate(self):
+        if self._IS_ACTIVE:
+          non_working_address = 'LTatMHrYyHcxhxrY27AqFN53bT4TauR86h'
+          p = bitcoin.rpc.Proxy()
+          r = p.validateaddress(non_working_address)
+          self.assertEqual(r['isvalid'], False)
+        else:
+            pass
+
+    def test_deterministic_multisig(self):
+        if self._IS_ACTIVE:
+            p = bitcoin.rpc.Proxy()
+            pubkeys = ["02389e049d7baf3b4170ddb5c85f0ac22198572d76e0fee3fdb6c434ac689f270d", 
+            "0364ca1b46c1aaee3f40a35b5d32937b2616ace2914fdacdc1bf95f53fe06514d0", 
+            "03eac5ba66377c3bc1a92d1db3c22dc8cd0626a17f22c13d481fd14ca1fa2cf7f6"]
+            multisig_addr = "39NHQCfNjGRLGuAH5tuPXfERJsDncYehyH"
+            redeemScript = "522102389e049d7baf3b4170ddb5c85f0ac22198572d76e0fee3fdb6c434ac689f270d210364ca1b46c1aaee3f40a35b5d32937b2616ace2914fdacdc1bf95f53fe06514d02103eac5ba66377c3bc1a92d1db3c22dc8cd0626a17f22c13d481fd14ca1fa2cf7f653ae"
+            r = p.createmultisig(2, pubkeys)
+            self.assertEqual(str(r['address']), multisig_addr)
+            self.assertEqual(r['redeemScript'].hex(), redeemScript)
+        else:
+            pass
+
+    def test_signmessagewithprivkey(self):
+        """As of now, signmessagewithprivkey returns string of 
+        signature. Later this should change 
+        """
+        if self._IS_ACTIVE:    
+            proxy = bitcoin.rpc.Proxy()
+            c_sig = "Hy+OtvwJnE0ylgORtqG8/U9ZP11IW38GaSCxIvlAcrLVGWJV61Zxfb/h/A51VPEJZkIFogqxceIMTCppfEOyl5I="
+            privkey_txt = "Kya9eoTsoct6rsztC5rSLfuU2S4Dw5xtgCy2uPJgbkSLXd4FqquD"
+            privkey = CBitcoinSecret(privkey_txt)
+            msg = "So Long as Men Die"
+            # Check from CBitcoinSecret
+            sig = proxy.signmessagewithprivkey(privkey, msg)
+            self.assertEqual(sig, c_sig)
+            # Check from str
+            sig2 = proxy.signmessagewithprivkey(privkey_txt, msg)
+            self.assertEqual(sig2, c_sig)
+        else:
+            pass
+
+    def test_verifymessage(self):      
+        if self._IS_ACTIVE: 
+            proxy = bitcoin.rpc.Proxy()
+            sig = "ILRG2SnP6oPIofrfEDVk71J8rvM2KKbXU+D4+xWB2RRST4I2ilCTc7rXCS0Zu1/ousOX4aFhCrF815De71xZyxY="
+            addr_txt = "14wCZ9KpTuXB35kdYH2Loy1oP1ak1BT3JH" # Not corresponding addr as signwithprivkey
+            addr = CBitcoinAddress(addr_txt)
+            msg = "So Long as Men Die" 
+            #Check with both address and str
+            self.assertTrue(proxy.verifymessage(addr_txt, sig, msg))
+            self.assertTrue(proxy.verifymessage(addr, sig, msg))
+            return proxy.verifymessage(addr, sig, msg)
+        else:
+            pass
+
+    # def test_setxfee(self):
+    #     """ This test will change settings of user's core instance, so
+    #     It is commented out for now. 
+    #     """
+    #     if self._IS_ACTIVE:
+    #         proxy = bitcoin.rpc.Proxy()
+    #         self.assertTrue( proxy.settxfee(2) )
+    #     else:
+    #         pass
+
+    # def test_gettxout(self):
+    #     """Txout disappears if spent, so difficult to set static test"""
+    #     if self._IS_ACTIVE:
+    #         proxy = bitcoin.rpc.Proxy()
+    #         txo = COutPoint(lx("2700507d971a25728a257ed208ba409e7510f861dec928a478ee92f5ef2b4527"), 0)
+    #         r = proxy.gettxout(txo)
+    #         script = CScript.fromhex("76a9147179f4af7439435720637ee3276aabed1440719188ac")
+    #         self.assertEqual(r['txout'].scriptPubKey, script)
+    #     else:
+    #         pass
+
+
+    def test_getmininginfo(self):
+        if self._IS_ACTIVE:
+            proxy = bitcoin.rpc.Proxy()
+            proxy.getmininginfo()
+        else:
+            pass
+