diff --git a/doc/release-notes.md b/doc/release-notes.md
--- a/doc/release-notes.md
+++ b/doc/release-notes.md
@@ -31,3 +31,8 @@
   - `fundrawtransaction` now returns RPC_WALLET_ERROR if bitcoind is unable to create
   the transaction. The error message provides further details. Previously returned
   RPC_INTERNAL_ERROR.
+  - The `gettxoutsetinfo` response now contains `disk_size` and `bogosize` instead of
+    `bytes_serialized`. The first is a more accurate estimate of actual disk usage, but
+    is not deterministic. The second is unrelated to disk usage, but is a
+    database-independent metric of UTXO set size: it counts every UTXO entry as 50 + the
+    length of its scriptPubKey.
diff --git a/qa/rpc-tests/blockchain.py b/qa/rpc-tests/blockchain.py
--- a/qa/rpc-tests/blockchain.py
+++ b/qa/rpc-tests/blockchain.py
@@ -50,8 +50,11 @@
         assert_equal(res['transactions'], 200)
         assert_equal(res['height'], 200)
         assert_equal(res['txouts'], 200)
-        assert_equal(res['bytes_serialized'], 13924)
+        assert_equal(res['bogosize'], 17000),
         assert_equal(res['bestblock'], node.getblockhash(200))
+        size = res['disk_size']
+        assert size > 6400
+        assert size < 64000
         assert_equal(len(res['bestblock']), 64)
         assert_equal(len(res['hash_serialized']), 64)
 
@@ -65,6 +68,7 @@
         assert_equal(res2['total_amount'], Decimal('0'))
         assert_equal(res2['height'], 0)
         assert_equal(res2['txouts'], 0)
+        assert_equal(res2['bogosize'], 0),
         assert_equal(res2['bestblock'], node.getblockhash(0))
         assert_equal(len(res2['hash_serialized']), 64)
 
@@ -77,6 +81,7 @@
         assert_equal(res['transactions'], res3['transactions'])
         assert_equal(res['height'], res3['height'])
         assert_equal(res['txouts'], res3['txouts'])
+        assert_equal(res['bogosize'], res3['bogosize'])
         assert_equal(res['bestblock'], res3['bestblock'])
         assert_equal(res['hash_serialized'], res3['hash_serialized'])
 
diff --git a/src/coins.h b/src/coins.h
--- a/src/coins.h
+++ b/src/coins.h
@@ -417,6 +417,9 @@
 
     //! As we use CCoinsViews polymorphically, have a virtual destructor
     virtual ~CCoinsView() {}
+
+    //! Estimate database size (0 if not implemented)
+    virtual size_t EstimateSize() const { return 0; }
 };
 
 /** CCoinsView backed by another CCoinsView */
@@ -433,6 +436,7 @@
     void SetBackend(CCoinsView &viewIn);
     bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock);
     CCoinsViewCursor *Cursor() const;
+    size_t EstimateSize() const override;
 };
 
 /**
diff --git a/src/coins.cpp b/src/coins.cpp
--- a/src/coins.cpp
+++ b/src/coins.cpp
@@ -77,6 +77,9 @@
 CCoinsViewCursor *CCoinsViewBacked::Cursor() const {
     return base->Cursor();
 }
+size_t CCoinsViewBacked::EstimateSize() const {
+    return base->EstimateSize();
+}
 
 SaltedTxidHasher::SaltedTxidHasher()
     : k0(GetRand(std::numeric_limits<uint64_t>::max())),
diff --git a/src/dbwrapper.h b/src/dbwrapper.h
--- a/src/dbwrapper.h
+++ b/src/dbwrapper.h
@@ -273,6 +273,22 @@
      * Return true if the database managed by this class contains no entries.
      */
     bool IsEmpty();
+
+    template <typename K>
+    size_t EstimateSize(const K &key_begin, const K &key_end) const {
+        CDataStream ssKey1(SER_DISK, CLIENT_VERSION),
+            ssKey2(SER_DISK, CLIENT_VERSION);
+        ssKey1.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
+        ssKey2.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
+        ssKey1 << key_begin;
+        ssKey2 << key_end;
+        leveldb::Slice slKey1(ssKey1.data(), ssKey1.size());
+        leveldb::Slice slKey2(ssKey2.data(), ssKey2.size());
+        uint64_t size = 0;
+        leveldb::Range range(slKey1, slKey2);
+        pdb->GetApproximateSizes(&range, 1, &size);
+        return size;
+    }
 };
 
 #endif // BITCOIN_DBWRAPPER_H
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -857,13 +857,14 @@
     uint256 hashBlock;
     uint64_t nTransactions;
     uint64_t nTransactionOutputs;
-    uint64_t nSerializedSize;
+    uint64_t nBogoSize;
     uint256 hashSerialized;
+    uint64_t nDiskSize;
     CAmount nTotalAmount;
 
     CCoinsStats()
-        : nHeight(0), nTransactions(0), nTransactionOutputs(0),
-          nSerializedSize(0), nTotalAmount(0) {}
+        : nHeight(0), nTransactions(0), nTransactionOutputs(0), nBogoSize(0),
+          nDiskSize(0), nTotalAmount(0) {}
 };
 
 //! Calculate statistics about the unspent transaction output set
@@ -885,6 +886,7 @@
         if (pcursor->GetKey(key) && pcursor->GetValue(coins)) {
             stats.nTransactions++;
             ss << key;
+            ss << VARINT(coins.nHeight * 2 + coins.fCoinBase);
             for (size_t i = 0; i < coins.vout.size(); i++) {
                 const CTxOut &out = coins.vout[i];
                 if (!out.IsNull()) {
@@ -892,9 +894,13 @@
                     ss << VARINT(i + 1);
                     ss << out;
                     nTotalAmount += out.nValue;
+                    stats.nBogoSize +=
+                        32 /* txid */ + 4 /* vout index */ +
+                        4 /* height + coinbase */ + 8 /* amount */ +
+                        2 /* scriptPubKey len */ +
+                        out.scriptPubKey.size() /* scriptPubKey */;
                 }
             }
-            stats.nSerializedSize += 32 + pcursor->GetValueSize();
             ss << VARINT(0);
         } else {
             return error("%s: unable to read value", __func__);
@@ -903,6 +909,7 @@
     }
     stats.hashSerialized = ss.GetHash();
     stats.nTotalAmount = nTotalAmount;
+    stats.nDiskSize = view->EstimateSize();
     return true;
 }
 
@@ -982,8 +989,11 @@
             "  \"transactions\": n,      (numeric) The number of transactions\n"
             "  \"txouts\": n,            (numeric) The number of output "
             "transactions\n"
-            "  \"bytes_serialized\": n,  (numeric) The serialized size\n"
+            "  \"bogosize\": n,          (numeric) A database-independent "
+            "metric for UTXO set size\n"
             "  \"hash_serialized\": \"hash\",   (string) The serialized hash\n"
+            "  \"disk_size\": n,         (numeric) The estimated size of the "
+            "chainstate on disk\n"
             "  \"total_amount\": x.xxx          (numeric) The total amount\n"
             "}\n"
             "\nExamples:\n" +
@@ -1000,8 +1010,9 @@
         ret.push_back(Pair("bestblock", stats.hashBlock.GetHex()));
         ret.push_back(Pair("transactions", int64_t(stats.nTransactions)));
         ret.push_back(Pair("txouts", int64_t(stats.nTransactionOutputs)));
-        ret.push_back(Pair("bytes_serialized", int64_t(stats.nSerializedSize)));
+        ret.push_back(Pair("bogosize", int64_t(stats.nBogoSize)));
         ret.push_back(Pair("hash_serialized", stats.hashSerialized.GetHex()));
+        ret.push_back(Pair("disk_size", stats.nDiskSize));
         ret.push_back(
             Pair("total_amount", ValueFromAmount(stats.nTotalAmount)));
     } else {
diff --git a/src/txdb.h b/src/txdb.h
--- a/src/txdb.h
+++ b/src/txdb.h
@@ -76,6 +76,8 @@
     uint256 GetBestBlock() const;
     bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock);
     CCoinsViewCursor *Cursor() const;
+
+    size_t EstimateSize() const override;
 };
 
 /** Specialization of CCoinsViewCursor to iterate over a CCoinsViewDB */
diff --git a/src/txdb.cpp b/src/txdb.cpp
--- a/src/txdb.cpp
+++ b/src/txdb.cpp
@@ -67,6 +67,10 @@
     return db.WriteBatch(batch);
 }
 
+size_t CCoinsViewDB::EstimateSize() const {
+    return db.EstimateSize(DB_COINS, char(DB_COINS + 1));
+}
+
 CBlockTreeDB::CBlockTreeDB(size_t nCacheSize, bool fMemory, bool fWipe)
     : CDBWrapper(GetDataDir() / "blocks" / "index", nCacheSize, fMemory,
                  fWipe) {}