Auto merge of #1566 - daira:1557.make-v2-txns-standard, r=ageis
1557.make v2 txns standard Make v2 transactions standard. This also corrects a rule about admitting large orphan transactions into the mempool, to account for v2-specific fields. ref #1557
This commit is contained in:
@@ -157,7 +157,7 @@ static void RegisterLoad(const string& strInput)
|
|||||||
static void MutateTxVersion(CMutableTransaction& tx, const string& cmdVal)
|
static void MutateTxVersion(CMutableTransaction& tx, const string& cmdVal)
|
||||||
{
|
{
|
||||||
int64_t newVersion = atoi64(cmdVal);
|
int64_t newVersion = atoi64(cmdVal);
|
||||||
if (newVersion < 1 || newVersion > CTransaction::CURRENT_VERSION)
|
if (newVersion < CTransaction::MIN_CURRENT_VERSION || newVersion > CTransaction::MAX_CURRENT_VERSION)
|
||||||
throw runtime_error("Invalid TX version requested");
|
throw runtime_error("Invalid TX version requested");
|
||||||
|
|
||||||
tx.nVersion = (int) newVersion;
|
tx.nVersion = (int) newVersion;
|
||||||
|
|||||||
@@ -564,7 +564,7 @@ bool AddOrphanTx(const CTransaction& tx, NodeId peer)
|
|||||||
// have been mined or received.
|
// have been mined or received.
|
||||||
// 10,000 orphans, each of which is at most 5,000 bytes big is
|
// 10,000 orphans, each of which is at most 5,000 bytes big is
|
||||||
// at most 500 megabytes of orphans:
|
// at most 500 megabytes of orphans:
|
||||||
unsigned int sz = tx.GetSerializeSize(SER_NETWORK, CTransaction::CURRENT_VERSION);
|
unsigned int sz = tx.GetSerializeSize(SER_NETWORK, tx.nVersion);
|
||||||
if (sz > 5000)
|
if (sz > 5000)
|
||||||
{
|
{
|
||||||
LogPrint("mempool", "ignoring large orphan tx (size: %u, hash: %s)\n", sz, hash.ToString());
|
LogPrint("mempool", "ignoring large orphan tx (size: %u, hash: %s)\n", sz, hash.ToString());
|
||||||
@@ -639,7 +639,7 @@ unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans)
|
|||||||
|
|
||||||
bool IsStandardTx(const CTransaction& tx, string& reason)
|
bool IsStandardTx(const CTransaction& tx, string& reason)
|
||||||
{
|
{
|
||||||
if (tx.nVersion > CTransaction::CURRENT_VERSION || tx.nVersion < 1) {
|
if (tx.nVersion > CTransaction::MAX_CURRENT_VERSION || tx.nVersion < CTransaction::MIN_CURRENT_VERSION) {
|
||||||
reason = "version";
|
reason = "version";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -137,7 +137,7 @@ std::string CTxOut::ToString() const
|
|||||||
return strprintf("CTxOut(nValue=%d.%08d, scriptPubKey=%s)", nValue / COIN, nValue % COIN, scriptPubKey.ToString().substr(0,30));
|
return strprintf("CTxOut(nValue=%d.%08d, scriptPubKey=%s)", nValue / COIN, nValue % COIN, scriptPubKey.ToString().substr(0,30));
|
||||||
}
|
}
|
||||||
|
|
||||||
CMutableTransaction::CMutableTransaction() : nVersion(CTransaction::CURRENT_VERSION), nLockTime(0) {}
|
CMutableTransaction::CMutableTransaction() : nVersion(CTransaction::MIN_CURRENT_VERSION), nLockTime(0) {}
|
||||||
CMutableTransaction::CMutableTransaction(const CTransaction& tx) : nVersion(tx.nVersion), vin(tx.vin), vout(tx.vout), nLockTime(tx.nLockTime),
|
CMutableTransaction::CMutableTransaction(const CTransaction& tx) : nVersion(tx.nVersion), vin(tx.vin), vout(tx.vout), nLockTime(tx.nLockTime),
|
||||||
vjoinsplit(tx.vjoinsplit), joinSplitPubKey(tx.joinSplitPubKey), joinSplitSig(tx.joinSplitSig)
|
vjoinsplit(tx.vjoinsplit), joinSplitPubKey(tx.joinSplitPubKey), joinSplitSig(tx.joinSplitSig)
|
||||||
{
|
{
|
||||||
@@ -154,7 +154,7 @@ void CTransaction::UpdateHash() const
|
|||||||
*const_cast<uint256*>(&hash) = SerializeHash(*this);
|
*const_cast<uint256*>(&hash) = SerializeHash(*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
CTransaction::CTransaction() : nVersion(CTransaction::CURRENT_VERSION), vin(), vout(), nLockTime(0), vjoinsplit(), joinSplitPubKey(), joinSplitSig() { }
|
CTransaction::CTransaction() : nVersion(CTransaction::MIN_CURRENT_VERSION), vin(), vout(), nLockTime(0), vjoinsplit(), joinSplitPubKey(), joinSplitSig() { }
|
||||||
|
|
||||||
CTransaction::CTransaction(const CMutableTransaction &tx) : nVersion(tx.nVersion), vin(tx.vin), vout(tx.vout), nLockTime(tx.nLockTime), vjoinsplit(tx.vjoinsplit),
|
CTransaction::CTransaction(const CMutableTransaction &tx) : nVersion(tx.nVersion), vin(tx.vin), vout(tx.vout), nLockTime(tx.nLockTime), vjoinsplit(tx.vjoinsplit),
|
||||||
joinSplitPubKey(tx.joinSplitPubKey), joinSplitSig(tx.joinSplitSig)
|
joinSplitPubKey(tx.joinSplitPubKey), joinSplitSig(tx.joinSplitSig)
|
||||||
|
|||||||
@@ -309,7 +309,9 @@ private:
|
|||||||
public:
|
public:
|
||||||
typedef boost::array<unsigned char, 64> joinsplit_sig_t;
|
typedef boost::array<unsigned char, 64> joinsplit_sig_t;
|
||||||
|
|
||||||
static const int32_t CURRENT_VERSION=1;
|
// Transactions that include a list of JoinSplits are version 2.
|
||||||
|
static const int32_t MIN_CURRENT_VERSION = 1;
|
||||||
|
static const int32_t MAX_CURRENT_VERSION = 2;
|
||||||
|
|
||||||
// The local variables are made const to prevent unintended modification
|
// The local variables are made const to prevent unintended modification
|
||||||
// without updating the cached hash value. However, CTransaction is not
|
// without updating the cached hash value. However, CTransaction is not
|
||||||
|
|||||||
@@ -590,4 +590,55 @@ BOOST_AUTO_TEST_CASE(test_IsStandard)
|
|||||||
BOOST_CHECK(!IsStandardTx(t, reason));
|
BOOST_CHECK(!IsStandardTx(t, reason));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(test_IsStandardV2)
|
||||||
|
{
|
||||||
|
LOCK(cs_main);
|
||||||
|
CBasicKeyStore keystore;
|
||||||
|
CCoinsView coinsDummy;
|
||||||
|
CCoinsViewCache coins(&coinsDummy);
|
||||||
|
std::vector<CMutableTransaction> dummyTransactions = SetupDummyInputs(keystore, coins);
|
||||||
|
|
||||||
|
CMutableTransaction t;
|
||||||
|
t.vin.resize(1);
|
||||||
|
t.vin[0].prevout.hash = dummyTransactions[0].GetHash();
|
||||||
|
t.vin[0].prevout.n = 1;
|
||||||
|
t.vin[0].scriptSig << std::vector<unsigned char>(65, 0);
|
||||||
|
t.vout.resize(1);
|
||||||
|
t.vout[0].nValue = 90*CENT;
|
||||||
|
CKey key;
|
||||||
|
key.MakeNewKey(true);
|
||||||
|
t.vout[0].scriptPubKey = GetScriptForDestination(key.GetPubKey().GetID());
|
||||||
|
|
||||||
|
string reason;
|
||||||
|
// A v2 transaction with no JoinSplits is still standard.
|
||||||
|
t.nVersion = 2;
|
||||||
|
BOOST_CHECK(IsStandardTx(t, reason));
|
||||||
|
|
||||||
|
// ... and with one JoinSplit.
|
||||||
|
t.vjoinsplit.push_back(JSDescription());
|
||||||
|
BOOST_CHECK(IsStandardTx(t, reason));
|
||||||
|
|
||||||
|
// ... and when that JoinSplit takes from a transparent input.
|
||||||
|
JSDescription *jsdesc = &t.vjoinsplit[0];
|
||||||
|
jsdesc->vpub_old = 10*CENT;
|
||||||
|
t.vout[0].nValue -= 10*CENT;
|
||||||
|
BOOST_CHECK(IsStandardTx(t, reason));
|
||||||
|
|
||||||
|
// A v2 transaction with JoinSplits but no transparent inputs is standard.
|
||||||
|
jsdesc->vpub_old = 0;
|
||||||
|
jsdesc->vpub_new = 100*CENT;
|
||||||
|
t.vout[0].nValue = 90*CENT;
|
||||||
|
t.vin.resize(0);
|
||||||
|
BOOST_CHECK(IsStandardTx(t, reason));
|
||||||
|
|
||||||
|
// v2 transactions can still be non-standard for the same reasons as v1.
|
||||||
|
t.vout[0].nValue = 501; // dust
|
||||||
|
BOOST_CHECK(!IsStandardTx(t, reason));
|
||||||
|
|
||||||
|
// v3 is not standard.
|
||||||
|
t.nVersion = 3;
|
||||||
|
t.vout[0].nValue = 90*CENT;
|
||||||
|
BOOST_CHECK(!IsStandardTx(t, reason));
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_SUITE_END()
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
|
|||||||
Reference in New Issue
Block a user