diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp index 4547c54c..e90c8416 100644 --- a/src/bitcoinrpc.cpp +++ b/src/bitcoinrpc.cpp @@ -1218,6 +1218,7 @@ Array RPCConvertValues(const std::string &strMethod, const std::vector 1) ConvertTo(params[1]); if (strMethod == "listunspent" && n > 2) ConvertTo(params[2]); if (strMethod == "getblock" && n > 1) ConvertTo(params[1]); + if (strMethod == "createwalletuser" && n > 1) ConvertTo(params[1]); if (strMethod == "getrawtransaction" && n > 1) ConvertTo(params[1]); if (strMethod == "gettxout" && n > 1) ConvertTo(params[1]); if (strMethod == "gettxout" && n > 2) ConvertTo(params[2]); diff --git a/src/rpcrawtransaction.cpp b/src/rpcrawtransaction.cpp index 8fcca6ad..c641d108 100644 --- a/src/rpcrawtransaction.cpp +++ b/src/rpcrawtransaction.cpp @@ -11,6 +11,7 @@ #include "init.h" #include "net.h" #include "wallet.h" +#include "twister.h" using namespace std; using namespace boost; @@ -145,9 +146,9 @@ Value getrawtransaction(const Array& params, bool fHelp) Value createrawtransaction(const Array& params, bool fHelp) { - if (fHelp || params.size() != 2) + if (fHelp || (params.size() != 2 && params.size() != 3)) throw runtime_error( - "createrawtransaction \n" + "createrawtransaction [signedByOldKey]\n" "Create a transaction registering a new user\n" "Returns hex-encoded raw transaction.\n" "it is not stored in the wallet or transmitted to the network."); @@ -165,6 +166,10 @@ Value createrawtransaction(const Array& params, bool fHelp) throw JSONRPCError(RPC_INTERNAL_ERROR, "pubkey is not valid"); rawTx.pubKey << vch; + if( params.size() > 2) { + vector vchSign(ParseHexV(params[2], "signedByOldKey")); + rawTx.pubKey << vchSign; + } DoTxProofOfWork(rawTx); @@ -221,6 +226,13 @@ Value sendrawtransaction(const Array& params, bool fHelp) uint256 hashBlock; CTransaction tx2; fHave = GetTransaction(hashTx, tx2, hashBlock); + + // treat replacement as !fHave + if( fHave && verifyDuplicateOrReplacementTx(tx, false, true) ) { + printf("verifyDuplicateOrReplacementTx true\n"); + fHave = false; + } + if (!fHave) { // push to local node CValidationState state; @@ -255,6 +267,9 @@ Value sendnewusertransaction(const Array& params, bool fHelp) throw JSONRPCError(RPC_INVALID_PARAMETER, "username must be string"); string strUsername = params[0].get_str(); + CKeyID oldKeyID; + bool replaceKey = pwalletMain->GetKeyIdBeingReplaced(strUsername, oldKeyID); + CKeyID keyID; if( !pwalletMain->GetKeyIdFromUsername(strUsername, keyID) ) throw JSONRPCError(RPC_WALLET_INVALID_ACCOUNT_NAME, "Error: username must exist in wallet"); @@ -267,14 +282,27 @@ Value sendnewusertransaction(const Array& params, bool fHelp) CTransaction txOut; uint256 hashBlock; uint256 userhash = SerializeHash(strUsername); - if( GetTransaction(userhash, txOut, hashBlock) ) + bool userInTxIndex = GetTransaction(userhash, txOut, hashBlock); + if( !replaceKey && userInTxIndex ) throw JSONRPCError(RPC_WALLET_INVALID_ACCOUNT_NAME, "Error: this username exists in tx database"); + if( replaceKey && !userInTxIndex ) + throw JSONRPCError(RPC_WALLET_INVALID_ACCOUNT_NAME, "Error: key replacemente requires old key in tx database"); Array createTxParams; createTxParams.push_back(strUsername); createTxParams.push_back(HexStr(pubkey)); + if( replaceKey ) { + string newPubKey((const char *)pubkey.begin(), static_cast(pubkey.size())); + string signedByOldKey; + signedByOldKey = createSignature(newPubKey, oldKeyID); + createTxParams.push_back(HexStr(signedByOldKey)); + } Value txValue = createrawtransaction(createTxParams, false); + if( replaceKey ) { + pwalletMain->ForgetReplacementMap(strUsername); + } + std::string strTxHex = txValue.get_str(); Array sendTxParams; sendTxParams.push_back(strTxHex); diff --git a/src/rpcwallet.cpp b/src/rpcwallet.cpp index 1116d7f7..4c29199f 100644 --- a/src/rpcwallet.cpp +++ b/src/rpcwallet.cpp @@ -90,9 +90,9 @@ Value getinfo(const Array& params, bool fHelp) Value createwalletuser(const Array& params, bool fHelp) { - if (fHelp || params.size() != 1) + if (fHelp || (params.size() != 1 && params.size() != 2)) throw runtime_error( - "createwalletuser \n" + "createwalletuser [replacekey]\n" "Create a new key pair for user and add it to wallet\n" "Use sendnewusertransaction to publish it to the network.\n" "Returns key secret (keep it safe)"); @@ -101,18 +101,28 @@ Value createwalletuser(const Array& params, bool fHelp) string strUsername = params[0].get_str(); + bool replaceKey = false; + if (params.size() > 1) + replaceKey = params[1].get_bool(); + CKeyID keyID; - if( pwalletMain->GetKeyIdFromUsername(strUsername, keyID) ) + bool keyInWallet = pwalletMain->GetKeyIdFromUsername(strUsername, keyID); + if( keyInWallet && !replaceKey ) throw JSONRPCError(RPC_WALLET_INVALID_ACCOUNT_NAME, "Error: this username exists in wallet"); + if( !keyInWallet && replaceKey ) + throw JSONRPCError(RPC_WALLET_INVALID_ACCOUNT_NAME, "Error: replacekey given but old key not in wallet"); uint256 userhash = SerializeHash(strUsername); printf("createwalletuser: usernamehash(%s) = %s\n", strUsername.c_str(), userhash.GetHex().c_str()); CTransaction txOut; uint256 hashBlock; - if( GetTransaction(userhash, txOut, hashBlock) ) + if( GetTransaction(userhash, txOut, hashBlock) && !replaceKey ) throw JSONRPCError(RPC_WALLET_INVALID_ACCOUNT_NAME, "Error: this username exists in tx database"); + if( replaceKey && !pwalletMain->MoveKeyForReplacement(strUsername) ) + throw JSONRPCError(RPC_WALLET_ERROR, "Error: moving key for replacement"); + // Generate a new key that is added to wallet CPubKey newKey = pwalletMain->GenerateNewKey(strUsername); keyID = newKey.GetID(); diff --git a/src/twister.cpp b/src/twister.cpp index c9b5675a..f51aa692 100644 --- a/src/twister.cpp +++ b/src/twister.cpp @@ -436,22 +436,16 @@ void stopSessionTorrent() printf("libtorrent + dht stopped\n"); } -std::string createSignature(std::string const &strMessage, std::string const &strUsername) +std::string createSignature(std::string const &strMessage, CKeyID &keyID) { if (pwalletMain->IsLocked()) { printf("createSignature: Error please enter the wallet passphrase with walletpassphrase first.\n"); return std::string(); } - CKeyID keyID; - if( !pwalletMain->GetKeyIdFromUsername(strUsername, keyID) ) { - printf("createSignature: user '%s' unknown.\n", strUsername.c_str()); - return std::string(); - } - CKey key; if (!pwalletMain->GetKey(keyID, key)) { - printf("createSignature: private key not available for user '%s'.\n", strUsername.c_str()); + printf("createSignature: private key not available for given keyid.\n"); return std::string(); } @@ -468,6 +462,23 @@ std::string createSignature(std::string const &strMessage, std::string const &st return std::string((const char *)&vchSig[0], vchSig.size()); } +std::string createSignature(std::string const &strMessage, std::string const &strUsername) +{ + if (pwalletMain->IsLocked()) { + printf("createSignature: Error please enter the wallet passphrase with walletpassphrase first.\n"); + return std::string(); + } + + CKeyID keyID; + if( !pwalletMain->GetKeyIdFromUsername(strUsername, keyID) ) { + printf("createSignature: user '%s' unknown.\n", strUsername.c_str()); + return std::string(); + } + + return createSignature( strMessage, keyID ); +} + + bool getUserPubKey(std::string const &strUsername, CPubKey &pubkey) { { diff --git a/src/twister.h b/src/twister.h index 21c10b7e..2b858891 100644 --- a/src/twister.h +++ b/src/twister.h @@ -2,6 +2,7 @@ #define TWISTER_H #include "util.h" +#include "key.h" #include #define LIBTORRENT_PORT_OFFSET 1000 @@ -19,6 +20,7 @@ public: void startSessionTorrent(boost::thread_group& threadGroup); void stopSessionTorrent(); +std::string createSignature(std::string const &strMessage, CKeyID &keyID); std::string createSignature(std::string const &strMessage, std::string const &strUsername); bool verifySignature(std::string const &strMessage, std::string const &strUsername, std::string const &strSign); diff --git a/src/wallet.cpp b/src/wallet.cpp index a3b33726..d3fd5acd 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -121,6 +121,41 @@ bool CWallet::GetUsernameFromKeyId(CKeyID keyid, std::string &username) return false; } +bool CWallet::MoveKeyForReplacement(std::string username) +{ + for (std::map::iterator it = mapKeyMetadata.begin(); it != mapKeyMetadata.end(); it++) { + if (it->second.username == username) { + mapKeyReplacement.insert(make_pair(it->first, username)); + mapKeyMetadata.erase(it); + return true; + } + } + return false; +} + +bool CWallet::GetKeyIdBeingReplaced(std::string username, CKeyID &keyid) +{ + for (std::map::const_iterator it = mapKeyReplacement.begin(); it != mapKeyReplacement.end(); it++) { + if (it->second == username) { + keyid = it->first; + return true; + } + } + return false; +} + +bool CWallet::ForgetReplacementMap(std::string username) +{ + for (std::map::iterator it = mapKeyReplacement.begin(); it != mapKeyReplacement.end(); it++) { + if (it->second == username) { + mapKeyReplacement.erase(it); + return true; + } + } + return false; +} + + bool CWallet::LoadCryptedKey(const CPubKey &vchPubKey, const std::vector &vchCryptedSecret) { return CCryptoKeyStore::AddCryptedKey(vchPubKey, vchCryptedSecret); diff --git a/src/wallet.h b/src/wallet.h index 1d2388cb..428fcdd7 100644 --- a/src/wallet.h +++ b/src/wallet.h @@ -84,6 +84,7 @@ public: std::set setKeyPool; std::map mapKeyMetadata; + std::map mapKeyReplacement; typedef std::map MasterKeyMap; MasterKeyMap mapMasterKeys; @@ -200,6 +201,10 @@ public: // get the current wallet format (the oldest client version guaranteed to understand this wallet) int GetVersion() { return nWalletVersion; } + bool MoveKeyForReplacement(std::string username); + bool GetKeyIdBeingReplaced(std::string username, CKeyID &keyid); + bool ForgetReplacementMap(std::string username); + /** Address book entry changed. * @note called with lock cs_wallet held. */ diff --git a/twister-test.py b/twister-test.py index 44258828..32928591 100755 --- a/twister-test.py +++ b/twister-test.py @@ -11,7 +11,7 @@ n = int(sys.argv[2]) datadir = "/tmp/twister%d" % n port = "%d" % (30000+n) rpcport = "%d" % (40000+n) -rpcline = " -rpcuser=user -rpcpassword=pwd -rpcport=" +rpcline = " -rpcuser=user -rpcpassword=pwd -rpcallowip=127.0.0.1 -rpcport=" rpccfg = rpcline + rpcport rpccfg1 = rpcline + "40001"