From 1431b9e03ca55b761c4aef8daa29b2d42e8534c5 Mon Sep 17 00:00:00 2001 From: Jimmy Kiselak Date: Wed, 14 Jan 2015 19:17:19 -0500 Subject: [PATCH] Enable spending NCC transactions (both updating and abandoning their claims), and enable printing them out with their info --- src/Makefile.am | 2 + src/coins.cpp | 2 - src/main.cpp | 70 +--------- src/main.h | 4 - src/ncc.cpp | 66 ++++++++++ src/ncc.h | 11 ++ src/rpcclient.cpp | 4 + src/rpcserver.cpp | 4 + src/rpcserver.h | 4 + src/rpcwallet.cpp | 298 +++++++++++++++++++++++++++++++++++++++++- src/script/sign.cpp | 6 +- src/wallet.cpp | 44 +++++-- src/wallet.h | 4 +- src/wallet_ismine.cpp | 22 +++- src/wallet_ismine.h | 3 +- 15 files changed, 446 insertions(+), 98 deletions(-) create mode 100644 src/ncc.cpp create mode 100644 src/ncc.h diff --git a/src/Makefile.am b/src/Makefile.am index bc2b1aff9..51ed14292 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -103,6 +103,7 @@ BITCOIN_CORE_H = \ merkleblock.h \ miner.h \ mruset.h \ + ncc.h \ netbase.h \ net.h \ noui.h \ @@ -247,6 +248,7 @@ libbitcoin_common_a_SOURCES = \ hash.cpp \ key.cpp \ keystore.cpp \ + ncc.cpp \ netbase.cpp \ protocol.cpp \ pubkey.cpp \ diff --git a/src/coins.cpp b/src/coins.cpp index 921cbdac4..ef4f96fde 100644 --- a/src/coins.cpp +++ b/src/coins.cpp @@ -6,8 +6,6 @@ #include "random.h" -#include "util.h" - #include /** diff --git a/src/main.cpp b/src/main.cpp index 479a69d97..496bf574d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -21,6 +21,7 @@ #include "undo.h" #include "util.h" #include "utilmoneystr.h" +#include "ncc.h" #include @@ -619,75 +620,6 @@ unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans) } - - - -bool DecodeNCCScript(const CScript& scriptIn, int& op, std::vector >& vvchParams) -{ - CScript::const_iterator pc = scriptIn.begin(); - return DecodeNCCScript(scriptIn, op, vvchParams, pc); -} - -bool DecodeNCCScript(const CScript& scriptIn, int& op, std::vector >& vvchParams, CScript::const_iterator& pc) -{ - opcodetype opcode; - if (!scriptIn.GetOp(pc, opcode)) - { - return false; - } - if (opcode != OP_CLAIM_NAME) - { - return false; - } - - op = opcode; - - std::vector vchName; - std::vector vchValue; - - // The correct format is: - // OP_CLAIM_NAME vchName vchValue OP_DROP2 OP_DROP pubkeyscript - // All others are invalid. - - if (!scriptIn.GetOp(pc, opcode, vchName) || opcode < 0 || opcode > OP_PUSHDATA4) - { - return false; - } - if (!scriptIn.GetOp(pc, opcode, vchValue) || opcode < 0 || opcode > OP_PUSHDATA4) - { - return false; - } - if (!scriptIn.GetOp(pc, opcode) || opcode != OP_2DROP) - { - return false; - } - if (!scriptIn.GetOp(pc, opcode) || opcode != OP_DROP) - { - return false; - } - - vvchParams.push_back(vchName); - vvchParams.push_back(vchValue); - - return true; -} - -CScript StripNCCScriptPrefix(const CScript& scriptIn) -{ - int op; - std::vector > vvchParams; - CScript::const_iterator pc = scriptIn.begin(); - - if (!DecodeNCCScript(scriptIn, op, vvchParams, pc)) - { - return scriptIn; - } - - return CScript(pc, scriptIn.end()); -} - - - bool IsStandardTx(const CTransaction& tx, string& reason) { AssertLockHeld(cs_main); diff --git a/src/main.h b/src/main.h index e083732c8..9163e164b 100644 --- a/src/main.h +++ b/src/main.h @@ -301,10 +301,6 @@ bool IsStandardTx(const CTransaction& tx, std::string& reason); bool IsFinalTx(const CTransaction &tx, int nBlockHeight = 0, int64_t nBlockTime = 0); -bool DecodeNCCScript(const CScript& scriptIn, int& op, std::vector >& vvchParams); -bool DecodeNCCScript(const CScript& scriptIn, int& op, std::vector >& vvchParams, CScript::const_iterator& pc); -CScript StripNCCScriptPrefix(const CScript& scriptIn); - /** * Closure representing one script verification diff --git a/src/ncc.cpp b/src/ncc.cpp new file mode 100644 index 000000000..e201396b5 --- /dev/null +++ b/src/ncc.cpp @@ -0,0 +1,66 @@ +#include "ncc.h" + + +bool DecodeNCCScript(const CScript& scriptIn, int& op, std::vector >& vvchParams) +{ + CScript::const_iterator pc = scriptIn.begin(); + return DecodeNCCScript(scriptIn, op, vvchParams, pc); +} + +bool DecodeNCCScript(const CScript& scriptIn, int& op, std::vector >& vvchParams, CScript::const_iterator& pc) +{ + opcodetype opcode; + if (!scriptIn.GetOp(pc, opcode)) + { + return false; + } + if (opcode != OP_CLAIM_NAME) + { + return false; + } + + op = opcode; + + std::vector vchName; + std::vector vchValue; + + // The correct format is: + // OP_CLAIM_NAME vchName vchValue OP_DROP2 OP_DROP pubkeyscript + // All others are invalid. + + if (!scriptIn.GetOp(pc, opcode, vchName) || opcode < 0 || opcode > OP_PUSHDATA4) + { + return false; + } + if (!scriptIn.GetOp(pc, opcode, vchValue) || opcode < 0 || opcode > OP_PUSHDATA4) + { + return false; + } + if (!scriptIn.GetOp(pc, opcode) || opcode != OP_2DROP) + { + return false; + } + if (!scriptIn.GetOp(pc, opcode) || opcode != OP_DROP) + { + return false; + } + + vvchParams.push_back(vchName); + vvchParams.push_back(vchValue); + + return true; +} + +CScript StripNCCScriptPrefix(const CScript& scriptIn) +{ + int op; + std::vector > vvchParams; + CScript::const_iterator pc = scriptIn.begin(); + + if (!DecodeNCCScript(scriptIn, op, vvchParams, pc)) + { + return scriptIn; + } + + return CScript(pc, scriptIn.end()); +} diff --git a/src/ncc.h b/src/ncc.h new file mode 100644 index 000000000..2f4d0dd8a --- /dev/null +++ b/src/ncc.h @@ -0,0 +1,11 @@ +#ifndef BITCOIN_NCC_H +#define BITCOIN_NCC_H + +#include "script/script.h" +#include + +bool DecodeNCCScript(const CScript& scriptIn, int& op, std::vector >& vvchParams); +bool DecodeNCCScript(const CScript& scriptIn, int& op, std::vector >& vvchParams, CScript::const_iterator& pc); +CScript StripNCCScriptPrefix(const CScript& scriptIn); + +#endif // BITCOIN_NCC_H diff --git a/src/rpcclient.cpp b/src/rpcclient.cpp index 4e45bc32a..170561bbb 100644 --- a/src/rpcclient.cpp +++ b/src/rpcclient.cpp @@ -88,6 +88,10 @@ static const CRPCConvertParam vRPCConvertParams[] = { "estimatepriority", 0 }, { "prioritisetransaction", 1 }, { "prioritisetransaction", 2 }, + { "claimname", 2}, + { "updatename", 2}, + { "abandonname", 2}, + { "listnameclaims", 0}, }; class CRPCConvertTable diff --git a/src/rpcserver.cpp b/src/rpcserver.cpp index 210c7fe97..55d4c81cc 100644 --- a/src/rpcserver.cpp +++ b/src/rpcserver.cpp @@ -340,6 +340,10 @@ static const CRPCCommand vRPCCommands[] = { "wallet", "move", &movecmd, false, false, true }, { "wallet", "sendfrom", &sendfrom, false, false, true }, { "wallet", "sendmany", &sendmany, false, false, true }, + { "wallet", "claimname", &claimname, false, false, true }, + { "wallet", "listnameclaims", &listnameclaims, false, false, true }, + { "wallet", "updatename", &updatename, false, false, true }, + { "wallet", "abandonname", &abandonname, false, false, true }, { "wallet", "sendtoaddress", &sendtoaddress, false, false, true }, { "wallet", "setaccount", &setaccount, true, false, true }, { "wallet", "settxfee", &settxfee, true, false, true }, diff --git a/src/rpcserver.h b/src/rpcserver.h index fb561ab93..3f568cb62 100644 --- a/src/rpcserver.h +++ b/src/rpcserver.h @@ -168,6 +168,10 @@ extern json_spirit::Value getrawchangeaddress(const json_spirit::Array& params, extern json_spirit::Value setaccount(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value getaccount(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value getaddressesbyaccount(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value claimname(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value listnameclaims(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value updatename(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value abandonname(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value sendtoaddress(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value signmessage(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value verifymessage(const json_spirit::Array& params, bool fHelp); diff --git a/src/rpcwallet.cpp b/src/rpcwallet.cpp index 0c4fc71d8..4863f6422 100644 --- a/src/rpcwallet.cpp +++ b/src/rpcwallet.cpp @@ -15,6 +15,7 @@ #include "utilmoneystr.h" #include "wallet.h" #include "walletdb.h" +#include "ncc.h" #include @@ -333,7 +334,8 @@ void ClaimName(const std::vector vchName, const std::vector vchName, const std::vector vchValue, CAmount nAmount, CWalletTx& wtxNew, CWalletTx wtxIn, unsigned int nTxOut) +{ + // Check amount + if (nAmount <= 0) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid amount"); + + if (nAmount - wtxIn.vout[nTxOut].nValue > pwalletMain->GetBalance()) + throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds"); + + string strError; + if (pwalletMain->IsLocked()) + { + strError = "Error: Wallet locked, unable to create transaction!"; + LogPrintf("UpdateName() : %s", strError); + throw JSONRPCError(RPC_WALLET_ERROR, strError); + } + + //Get new address + CPubKey newKey; + if (!pwalletMain->GetKeyFromPool(newKey)) + throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first"); + + CScript scriptPubKey = GetScriptForDestination(CTxDestination(newKey.GetID())); + CScript claimScript = CScript() << OP_CLAIM_NAME << vchName << vchValue << OP_2DROP << OP_DROP; + claimScript = claimScript + scriptPubKey; + + CReserveKey reservekey(pwalletMain); + CAmount nFeeRequired; + if (!pwalletMain->CreateTransaction(claimScript, nAmount, wtxNew, reservekey, nFeeRequired, strError, NULL, &wtxIn, nTxOut)) + { + if (nAmount + nFeeRequired - wtxIn.vout[nTxOut].nValue > pwalletMain->GetBalance()) + strError = strprintf("Error: This transaction requires a transaction fee of at leaste %s because of its amount, complexity, or use of recently received funds!", FormatMoney(nFeeRequired)); + LogPrintf("ClaimName() : %s\n", strError); + throw JSONRPCError(RPC_WALLET_ERROR, strError); + } + if (!pwalletMain->CommitTransaction(wtxNew, reservekey)) + throw JSONRPCError(RPC_WALLET_ERROR, "Error: The transaction was rejected! This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here."); +} + + +Value updatename(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 3) + throw runtime_error( + "updatename \"txid\" \"value\" amount\n" + "Create a transaction which issues a claim assigning a value to a name, spending the previous txout which issued a claim over the same name and therefore superseding that claim. The claim will be authoritative if the transaction amount is greater than the transaction amount of all other unspent transactions which issue a claim over the same name, and it will remain authoritative as long as it remains unspent and there are no greater unspent transactions issuing a claim over the same name.\n" + + HelpRequiringPassphrase() + + "\nArguments:\n" + "1. \"txid\" (string, required) The transaction containing the unspent txout which should be spent.\n" + "2. \"value\" (string, required) The value to assign to the name.\n" + "3. \"amount\" (numeric, required) The amount in ncc to send. eg 0.1\n" + "\nResult:\n" + "\"transactionid\" (string) The new transaction id.\n" + ); + uint256 hash; + hash.SetHex(params[0].get_str()); + + std::vector vchName; + string sValue = params[1].get_str(); + std::vector vchValue (sValue.begin(), sValue.end()); + CAmount nAmount = AmountFromValue(params[2]); + + isminefilter filter = ISMINE_NCC; + + Object entry; + if (!pwalletMain->mapWallet.count(hash)) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid or non-wallet transaction id"); + + const CWalletTx& wtx = pwalletMain->mapWallet[hash]; + int op; + std::vector > vvchParams; + CWalletTx wtxNew; + bool fFound = false; + for (unsigned int i = 0; !fFound && i < wtx.vout.size(); i++) + { + if ((filter & pwalletMain->IsMine(wtx.vout[i]))) + { + if (DecodeNCCScript(wtx.vout[i].scriptPubKey, op, vvchParams)) + { + vchName = vvchParams[0]; + EnsureWalletIsUnlocked(); + UpdateName(vchName, vchValue, nAmount, wtxNew, wtx, i); + fFound = true; + } + } + } + if (!fFound) + throw runtime_error("Error: The given transaction contains no NCC scripts owned by this wallet"); + return wtxNew.GetHash().GetHex(); +} + + +void AbandonName(const CTxDestination &address, CAmount nAmount, CWalletTx& wtxNew, CWalletTx wtxIn, unsigned int nTxOut) +{ + // Check amount + if (nAmount <= 0) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid amount"); + + if (nAmount - wtxIn.vout[nTxOut].nValue > pwalletMain->GetBalance()) + throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds"); + + string strError; + if (pwalletMain->IsLocked()) + { + strError = "Error: Wallet locked, unable to create transaction!"; + LogPrintf("AbandonName() : %s", strError); + throw JSONRPCError(RPC_WALLET_ERROR, strError); + } + + CScript scriptPubKey = GetScriptForDestination(address); + + CReserveKey reservekey(pwalletMain); + CAmount nFeeRequired; + if (!pwalletMain->CreateTransaction(scriptPubKey, nAmount, wtxNew, reservekey, nFeeRequired, strError, NULL, &wtxIn, nTxOut)) + { + if (nAmount + nFeeRequired - wtxIn.vout[nTxOut].nValue > pwalletMain->GetBalance()) + strError = strprintf("Error: This transaction requires a transaction fee of a least %s because of its amount, complexity, or use of recently received funds!", FormatMoney(nFeeRequired)); + LogPrintf("AbandonName() : %s\n", strError); + throw JSONRPCError(RPC_WALLET_ERROR, strError); + } + if (!pwalletMain->CommitTransaction(wtxNew, reservekey)) + throw JSONRPCError(RPC_WALLET_ERROR, "Error: The transaction was rejected! This might happen if some of the coins in your wallet were already spent, such as if you used a copy of a wallet.dat and coins were spent in the copy but not marked as spent here."); +} + +Value abandonname(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 3) + throw runtime_error( + "abandonname \"txid\" \"bitcoinaddress\" \"amount\"\n" + "Create a transaction which spends a txout which assigned a value to a name, effectively abandoning that claim.\n" + + HelpRequiringPassphrase() + + "\nArguments:\n" + "1. \"txid\" (string, required) The transaction containing the unspent txout which should be spent.\n" + "2. \"bitcoinaddress\" (string, required) The bitcoin address to send to.\n" + "3. \"amount\" (numeric, required) The amount to send to the bitcoin address. eg 0.1\n" + "\nResult:\n" + "\"transactionid\" (string) The new transaction id.\n" + ); + + uint256 hash; + hash.SetHex(params[0].get_str()); + + CBitcoinAddress address(params[1].get_str()); + if (!address.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); + + CAmount nAmount = AmountFromValue(params[2]); + + isminefilter filter = ISMINE_NCC; + + Object entry; + if (!pwalletMain->mapWallet.count(hash)) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid or non-wallet transaction id"); + + const CWalletTx& wtx = pwalletMain->mapWallet[hash]; + int op; + std::vector > vvchParams; + CWalletTx wtxNew; + bool fFound = false; + for (unsigned int i = 0; !fFound && i < wtx.vout.size(); i++) + { + if ((filter & pwalletMain->IsMine(wtx.vout[i]))) + { + if (DecodeNCCScript(wtx.vout[i].scriptPubKey, op, vvchParams)) + { + EnsureWalletIsUnlocked(); + AbandonName(address.Get(), nAmount, wtxNew, wtx, i); + fFound = true; + } + } + } + if (!fFound) + throw runtime_error("Error: The given transaction contains no NCC scripts owned by this wallet"); + return wtxNew.GetHash().GetHex(); +} + + +static void MaybePushAddress(Object & entry, const CTxDestination &dest); + + +void ListNameClaims(const CWalletTx& wtx, const string& strAccount, int nMinDepth, Array& ret, const isminefilter& filter, bool list_spent) +{ + CAmount nFee; + string strSentAccount; + list listSent; + list listReceived; + + wtx.GetAmounts(listReceived, listSent, nFee, strSentAccount, filter); + + bool fAllAccounts = (strAccount == string("*")); + + // Only care about sent + if ((!listSent.empty() || nFee != 0) && (fAllAccounts || strAccount == strSentAccount)) + { + BOOST_FOREACH(const COutputEntry& s, listSent) + { + if (list_spent || !pwalletMain->IsSpent(wtx.GetHash(), s.vout)) + { + Object entry; + const CScript& scriptPubKey = wtx.vout[s.vout].scriptPubKey; + int op; + vector > vvchParams; + if (!DecodeNCCScript(scriptPubKey, op, vvchParams)) + { + LogPrintf("ListNameClaims(): Txout classified as NCC could not be decoded. Txid: %s", wtx.GetHash().ToString()); + continue; + } + else if (vvchParams.size() != 2) + { + LogPrintf("ListNameClaims(): Wrong number of params to name claim script. Got %d, expected 2. Txid: %s", vvchParams.size(), wtx.GetHash().ToString()); + continue; + } + string sName (vvchParams[0].begin(), vvchParams[0].end()); + string sValue (vvchParams[1].begin(), vvchParams[1].end()); + entry.push_back(Pair("name", sName)); + entry.push_back(Pair("value", sValue)); + entry.push_back(Pair("txid", wtx.GetHash().ToString())); + entry.push_back(Pair("account", strSentAccount)); + MaybePushAddress(entry, s.destination); + entry.push_back(Pair("category", "ncc")); + entry.push_back(Pair("amount", ValueFromAmount(s.amount))); + entry.push_back(Pair("vout", s.vout)); + entry.push_back(Pair("fee", ValueFromAmount(nFee))); + ret.push_back(entry); + } + } + } +} + + + +Value listnameclaims(const Array& params, bool fHelp) +{ + if (fHelp || params.size() > 1) + throw runtime_error( + "listnameclaims activeonly\n" + "Return a list of all transactions claiming names.\n" + "\nArguments\n" + "1. activeonly (bool, optional, not implemented) Whether to only include transactions which are still active, i.e. have not been spent. Default is false.\n" + "\nResult:\n" + "[\n" + " {\n" + " \"name\":\"claimedname\", (string) The name that is claimed.\n" + " \"value\":\"value\" (string) The value assigned to the name.\n" + " \"account\":\"accountname\", (string) The account name associated with the transaction. \n" + " It will be \"\" for the default account.\n" + " \"address\":\"bitcoinaddress\", (string) The bitcoin address of the transaction.\n" + " \"category\":\"ncc\" (string) Always ncc\n" + " \"amount\": x.xxx, (numeric) The amount in btc.\n" + " \"vout\": n, (numeric) The vout value\n" + " \"fee\": x.xxx, (numeric) The amount of the fee in btc.\n" + " \"confirmations\": n, (numeric) The number of confirmations for the transaction\n" + " \"blockhash\": \"hashvalue\", (string) The block hash containing the transaction.\n" + " \"blockindex\": n, (numeric) The block index containing the transaction.\n" + " \"txid\": \"transactionid\", (string) The transaction id.\n" + " \"time\": xxx, (numeric) The transaction time in seconds since epoch (midnight Jan 1 1970 GMT).\n" + " \"timereceived\": xxx, (numeric) The time received in seconds since epoch (midnight Jan 1 1970 GMT).\n" + " \"comment\": \"...\", (string) If a comment is associated with the transaction.\n" + " }\n" + "]\n" + + ); + + string strAccount = "*"; + + bool fListSpent = true; + if (params.size() > 0) + fListSpent = !params[0].get_bool(); + isminefilter ncc_filter = ISMINE_NCC; + + Array ret; + + std::list acentries; + CWallet::TxItems txOrdered = pwalletMain->OrderedTxItems(acentries, strAccount); + + for (CWallet::TxItems::reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it) + { + CWalletTx *const pwtx = (*it).second.first; + if (pwtx != 0) + ListNameClaims(*pwtx, strAccount, 0, ret, ncc_filter, fListSpent); + } + + std::reverse(ret.begin(), ret.end()); + + return ret; + +} + + + void SendMoney(const CTxDestination &address, CAmount nValue, CWalletTx& wtxNew) { diff --git a/src/script/sign.cpp b/src/script/sign.cpp index adddd4ec7..8fb17e778 100644 --- a/src/script/sign.cpp +++ b/src/script/sign.cpp @@ -10,7 +10,7 @@ #include "keystore.h" #include "script/standard.h" #include "uint256.h" - +#include "ncc.h" #include using namespace std; @@ -57,8 +57,10 @@ bool Solver(const CKeyStore& keystore, const CScript& scriptPubKey, uint256 hash { scriptSigRet.clear(); + const CScript& strippedScriptPubKey = StripNCCScriptPrefix(scriptPubKey); + vector vSolutions; - if (!Solver(scriptPubKey, whichTypeRet, vSolutions)) + if (!Solver(strippedScriptPubKey, whichTypeRet, vSolutions)) return false; CKeyID keyID; diff --git a/src/wallet.cpp b/src/wallet.cpp index 6c5af3bdc..9ea45e3ec 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -14,6 +14,7 @@ #include "timedata.h" #include "util.h" #include "utilmoneystr.h" +#include "ncc.h" #include @@ -770,11 +771,14 @@ bool CWallet::IsChange(const CTxOut& txout) const if (::IsMine(*this, txout.scriptPubKey)) { CTxDestination address; - if (!ExtractDestination(txout.scriptPubKey, address)) + + const CScript& scriptPubKey = StripNCCScriptPrefix(txout.scriptPubKey); + + if (!ExtractDestination(scriptPubKey, address)) return true; LOCK(cs_wallet); - if (!mapAddressBook.count(address)) + if (!mapAddressBook.count(address) && (scriptPubKey == txout.scriptPubKey)) return true; } return false; @@ -860,7 +864,8 @@ void CWalletTx::GetAmounts(list& listReceived, // In either case, we need to get the destination address CTxDestination address; - if (!ExtractDestination(txout.scriptPubKey, address)) + const CScript& scriptPubKey = StripNCCScriptPrefix(txout.scriptPubKey); + if (!ExtractDestination(scriptPubKey, address)) { LogPrintf("CWalletTx::GetAmounts: Unknown transaction type found, txid %s\n", this->GetHash().ToString()); @@ -870,7 +875,7 @@ void CWalletTx::GetAmounts(list& listReceived, COutputEntry output = {address, txout.nValue, (int)i}; // If we are debited by the transaction, add the output as a "sent" entry - if (nDebit > 0) + if (nDebit > 0 || filter == ISMINE_NCC) listSent.push_back(output); // If we are receiving the output, add it as a "received" entry @@ -1360,7 +1365,9 @@ bool CWallet::SelectCoins(const CAmount& nTargetValue, set >& vecSend, - CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, std::string& strFailReason, const CCoinControl* coinControl) + CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, + std::string& strFailReason, const CCoinControl* coinControl, + const CWalletTx* pwtxIn, unsigned int nTxOut) { CAmount nValue = 0; BOOST_FOREACH (const PAIRTYPE(CScript, CAmount)& s, vecSend) @@ -1382,6 +1389,8 @@ bool CWallet::CreateTransaction(const vector >& vecSend, wtxNew.BindWallet(this); CMutableTransaction txNew; + bool fUsingWtxIn = (pwtxIn != NULL); + // Discourage fee sniping. // // However because of a off-by-one-error in previous versions we need to @@ -1428,13 +1437,22 @@ bool CWallet::CreateTransaction(const vector >& vecSend, txNew.vout.push_back(txout); } + CAmount nValueNeeded = nTotalValue; + if (fUsingWtxIn) + { + nValueNeeded = nValueNeeded - pwtxIn->vout[nTxOut].nValue; + } + // Choose coins to use set > setCoins; CAmount nValueIn = 0; - if (!SelectCoins(nTotalValue, setCoins, nValueIn, coinControl)) + if (!fUsingWtxIn || nValueNeeded > 0) { - strFailReason = _("Insufficient funds"); - return false; + if (!SelectCoins(nValueNeeded, setCoins, nValueIn, coinControl)) + { + strFailReason = _("Insufficient funds"); + return false; + } } BOOST_FOREACH(PAIRTYPE(const CWalletTx*, unsigned int) pcoin, setCoins) { @@ -1445,6 +1463,12 @@ bool CWallet::CreateTransaction(const vector >& vecSend, dPriority += (double)nCredit * (pcoin.first->GetDepthInMainChain()+1); } + if (fUsingWtxIn) + { + nValueIn += pwtxIn->vout[nTxOut].nValue; + setCoins.insert(make_pair(pwtxIn, nTxOut)); + } + CAmount nChange = nValueIn - nValue - nFeeRet; if (nChange > 0) @@ -1562,11 +1586,11 @@ bool CWallet::CreateTransaction(const vector >& vecSend, } bool CWallet::CreateTransaction(CScript scriptPubKey, const CAmount& nValue, - CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, std::string& strFailReason, const CCoinControl* coinControl) + CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, std::string& strFailReason, const CCoinControl* coinControl, const CWalletTx* pwtxIn, unsigned int nTxOut) { vector< pair > vecSend; vecSend.push_back(make_pair(scriptPubKey, nValue)); - return CreateTransaction(vecSend, wtxNew, reservekey, nFeeRet, strFailReason, coinControl); + return CreateTransaction(vecSend, wtxNew, reservekey, nFeeRet, strFailReason, coinControl, pwtxIn, nTxOut); } /** diff --git a/src/wallet.h b/src/wallet.h index 1d0dc97c6..03f2cb9af 100644 --- a/src/wallet.h +++ b/src/wallet.h @@ -289,9 +289,9 @@ public: CAmount GetUnconfirmedWatchOnlyBalance() const; CAmount GetImmatureWatchOnlyBalance() const; bool CreateTransaction(const std::vector >& vecSend, - CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, std::string& strFailReason, const CCoinControl *coinControl = NULL); + CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, std::string& strFailReason, const CCoinControl *coinControl = NULL, const CWalletTx* pwtxIn = NULL, unsigned int nTxOut = -1); bool CreateTransaction(CScript scriptPubKey, const CAmount& nValue, - CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, std::string& strFailReason, const CCoinControl *coinControl = NULL); + CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, std::string& strFailReason, const CCoinControl *coinControl = NULL, const CWalletTx* pwtxIn = NULL, unsigned int nTxOut = -1); bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey); static CFeeRate minTxFee; diff --git a/src/wallet_ismine.cpp b/src/wallet_ismine.cpp index 5482348e3..084400fc0 100644 --- a/src/wallet_ismine.cpp +++ b/src/wallet_ismine.cpp @@ -9,6 +9,7 @@ #include "keystore.h" #include "script/script.h" #include "script/standard.h" +#include "ncc.h" #include @@ -38,8 +39,17 @@ isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey) { vector vSolutions; txnouttype whichType; - if (!Solver(scriptPubKey, whichType, vSolutions)) { - if (keystore.HaveWatchOnly(scriptPubKey)) + isminetype spendable_type = ISMINE_SPENDABLE; + + CScript strippedScriptPubKey = StripNCCScriptPrefix(scriptPubKey); + if (strippedScriptPubKey != scriptPubKey) + { + spendable_type = ISMINE_NCC; + } + + + if (!Solver(strippedScriptPubKey, whichType, vSolutions)) { + if (keystore.HaveWatchOnly(strippedScriptPubKey)) return ISMINE_WATCH_ONLY; return ISMINE_NO; } @@ -53,12 +63,12 @@ isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey) case TX_PUBKEY: keyID = CPubKey(vSolutions[0]).GetID(); if (keystore.HaveKey(keyID)) - return ISMINE_SPENDABLE; + return spendable_type; break; case TX_PUBKEYHASH: keyID = CKeyID(uint160(vSolutions[0])); if (keystore.HaveKey(keyID)) - return ISMINE_SPENDABLE; + return spendable_type; break; case TX_SCRIPTHASH: { @@ -67,7 +77,7 @@ isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey) if (keystore.GetCScript(scriptID, subscript)) { isminetype ret = IsMine(keystore, subscript); if (ret == ISMINE_SPENDABLE) - return ret; + return spendable_type; } break; } @@ -80,7 +90,7 @@ isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey) // in shared-wallet situations. vector keys(vSolutions.begin()+1, vSolutions.begin()+vSolutions.size()-1); if (HaveKeys(keys, keystore) == keys.size()) - return ISMINE_SPENDABLE; + return spendable_type; break; } } diff --git a/src/wallet_ismine.h b/src/wallet_ismine.h index 6293df8b1..401707070 100644 --- a/src/wallet_ismine.h +++ b/src/wallet_ismine.h @@ -18,7 +18,8 @@ enum isminetype ISMINE_NO = 0, ISMINE_WATCH_ONLY = 1, ISMINE_SPENDABLE = 2, - ISMINE_ALL = ISMINE_WATCH_ONLY | ISMINE_SPENDABLE + ISMINE_NCC = 4, + ISMINE_ALL = ISMINE_WATCH_ONLY | ISMINE_SPENDABLE | ISMINE_NCC }; /** used for bitflags of isminetype */ typedef uint8_t isminefilter;