RPC: Add new methods suitable for malleable key pairs management;
[novacoin.git] / src / rpcwallet.cpp
index 046c190..8b3780e 100644 (file)
@@ -7,6 +7,8 @@
 #include "walletdb.h"
 #include "bitcoinrpc.h"
 #include "init.h"
+#include "util.h"
+#include "ntp.h"
 #include "base58.h"
 
 using namespace json_spirit;
@@ -15,12 +17,13 @@ using namespace std;
 int64_t nWalletUnlockTime;
 static CCriticalSection cs_nWalletUnlockTime;
 
-extern void TxToJSON(const CTransaction& tx, const uint256 hashBlock, json_spirit::Object& entry);
+extern int64_t nReserveBalance;
+extern void TxToJSON(const CTransaction& tx, const uint256& hashBlock, json_spirit::Object& entry);
 
 std::string HelpRequiringPassphrase()
 {
     return pwalletMain->IsCrypted()
-        ? "\nrequires wallet passphrase to be set with walletpassphrase first"
+        ? "\n\nRequires wallet passphrase to be set with walletpassphrase first"
         : "";
 }
 
@@ -42,11 +45,11 @@ void WalletTxToJSON(const CWalletTx& wtx, Object& entry)
     {
         entry.push_back(Pair("blockhash", wtx.hashBlock.GetHex()));
         entry.push_back(Pair("blockindex", wtx.nIndex));
-        entry.push_back(Pair("blocktime", (boost::int64_t)(mapBlockIndex[wtx.hashBlock]->nTime)));
+        entry.push_back(Pair("blocktime", (int64_t)(mapBlockIndex[wtx.hashBlock]->nTime)));
     }
     entry.push_back(Pair("txid", wtx.GetHash().GetHex()));
-    entry.push_back(Pair("time", (boost::int64_t)wtx.GetTxTime()));
-    entry.push_back(Pair("timereceived", (boost::int64_t)wtx.nTimeReceived));
+    entry.push_back(Pair("time", (int64_t)wtx.GetTxTime()));
+    entry.push_back(Pair("timereceived", (int64_t)wtx.nTimeReceived));
     BOOST_FOREACH(const PAIRTYPE(string,string)& item, wtx.mapValue)
         entry.push_back(Pair(item.first, item.second));
 }
@@ -69,7 +72,7 @@ Value getinfo(const Array& params, bool fHelp)
     proxyType proxy;
     GetProxy(NET_IPV4, proxy);
 
-    Object obj, diff;
+    Object obj, diff, timestamping;
     obj.push_back(Pair("version",       FormatFullVersion()));
     obj.push_back(Pair("protocolversion",(int)PROTOCOL_VERSION));
     obj.push_back(Pair("walletversion", pwalletMain->GetVersion()));
@@ -78,7 +81,18 @@ Value getinfo(const Array& params, bool fHelp)
     obj.push_back(Pair("newmint",       ValueFromAmount(pwalletMain->GetNewMint())));
     obj.push_back(Pair("stake",         ValueFromAmount(pwalletMain->GetStake())));
     obj.push_back(Pair("blocks",        (int)nBestHeight));
-    obj.push_back(Pair("timeoffset",    (boost::int64_t)GetTimeOffset()));
+
+    timestamping.push_back(Pair("systemclock", GetTime()));
+    timestamping.push_back(Pair("adjustedtime", GetAdjustedTime()));
+
+    int64_t nNtpOffset = GetNtpOffset(),
+            nP2POffset = GetNodesOffset();
+
+    timestamping.push_back(Pair("ntpoffset", nNtpOffset != INT64_MAX ? nNtpOffset : Value::null));
+    timestamping.push_back(Pair("p2poffset", nP2POffset != INT64_MAX ? nP2POffset : Value::null));
+
+    obj.push_back(Pair("timestamping", timestamping));
+
     obj.push_back(Pair("moneysupply",   ValueFromAmount(pindexBest->nMoneySupply)));
     obj.push_back(Pair("connections",   (int)vNodes.size()));
     obj.push_back(Pair("proxy",         (proxy.first.IsValid() ? proxy.first.ToStringIPPort() : string())));
@@ -89,12 +103,12 @@ Value getinfo(const Array& params, bool fHelp)
     obj.push_back(Pair("difficulty",    diff));
 
     obj.push_back(Pair("testnet",       fTestNet));
-    obj.push_back(Pair("keypoololdest", (boost::int64_t)pwalletMain->GetOldestKeyPoolTime()));
+    obj.push_back(Pair("keypoololdest", (int64_t)pwalletMain->GetOldestKeyPoolTime()));
     obj.push_back(Pair("keypoolsize",   (int)pwalletMain->GetKeyPoolSize()));
     obj.push_back(Pair("paytxfee",      ValueFromAmount(nTransactionFee)));
     obj.push_back(Pair("mininput",      ValueFromAmount(nMinimumInputValue)));
     if (pwalletMain->IsCrypted())
-        obj.push_back(Pair("unlocked_until", (boost::int64_t)nWalletUnlockTime / 1000));
+        obj.push_back(Pair("unlocked_until", (int64_t)nWalletUnlockTime / 1000));
     obj.push_back(Pair("errors",        GetWarnings("statusbar")));
     return obj;
 }
@@ -263,7 +277,7 @@ Value mergecoins(const Array& params, bool fHelp)
             "<amount> is resulting inputs sum\n"
             "<minvalue> is minimum value of inputs which are used in join process\n"
             "<outputvalue> is resulting value of inputs which will be created\n"
-            "All values are real and and rounded to the nearest " + FormatMoney(MIN_TXOUT_AMOUNT)
+            "All values are real and and rounded to the nearest " + FormatMoney(nMinimumInputValue)
             + HelpRequiringPassphrase());
 
     if (pwalletMain->IsLocked())
@@ -278,13 +292,13 @@ Value mergecoins(const Array& params, bool fHelp)
     // Output amount
     int64_t nOutputValue = AmountFromValue(params[2]);
 
-    if (nAmount < MIN_TXOUT_AMOUNT)
+    if (nAmount < nMinimumInputValue)
         throw JSONRPCError(-101, "Send amount too small");
 
-    if (nMinValue < MIN_TXOUT_AMOUNT)
+    if (nMinValue < nMinimumInputValue)
         throw JSONRPCError(-101, "Max value too small");
 
-    if (nOutputValue < MIN_TXOUT_AMOUNT)
+    if (nOutputValue < nMinimumInputValue)
         throw JSONRPCError(-101, "Output value too small");
 
     if (nOutputValue < nMinValue)
@@ -306,7 +320,7 @@ Value sendtoaddress(const Array& params, bool fHelp)
     if (fHelp || params.size() < 2 || params.size() > 4)
         throw runtime_error(
             "sendtoaddress <novacoinaddress> <amount> [comment] [comment-to]\n"
-            "<amount> is a real and is rounded to the nearest " + FormatMoney(MIN_TXOUT_AMOUNT)
+            "<amount> is a real and is rounded to the nearest " + FormatMoney(nMinimumInputValue)
             + HelpRequiringPassphrase());
 
     CBitcoinAddress address(params[0].get_str());
@@ -316,7 +330,7 @@ Value sendtoaddress(const Array& params, bool fHelp)
     // Amount
     int64_t nAmount = AmountFromValue(params[1]);
 
-    if (nAmount < MIN_TXOUT_AMOUNT)
+    if (nAmount < nMinimumInputValue)
         throw JSONRPCError(-101, "Send amount too small");
 
     // Wallet comments
@@ -453,7 +467,7 @@ Value getreceivedbyaddress(const Array& params, bool fHelp)
         throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid NovaCoin address");
     scriptPubKey.SetDestination(address.Get());
     if (!IsMine(*pwalletMain,scriptPubKey))
-        return (double)0.0;
+        return 0.0;
 
     // Minimum confirmations
     int nMinDepth = 1;
@@ -629,7 +643,7 @@ Value movecmd(const Array& params, bool fHelp)
     string strTo = AccountFromValue(params[1]);
     int64_t nAmount = AmountFromValue(params[2]);
 
-    if (nAmount < MIN_TXOUT_AMOUNT)
+    if (nAmount < nMinimumInputValue)
         throw JSONRPCError(-101, "Send amount too small");
 
     if (params.size() > 3)
@@ -677,7 +691,7 @@ Value sendfrom(const Array& params, bool fHelp)
     if (fHelp || params.size() < 3 || params.size() > 6)
         throw runtime_error(
             "sendfrom <fromaccount> <tonovacoinaddress> <amount> [minconf=1] [comment] [comment-to]\n"
-            "<amount> is a real and is rounded to the nearest " + FormatMoney(MIN_TXOUT_AMOUNT)
+            "<amount> is a real and is rounded to the nearest " + FormatMoney(nMinimumInputValue)
             + HelpRequiringPassphrase());
 
     string strAccount = AccountFromValue(params[0]);
@@ -686,7 +700,7 @@ Value sendfrom(const Array& params, bool fHelp)
         throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid NovaCoin address");
     int64_t nAmount = AmountFromValue(params[2]);
 
-    if (nAmount < MIN_TXOUT_AMOUNT)
+    if (nAmount < nMinimumInputValue)
         throw JSONRPCError(-101, "Send amount too small");
 
     int nMinDepth = 1;
@@ -720,7 +734,7 @@ Value sendmany(const Array& params, bool fHelp)
 {
     if (fHelp || params.size() < 2 || params.size() > 4)
         throw runtime_error(
-            "sendmany <fromaccount> {address:amount,...} [minconf=1] [comment]\n"
+            "sendmany <fromaccount> '{address:amount,...}' [minconf=1] [comment]\n"
             "amounts are double-precision floating point numbers"
             + HelpRequiringPassphrase());
 
@@ -753,7 +767,7 @@ Value sendmany(const Array& params, bool fHelp)
         scriptPubKey.SetDestination(address.Get());
         int64_t nAmount = AmountFromValue(s.value_);
 
-        if (nAmount < MIN_TXOUT_AMOUNT)
+        if (nAmount < nMinimumInputValue)
             throw JSONRPCError(-101, "Send amount too small");
 
         totalAmount += nAmount;
@@ -809,6 +823,8 @@ Value addmultisigaddress(const Array& params, bool fHelp)
         throw runtime_error(
             strprintf("not enough keys supplied "
                       "(got %" PRIszu " keys, but need at least %d to redeem)", keys.size(), nRequired));
+    if (keys.size() > 16)
+        throw runtime_error("Number of addresses involved in the multisignature address creation > 16\nReduce the number");
     std::vector<CKey> pubkeys;
     pubkeys.resize(keys.size());
     for (unsigned int i = 0; i < keys.size(); i++)
@@ -1123,7 +1139,7 @@ void AcentryToJSON(const CAccountingEntry& acentry, const string& strAccount, Ar
         Object entry;
         entry.push_back(Pair("account", acentry.strAccount));
         entry.push_back(Pair("category", "move"));
-        entry.push_back(Pair("time", (boost::int64_t)acentry.nTime));
+        entry.push_back(Pair("time", (int64_t)acentry.nTime));
         entry.push_back(Pair("amount", ValueFromAmount(acentry.nCreditDebit)));
         entry.push_back(Pair("otheraccount", acentry.strOtherAccount));
         entry.push_back(Pair("comment", acentry.strComment));
@@ -1408,10 +1424,12 @@ Value keypoolrefill(const Array& params, bool fHelp)
     if (fHelp || params.size() > 1)
         throw runtime_error(
             "keypoolrefill [new-size]\n"
-            "Fills the keypool."
+            "Fills the keypool.\n"
+            "IMPORTANT: Any previous backups you have made of your wallet file "
+            "should be replaced with the newly generated one."
             + HelpRequiringPassphrase());
 
-    unsigned int nSize = max<unsigned int>(GetArg("-keypool", 100), 0);
+    unsigned int nSize = max<unsigned int>(GetArgUInt("-keypool", 100), 0);
     if (params.size() > 0) {
         if (params[0].get_int() < 0)
             throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected valid size");
@@ -1428,6 +1446,33 @@ Value keypoolrefill(const Array& params, bool fHelp)
     return Value::null;
 }
 
+Value keypoolreset(const Array& params, bool fHelp)
+{
+    if (fHelp || params.size() > 1)
+        throw runtime_error(
+            "keypoolreset [new-size]\n"
+            "Resets the keypool.\n"
+            "IMPORTANT: Any previous backups you have made of your wallet file "
+            "should be replaced with the newly generated one."
+            + HelpRequiringPassphrase());
+
+    unsigned int nSize = max<unsigned int>(GetArgUInt("-keypool", 100), 0);
+    if (params.size() > 0) {
+        if (params[0].get_int() < 0)
+            throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected valid size");
+        nSize = (unsigned int) params[0].get_int();
+    }
+
+    EnsureWalletIsUnlocked();
+
+    pwalletMain->NewKeyPool(nSize);
+
+    if (pwalletMain->GetKeyPoolSize() < nSize)
+        throw JSONRPCError(RPC_WALLET_ERROR, "Error refreshing keypool.");
+
+    return Value::null;
+}
+
 
 void ThreadTopUpKeyPool(void* parg)
 {
@@ -1721,7 +1766,6 @@ Value reservebalance(const Array& params, bool fHelp)
     }
 
     Object result;
-    int64_t nReserveBalance = 0;
     if (mapArgs.count("-reservebalance") && !ParseMoney(mapArgs["-reservebalance"], nReserveBalance))
         throw runtime_error("invalid reserve balance amount\n");
     result.push_back(Pair("reserve", (nReserveBalance > 0)));
@@ -1789,25 +1833,147 @@ Value resendtx(const Array& params, bool fHelp)
     return Value::null;
 }
 
-// ppcoin: make a public-private key pair
+// Make a public-private key pair
 Value makekeypair(const Array& params, bool fHelp)
 {
-    if (fHelp || params.size() > 1)
+    if (fHelp || params.size() > 0)
         throw runtime_error(
-            "makekeypair [prefix]\n"
-            "Make a public/private key pair.\n"
-            "[prefix] is optional preferred prefix for the public key.\n");
+            "makekeypair\n"
+            "Make a public/private key pair.\n");
 
     string strPrefix = "";
     if (params.size() > 0)
         strPrefix = params[0].get_str();
+
     CKey key;
-    key.MakeNewKey(false);
+    key.MakeNewKey(true);
 
     CPrivKey vchPrivKey = key.GetPrivKey();
     Object result;
     result.push_back(Pair("PrivateKey", HexStr<CPrivKey::iterator>(vchPrivKey.begin(), vchPrivKey.end())));
+
+    bool fCompressed;
+    CSecret vchSecret = key.GetSecret(fCompressed);
+    result.push_back(Pair("Secret", HexStr<CSecret::iterator>(vchSecret.begin(), vchSecret.end())));
     result.push_back(Pair("PublicKey", HexStr(key.GetPubKey().Raw())));
     return result;
 }
+
+Value newmalleablekey(const Array& params, bool fHelp)
+{
+    if (fHelp || params.size() > 0)
+        throw runtime_error(
+            "newmalleablekey\n"
+            "Make a malleable public/private key pair.\n");
+
+    if (!fTestNet)
+        throw runtime_error("This feature has been disabled for mainNet clients");
+
+    CMalleableKeyView keyView = pwalletMain->GenerateNewMalleableKey();
+
+    CMalleableKey mKey;
+    if (!pwalletMain->GetMalleableKey(keyView, mKey))
+        throw runtime_error("Unable to generate new malleable key");
+
+    Object result;
+    result.push_back(Pair("PublicPair", mKey.GetMalleablePubKey().ToString()));
+    result.push_back(Pair("KeyView", keyView.ToString()));
+
+    return result;
+}
+
+Value dumpmalleablekey(const Array& params, bool fHelp)
+{
+    if (fHelp || params.size() != 1)
+        throw runtime_error (
+            "dumpmalleablekey <Key view>\n"
+            "Dump the private and public key pairs, which correspond to provided key view.\n");
+
+    CMalleableKey mKey;
+    CMalleableKeyView keyView;
+    keyView.SetString(params[0].get_str());
+
+    if (!pwalletMain->GetMalleableKey(keyView, mKey))
+        throw runtime_error("There is no such item in the wallet");
+
+    Object result;
+    result.push_back(Pair("PrivatePair", mKey.ToString()));
+    result.push_back(Pair("PublicPair", mKey.GetMalleablePubKey().ToString()));
+
+    return result;
+}
+
+Value adjustmalleablekey(const Array& params, bool fHelp)
+{
+    if (fHelp || params.size() != 3)
+        throw runtime_error(
+            "adjustmalleablekey <Malleable key data> <Public key variant data> <R data>\n"
+            "Calculate new private key using provided malleable key, public key and R data.\n");
+
+    CMalleableKey malleableKey;
+    malleableKey.SetString(params[0].get_str());
+
+    CKey privKeyVariant;
+    CPubKey vchPubKeyVariant = CPubKey(ParseHex(params[1].get_str()));
+
+    CPubKey R(ParseHex(params[2].get_str()));
+
+    if (!malleableKey.CheckKeyVariant(R,vchPubKeyVariant, privKeyVariant)) {
+        throw runtime_error("Unable to calculate the private key");
+    }
+
+    Object result;
+    bool fCompressed;
+    CSecret vchPrivKeyVariant = privKeyVariant.GetSecret(fCompressed);
+
+    result.push_back(Pair("PrivateKey", CBitcoinSecret(vchPrivKeyVariant, fCompressed).ToString()));
+
+    return result;
+}
+
+Value adjustmalleablepubkey(const Array& params, bool fHelp)
+{
+    if (fHelp || params.size() > 2 || params.size() == 0)
+        throw runtime_error(
+            "adjustmalleablepubkey <Malleable public key data>\n"
+            "Calculate new public key using provided malleable public key data.\n");
+
+    string pubKeyPair = params[0].get_str();
+    CMalleablePubKey malleablePubKey;
+
+    if (pubKeyPair.size() == 138) {
+        CDataStream ssPublicBytes(ParseHex(pubKeyPair), SER_NETWORK, PROTOCOL_VERSION);
+        ssPublicBytes >> malleablePubKey;
+    } else
+        malleablePubKey.SetString(pubKeyPair);
+
+    CPubKey R, vchPubKeyVariant;
+    malleablePubKey.GetVariant(R, vchPubKeyVariant);
+
+    Object result;
+    result.push_back(Pair("R", HexStr(R.Raw())));
+    result.push_back(Pair("PubkeyVariant", HexStr(vchPubKeyVariant.Raw())));
+    result.push_back(Pair("KeyVariantID", CBitcoinAddress(vchPubKeyVariant.GetID()).ToString()));
+
+    return result;
+}
+
+Value listmalleableviews(const Array& params, bool fHelp)
+{
+    if (fHelp || params.size() != 0)
+        throw runtime_error(
+            "listmalleableviews\n"
+            "Get list of views for generated malleable keys.\n");
+
+    std::list<CMalleableKeyView> keyViewList;
+    pwalletMain->ListMalleableViews(keyViewList);
+
+    Array result;
+    BOOST_FOREACH(const CMalleableKeyView &keyView, keyViewList)
+    {
+        result.push_back(keyView.ToString());
+    }
+
+    return result;
+}
+