Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

JSON-RPC method: prioritisetransaction <txid> <priority delta> #1583

Merged
merged 1 commit into from

9 participants

@luke-jr

Accepts the transaction into mined blocks at a higher (or lower) priority

@jgarzik
Owner

Code seems ACK-worthy.

Use cases?

@luke-jr

Various people (@gmaxwell and @gavinandresen included) expressed interest in this - one example was to allow captcha-solving as an alternative to fees.

src/main.h
@@ -396,6 +399,9 @@ class CTransaction
mutable int nDoS;
bool DoS(int nDoSIn, bool fIn) const { nDoS += nDoSIn; return fIn; }
+ double dPriorityDelta;
+
@Diapolo
Diapolo added a note

Have 2 empty lines a special meaning in the code?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
src/main.h
@@ -540,6 +547,9 @@ class CTransaction
int64 GetMinFee(unsigned int nBlockSize=1, bool fAllowFree=true, enum GetMinFee_mode mode=GMF_BLOCK) const
{
+ if (dPriorityDelta > 0)
@gavinandresen Owner

What's the logic here? Why would setting a >0 delta automatically imply a minimum fee of zero?

@luke-jr
luke-jr added a note

It seems to me, that if someone wants to improve the priority of a transaction, they do actually want to mine that transaction. What's the point of setting a priority of a transaction that you're not going to mine regardless?

@gavinandresen Owner

The point is "increase priority" and "requires no fee" should, in my opinion, be independent notions. Somebody might want to bump up the priority of transactions from the Bitcoin Faucet a little because they like the idea of giving newbies a good bitcoin experience, but don't want to crowd out other high-priority/low-fee transactions.

@luke-jr
luke-jr added a note

I can take this out if you want me to, but I don't see the point. A priority bump of just 1 has no practical effect on the overall priority comparison, but could be used to whitelist against the fee requirements, and the priority is entirely ignored if a transaction doesn't meet the fee requirements, so it makes no sense to increase the priority while not overriding the fee requirements, IMO.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
src/main.h
@@ -92,6 +92,9 @@
bool SendMessages(CNode* pto, bool fSendTrickle);
bool LoadExternalBlockFile(FILE* fileIn);
void GenerateBitcoins(bool fGenerate, CWallet* pwallet);
+bool PrioritiseTransaction(const uint256 hash, const std::string strHash, double dPriorityDelta);
+bool PrioritiseTransaction(const uint256 hash, double dPriorityDelta);
+bool PrioritiseTransaction(const std::string strHash, double dPriorityDelta);
@gavinandresen Owner

Three versions of this is excessive. Less code, please.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@gmaxwell
Owner

Re: Ignoring minfee: This call is by txid. If you don't like the fee, don't call it on the transaction. Ignoring minfee is the right thing to do here.

Though this should also have a fee delta, because we now prioritize above minfee txn by fee per KB. If someone is paying you behind the scenes to mine a transaction you should be able to add the amount you're being paid (or whatever) to the fee used in that calculation. e.g. prioritizetransaction .

@luke-jr

Updated with suggestions. Also, is it intentional that GetMinFee with GMF_BLOCK is never used anymore?

@luke-jr luke-jr referenced this pull request
Closed

next 2012-08-13 #1671

@BitcoinPullTester

Automatic sanity-testing: PASSED, see http://jenkins.bluematt.me/pull-tester/2c63ef9156288b3cc72668fdb6ce44ec3a440076 for binaries and test log.

@sipa
Owner

@luke-jr Which commit removed the call to GetMinFee with GMF_BLOCK?

src/main.cpp
@@ -3349,6 +3349,23 @@ class COrphan
};
+bool PrioritiseTransaction(const uint256 hash, const string strHash, double dPriorityDelta, int64 nFeeDelta)
+{
+ if (!mempool.mapTx.count(hash))
+ {
+ printf("PrioritiseTransaction: cannot find %s\n", strHash.c_str());
+ return false;
+ }
+
+ CTransaction &txn = mempool.mapTx[hash];
@sipa Owner
sipa added a note

This needs a LOCK on mempool.cs, imho. Even if it's somehow already safe because of the lock on cs_main, I'd prefer having it there, for when RPC locks get pushed down.

@luke-jr
luke-jr added a note

Fixed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@gmaxwell
Owner

Needs a rebase

@luke-jr

rebased

src/main.h
@@ -477,6 +479,10 @@ class CTransaction
std::vector<CTxOut> vout;
unsigned int nLockTime;
+ double dPriorityDelta;
+ int64 nFeeDelta;
@gavinandresen Owner

Adding 16 bytes to every in-memory transaction to support a feature that will not be used by 99.9% of users seems like the wrong thing to do.

How about instead keeping a map<transaction_id, pair<priorityDelta,feeDelta> > in the memory pool class, and have prioritisetransaction add to that map?

Finally: need to check to see if the free transaction rate limiter code needs to take this into account (I assume you should be able to prioritisetransaction and then if you receive the transaction over the network it should sail through the limiter and make it into the memory pool -- or is it assumed that the transaction will get into the memory pool some other way, like a sendrawtransaction call?).

@luke-jr
luke-jr added a note

With the deltas stored on CTransaction, applying them to not-received ones was impractical. Obviously using a map as you suggest solves this problem too - will do that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@luke-jr

Ok, finally redid this using a map.

@gavinandresen , look good?

@jgarzik
Owner

No objection... though I still prioritize with the 'z' :) Who the heck uses an 's'? :)

src/main.cpp
@@ -574,6 +574,16 @@ bool CTransaction::CheckTransaction(CValidationState &state) const
int64 CTransaction::GetMinFee(unsigned int nBlockSize, bool fAllowFree,
enum GetMinFee_mode mode) const
{
+ {
+ LOCK(mempool.cs);
+ uint256 hash = GetHash();
+ double dPriorityDelta = 0;
+ int64 nFeeDelta = 0;
+ mempool.ApplyDeltas(hash, dPriorityDelta, nFeeDelta);
@laanwj Owner
laanwj added a note

I don't like all these side-effects in a const getter function

Edit: hmm, never mind, ApplyDeltas does not actually change anything in the mempool object.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@luke-jr

It occurs to me that the map should be cleaned at some point. Any opinions on when to remove a txid from it?

@gmaxwell
Owner

Check it when removing transactions from the mempool?

@luke-jr

Probably don't want to lose priority adjustments if your block gets knocked off the main chain...

@petertodd

@luke-jr Set a expiry height after they get knocked off the main chain and remove them from the map after n blocks? If n=100 is reached we have bigger problems...

@gavinandresen

Rebase needed.

@luke-jr

Rebased. For purging from the map.. how about when we see a block confirm a transaction using it as an input?

@luke-jr

Rebased and added mapDeltas pruning when transactions are removed from the memory pool (ie, included in a block).

@laanwj
Owner

This pull has been open for almost two years now.
What still has to be done for this to me merged? (apart from a rebase)

@jgarzik
Owner

ACK -- appears to be done, to me, modulo a rebase.

This is IMO important to merge. We want to give miners controls like this, so that they may innovate and compete.

Related: Miners also need an "accept this TX even if it's non-standard" RPC or RPC flag.

@BitcoinPullTester

Automatic sanity-testing: FAILED MERGE, see http://jenkins.bluematt.me/pull-tester/p1583_4427dc5e6a6fd0f7349e6ea3e0d6a084491cf265/ for test log.

This pull does not merge cleanly onto current master
This test script verifies pulls every time they are updated. It, however, dies sometimes and fails to test properly. If you are waiting on a test, please check timestamps to verify that the test.log is moving at http://jenkins.bluematt.me/pull-tester/current/
Contact BlueMatt on freenode if something looks broken.

src/miner.cpp
@@ -77,6 +79,35 @@ class COrphan
};
+void CTxMemPool::PrioritiseTransaction(const uint256 hash, const string strHash, double dPriorityDelta, int64_t nFeeDelta)
@laanwj Owner
laanwj added a note

These implementations should be in txmempool.cpp.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@luke-jr luke-jr JSON-RPC method: prioritisetransaction <txid> <priority delta> <prior…
…ity tx fee>

Accepts the transaction into mined blocks at a higher (or lower) priority
2a72d45
@luke-jr

Rebased with @laanwj 's change.

@laanwj laanwj merged commit 2a72d45 into from
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Jun 26, 2014
  1. @luke-jr

    JSON-RPC method: prioritisetransaction <txid> <priority delta> <prior…

    luke-jr authored
    …ity tx fee>
    
    Accepts the transaction into mined blocks at a higher (or lower) priority
This page is out of date. Refresh to see the latest.
View
10 src/main.cpp
@@ -789,6 +789,16 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state)
int64_t GetMinFee(const CTransaction& tx, unsigned int nBytes, bool fAllowFree, enum GetMinFee_mode mode)
{
+ {
+ LOCK(mempool.cs);
+ uint256 hash = tx.GetHash();
+ double dPriorityDelta = 0;
+ int64_t nFeeDelta = 0;
+ mempool.ApplyDeltas(hash, dPriorityDelta, nFeeDelta);
+ if (dPriorityDelta > 0 || nFeeDelta > 0)
+ return 0;
+ }
+
// Base fee is either minTxFee or minRelayTxFee
CFeeRate baseFeeRate = (mode == GMF_RELAY) ? tx.minRelayTxFee : tx.minTxFee;
View
14 src/miner.cpp
@@ -3,6 +3,8 @@
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <inttypes.h>
+
#include "miner.h"
#include "core.h"
@@ -186,6 +188,9 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn)
unsigned int nTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION);
dPriority = tx.ComputePriority(dPriority, nTxSize);
+ uint256 hash = tx.GetHash();
+ mempool.ApplyDeltas(hash, dPriority, nTotalIn);
+
CFeeRate feeRate(nTotalIn-tx.GetValueOut(), nTxSize);
if (porphan)
@@ -227,10 +232,14 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn)
continue;
// Skip free transactions if we're past the minimum block size:
- if (fSortedByFee && (feeRate < CTransaction::minRelayTxFee) && (nBlockSize + nTxSize >= nBlockMinSize))
+ const uint256& hash = tx.GetHash();
+ double dPriorityDelta = 0;
+ int64_t nFeeDelta = 0;
+ mempool.ApplyDeltas(hash, dPriorityDelta, nFeeDelta);
+ if (fSortedByFee && (dPriorityDelta <= 0) && (nFeeDelta <= 0) && (feeRate < CTransaction::minRelayTxFee) && (nBlockSize + nTxSize >= nBlockMinSize))
continue;
- // Prioritize by fee once past the priority size or we run out of high-priority
+ // Prioritise by fee once past the priority size or we run out of high-priority
// transactions:
if (!fSortedByFee &&
((nBlockSize + nTxSize >= nBlockPrioritySize) || !AllowFree(dPriority)))
@@ -257,7 +266,6 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn)
continue;
CTxUndo txundo;
- const uint256& hash = tx.GetHash();
UpdateCoins(tx, state, view, txundo, pindexPrev->nHeight+1);
// Added
View
2  src/rpcclient.cpp
@@ -72,6 +72,8 @@ Array RPCConvertValues(const std::string &strMethod, const std::vector<std::stri
if (strMethod == "listtransactions" && n > 2) ConvertTo<int64_t>(params[2]);
if (strMethod == "listaccounts" && n > 0) ConvertTo<int64_t>(params[0]);
if (strMethod == "walletpassphrase" && n > 1) ConvertTo<int64_t>(params[1]);
+ if (strMethod == "prioritisetransaction" && n > 1) ConvertTo<double>(params[1]);
+ if (strMethod == "prioritisetransaction" && n > 2) ConvertTo<int64_t>(params[2]);
if (strMethod == "getblocktemplate" && n > 0) ConvertTo<Object>(params[0]);
if (strMethod == "listsinceblock" && n > 1) ConvertTo<int64_t>(params[1]);
if (strMethod == "sendmany" && n > 1) ConvertTo<Object>(params[1]);
View
14 src/rpcmining.cpp
@@ -247,6 +247,20 @@ Value getmininginfo(const Array& params, bool fHelp)
}
+Value prioritisetransaction(const Array& params, bool fHelp)
+{
+ if (fHelp || params.size() != 3)
+ throw runtime_error(
+ "prioritisetransaction <txid> <priority delta> <fee delta>\n"
+ "Accepts the transaction into mined blocks at a higher (or lower) priority");
+
+ uint256 hash;
+ hash.SetHex(params[0].get_str());
+ mempool.PrioritiseTransaction(hash, params[0].get_str(), params[1].get_real(), params[2].get_int64());
+ return true;
+}
+
+
Value getblocktemplate(const Array& params, bool fHelp)
{
if (fHelp || params.size() > 1)
View
1  src/rpcserver.cpp
@@ -254,6 +254,7 @@ static const CRPCCommand vRPCCommands[] =
{ "getblocktemplate", &getblocktemplate, true, false, false },
{ "getmininginfo", &getmininginfo, true, false, false },
{ "getnetworkhashps", &getnetworkhashps, true, false, false },
+ { "prioritisetransaction", &prioritisetransaction, true, false, false },
{ "submitblock", &submitblock, false, true, false },
/* Raw transactions */
View
1  src/rpcserver.h
@@ -130,6 +130,7 @@ extern json_spirit::Value setgenerate(const json_spirit::Array& params, bool fHe
extern json_spirit::Value getnetworkhashps(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value gethashespersec(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value getmininginfo(const json_spirit::Array& params, bool fHelp);
+extern json_spirit::Value prioritisetransaction(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value getblocktemplate(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value submitblock(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value estimatefee(const json_spirit::Array& params, bool fHelp);
View
29 src/txmempool.cpp
@@ -447,6 +447,7 @@ void CTxMemPool::removeForBlock(const std::vector<CTransaction>& vtx, unsigned i
std::list<CTransaction> dummy;
remove(tx, dummy, false);
removeConflicts(tx, conflicts);
+ ClearPrioritisation(tx.GetHash());
}
}
@@ -564,6 +565,34 @@ CTxMemPool::ReadFeeEstimates(CAutoFile& filein)
return true;
}
+void CTxMemPool::PrioritiseTransaction(const uint256 hash, const string strHash, double dPriorityDelta, int64_t nFeeDelta)
+{
+ {
+ LOCK(cs);
+ std::pair<double, int64_t> &deltas = mapDeltas[hash];
+ deltas.first += dPriorityDelta;
+ deltas.second += nFeeDelta;
+ }
+ LogPrintf("PrioritiseTransaction: %s priority += %f, fee += %d\n", strHash.c_str(), dPriorityDelta, nFeeDelta);
+}
+
+void CTxMemPool::ApplyDeltas(const uint256 hash, double &dPriorityDelta, int64_t &nFeeDelta)
+{
+ LOCK(cs);
+ std::map<uint256, std::pair<double, int64_t> >::iterator pos = mapDeltas.find(hash);
+ if (pos == mapDeltas.end())
+ return;
+ const std::pair<double, int64_t> &deltas = pos->second;
+ dPriorityDelta += deltas.first;
+ nFeeDelta += deltas.second;
+}
+
+void CTxMemPool::ClearPrioritisation(const uint256 hash)
+{
+ LOCK(cs);
+ mapDeltas.erase(hash);
+}
+
CCoinsViewMemPool::CCoinsViewMemPool(CCoinsView &baseIn, CTxMemPool &mempoolIn) : CCoinsViewBacked(baseIn), mempool(mempoolIn) { }
View
6 src/txmempool.h
@@ -71,6 +71,7 @@ class CTxMemPool
mutable CCriticalSection cs;
std::map<uint256, CTxMemPoolEntry> mapTx;
std::map<COutPoint, CInPoint> mapNextTx;
+ std::map<uint256, std::pair<double, int64_t> > mapDeltas;
CTxMemPool();
~CTxMemPool();
@@ -95,6 +96,11 @@ class CTxMemPool
unsigned int GetTransactionsUpdated() const;
void AddTransactionsUpdated(unsigned int n);
+ /** Affect CreateNewBlock prioritisation of transactions */
+ void PrioritiseTransaction(const uint256 hash, const std::string strHash, double dPriorityDelta, int64_t nFeeDelta);
+ void ApplyDeltas(const uint256 hash, double &dPriorityDelta, int64_t &nFeeDelta);
+ void ClearPrioritisation(const uint256 hash);
+
unsigned long size()
{
LOCK(cs);
Something went wrong with that request. Please try again.