Implement a hard fork for extended/infinite claim expiration times #112

Closed
lbrynaut wants to merge 247 commits from claim-expiration into master
15 changed files with 446 additions and 98 deletions
Showing only changes of commit 1431b9e03c - Show all commits

View file

@ -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 \

View file

@ -6,8 +6,6 @@
#include "random.h"
#include "util.h"
#include <assert.h>
/**

View file

@ -21,6 +21,7 @@
#include "undo.h"
#include "util.h"
#include "utilmoneystr.h"
#include "ncc.h"
#include <sstream>
@ -619,75 +620,6 @@ unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans)
}
bool DecodeNCCScript(const CScript& scriptIn, int& op, std::vector<std::vector<unsigned char> >& vvchParams)
{
CScript::const_iterator pc = scriptIn.begin();
return DecodeNCCScript(scriptIn, op, vvchParams, pc);
}
bool DecodeNCCScript(const CScript& scriptIn, int& op, std::vector<std::vector<unsigned char> >& 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<unsigned char> vchName;
std::vector<unsigned char> 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<std::vector<unsigned char> > 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);

View file

@ -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<std::vector<unsigned char> >& vvchParams);
bool DecodeNCCScript(const CScript& scriptIn, int& op, std::vector<std::vector<unsigned char> >& vvchParams, CScript::const_iterator& pc);
CScript StripNCCScriptPrefix(const CScript& scriptIn);
/**
* Closure representing one script verification

66
src/ncc.cpp Normal file
View file

@ -0,0 +1,66 @@
#include "ncc.h"
bool DecodeNCCScript(const CScript& scriptIn, int& op, std::vector<std::vector<unsigned char> >& vvchParams)
{
CScript::const_iterator pc = scriptIn.begin();
return DecodeNCCScript(scriptIn, op, vvchParams, pc);
}
bool DecodeNCCScript(const CScript& scriptIn, int& op, std::vector<std::vector<unsigned char> >& 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<unsigned char> vchName;
std::vector<unsigned char> 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<std::vector<unsigned char> > vvchParams;
CScript::const_iterator pc = scriptIn.begin();
if (!DecodeNCCScript(scriptIn, op, vvchParams, pc))
{
return scriptIn;
}
return CScript(pc, scriptIn.end());
}

11
src/ncc.h Normal file
View file

@ -0,0 +1,11 @@
#ifndef BITCOIN_NCC_H
#define BITCOIN_NCC_H
#include "script/script.h"
#include <vector>
bool DecodeNCCScript(const CScript& scriptIn, int& op, std::vector<std::vector<unsigned char> >& vvchParams);
bool DecodeNCCScript(const CScript& scriptIn, int& op, std::vector<std::vector<unsigned char> >& vvchParams, CScript::const_iterator& pc);
CScript StripNCCScriptPrefix(const CScript& scriptIn);
#endif // BITCOIN_NCC_H

View file

@ -88,6 +88,10 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "estimatepriority", 0 },
{ "prioritisetransaction", 1 },
{ "prioritisetransaction", 2 },
{ "claimname", 2},
{ "updatename", 2},
{ "abandonname", 2},
{ "listnameclaims", 0},
};
class CRPCConvertTable

View file

@ -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 },

View file

@ -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);

View file

@ -15,6 +15,7 @@
#include "utilmoneystr.h"
#include "wallet.h"
#include "walletdb.h"
#include "ncc.h"
#include <stdint.h>
@ -333,7 +334,8 @@ void ClaimName(const std::vector<unsigned char> vchName, const std::vector<unsig
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 << scriptPubKey;
CScript claimScript = CScript() << OP_CLAIM_NAME << vchName << vchValue << OP_2DROP << OP_DROP;
claimScript = claimScript + scriptPubKey;
CReserveKey reservekey(pwalletMain);
CAmount nFeeRequired;
@ -353,7 +355,7 @@ Value claimname(const Array& params, bool fHelp)
if (fHelp || params.size() != 3)
throw runtime_error(
"claimname \"name\" \"value\" amount\n"
"\nCreate a transaction which issues a claim assigning a value to a name. 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. The amount is a real and is rounded to the nearest 0.00000001\n"
"\nCreate a transaction which issues a claim assigning a value to a name. 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 other greater unspent transactions issuing a claim over the same name. The amount is a real and is rounded to the nearest 0.00000001\n"
+ HelpRequiringPassphrase() +
"\nArguments:\n"
"1. \"name\" (string, required) The name to be assigned the value.\n"
@ -378,6 +380,298 @@ Value claimname(const Array& params, bool fHelp)
}
void UpdateName(const std::vector<unsigned char> vchName, const std::vector<unsigned char> 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<unsigned char> vchName;
string sValue = params[1].get_str();
std::vector<unsigned char> 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<std::vector<unsigned char> > 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<std::vector<unsigned char> > 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<COutputEntry> listSent;
list<COutputEntry> 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<vector<unsigned char> > 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<CAccountingEntry> 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)
{
// Check amount

View file

@ -10,7 +10,7 @@
#include "keystore.h"
#include "script/standard.h"
#include "uint256.h"
#include "ncc.h"
#include <boost/foreach.hpp>
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<valtype> vSolutions;
if (!Solver(scriptPubKey, whichTypeRet, vSolutions))
if (!Solver(strippedScriptPubKey, whichTypeRet, vSolutions))
return false;
CKeyID keyID;

View file

@ -14,6 +14,7 @@
#include "timedata.h"
#include "util.h"
#include "utilmoneystr.h"
#include "ncc.h"
#include <assert.h>
@ -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<COutputEntry>& 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<COutputEntry>& 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<pair<const CWalletTx*
bool CWallet::CreateTransaction(const vector<pair<CScript, CAmount> >& 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<pair<CScript, CAmount> >& 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,14 +1437,23 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, CAmount> >& vecSend,
txNew.vout.push_back(txout);
}
CAmount nValueNeeded = nTotalValue;
if (fUsingWtxIn)
{
nValueNeeded = nValueNeeded - pwtxIn->vout[nTxOut].nValue;
}
// Choose coins to use
set<pair<const CWalletTx*,unsigned int> > setCoins;
CAmount nValueIn = 0;
if (!SelectCoins(nTotalValue, setCoins, nValueIn, coinControl))
if (!fUsingWtxIn || nValueNeeded > 0)
{
if (!SelectCoins(nValueNeeded, setCoins, nValueIn, coinControl))
{
strFailReason = _("Insufficient funds");
return false;
}
}
BOOST_FOREACH(PAIRTYPE(const CWalletTx*, unsigned int) pcoin, setCoins)
{
CAmount nCredit = pcoin.first->vout[pcoin.second].nValue;
@ -1445,6 +1463,12 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, CAmount> >& 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<pair<CScript, CAmount> >& 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<CScript, CAmount> > 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);
}
/**

View file

@ -289,9 +289,9 @@ public:
CAmount GetUnconfirmedWatchOnlyBalance() const;
CAmount GetImmatureWatchOnlyBalance() const;
bool CreateTransaction(const std::vector<std::pair<CScript, CAmount> >& 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;

View file

@ -9,6 +9,7 @@
#include "keystore.h"
#include "script/script.h"
#include "script/standard.h"
#include "ncc.h"
#include <boost/foreach.hpp>
@ -38,8 +39,17 @@ isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey)
{
vector<valtype> 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<valtype> keys(vSolutions.begin()+1, vSolutions.begin()+vSolutions.size()-1);
if (HaveKeys(keys, keystore) == keys.size())
return ISMINE_SPENDABLE;
return spendable_type;
break;
}
}

View file

@ -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;