Skip to content

Commit 10ea130

Browse files
authored
Merge pull request #226 from instagibbs/mempoollock
Fix claimpegins from mempool locks
2 parents 5dcf8a3 + 793e92b commit 10ea130

File tree

2 files changed

+82
-45
lines changed

2 files changed

+82
-45
lines changed

qa/rpc-tests/pegging.py

Lines changed: 47 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@
100100
subprocess.Popen(sidechain2start.split(), stdout=subprocess.PIPE)
101101

102102
print("Daemons started")
103-
time.sleep(2)
103+
time.sleep(3)
104104

105105
bitcoin = AuthServiceProxy("http://bitcoinrpc:"+bitcoin_pass+"@127.0.0.1:"+str(bitcoin_port))
106106
sidechain = AuthServiceProxy("http://sidechainrpc:"+sidechain_pass+"@127.0.0.1:"+str(sidechain_port))
@@ -114,13 +114,17 @@
114114

115115
# Lockup some funds to unlock later
116116
sidechain.sendtomainchain(addr, 50)
117-
sidechain.generate(101)
117+
# Tests withdrawlock tracking in database
118+
sidechain.generate(1)
119+
# Tests withdrawlock in mempool
120+
sidechain.sendtomainchain(addr, 50)
118121

119122
addrs = sidechain.getpeginaddress()
120-
txid = bitcoin.sendtoaddress(addrs["mainchain_address"], 49)
123+
txid1 = bitcoin.sendtoaddress(addrs["mainchain_address"], 24)
124+
txid2 = bitcoin.sendtoaddress(addrs["mainchain_address"], 24)
121125
bitcoin.generate(10)
122-
proof = bitcoin.gettxoutproof([txid])
123-
raw = bitcoin.getrawtransaction(txid)
126+
proof = bitcoin.gettxoutproof([txid1])
127+
raw = bitcoin.getrawtransaction(txid1)
124128

125129
print("Attempting peg-in")
126130

@@ -132,8 +136,13 @@
132136
pass
133137

134138
timeout = 20
135-
# Should succeed via wallet lookup for address match
136-
pegtxid = sidechain.claimpegin(raw, proof)
139+
# Both should succeed via wallet lookup for address match, and when given
140+
pegtxid1 = sidechain.claimpegin(raw, proof)
141+
142+
proof = bitcoin.gettxoutproof([txid2])
143+
raw = bitcoin.getrawtransaction(txid2)
144+
pegtxid2 = sidechain.claimpegin(raw, proof, addrs["sidechain_address"])
145+
137146
while len(sidechain.getrawmempool()) != len(sidechain2.getrawmempool()):
138147
time.sleep(1)
139148
timeout -= 1
@@ -147,13 +156,42 @@
147156
raise Exception("Blocks are not propagating.")
148157

149158

150-
tx = sidechain.gettransaction(pegtxid)
159+
tx1 = sidechain.gettransaction(pegtxid1)
160+
tx2 = sidechain.gettransaction(pegtxid2)
151161

152-
if "confirmations" in tx and tx["confirmations"] > 0:
162+
if "confirmations" in tx1 and tx1["confirmations"] > 0 and "confirmations" in tx2 and tx2["confirmations"] > 0:
153163
print("Peg-in is confirmed: Success!")
154164
else:
155165
raise Exception("Peg-in confirmation has failed.")
156166

167+
# Make a few large locks, then do many claims in mempool
168+
n_locks = 10
169+
n_claims = 30
170+
171+
print("Flooding mempool with many small claims")
172+
pegtxs = []
173+
for i in range(n_locks):
174+
# Lockup some funds to unlock later
175+
sidechain.sendtomainchain(addr, 50)
176+
sidechain.generate(1)
177+
sidechain.generate(101)
178+
179+
for i in range(n_claims):
180+
addrs = sidechain.getpeginaddress()
181+
txid = bitcoin.sendtoaddress(addrs["mainchain_address"], 1)
182+
bitcoin.generate(10)
183+
proof = bitcoin.gettxoutproof([txid])
184+
raw = bitcoin.getrawtransaction(txid)
185+
pegtxs += [sidechain.claimpegin(raw, proof)]
186+
187+
sidechain.generate(1)
188+
for pegtxid in pegtxs:
189+
tx = sidechain.gettransaction(pegtxid)
190+
if "confirmations" not in tx or tx["confirmations"] == 0:
191+
raise Exception("Peg-in confirmation has failed.")
192+
193+
print("Success!")
194+
157195
except JSONRPCException as e:
158196
print("Pegging testing failed, aborting:")
159197
print(e.error)

src/validation.cpp

Lines changed: 35 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1210,15 +1210,21 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C
12101210
CAmount inChainInputValue;
12111211
double dPriority = view.GetPriority(tx, chainActive.Height(), inChainInputValue);
12121212

1213+
bool fIsWithdrawLockSpender = false;
1214+
12131215
// Keep track of transactions that spend a coinbase, which we re-scan
12141216
// during reorgs to ensure COINBASE_MATURITY is still met.
1217+
// Also track withdraw lock spends to allow them through free relay
12151218
bool fSpendsCoinbase = false;
12161219
BOOST_FOREACH(const CTxIn &txin, tx.vin) {
12171220
const CCoins *coins = view.AccessCoins(txin.prevout.hash);
12181221
if (coins->IsCoinBase()) {
12191222
fSpendsCoinbase = true;
12201223
break;
12211224
}
1225+
if (coins->vout[txin.prevout.n].scriptPubKey.IsWithdrawLock()) {
1226+
fIsWithdrawLockSpender = true;
1227+
}
12221228
}
12231229

12241230
CTxMemPoolEntry entry(ptx, nFees, nAcceptTime, dPriority, chainActive.Height(),
@@ -1237,32 +1243,12 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C
12371243
CAmount mempoolRejectFee = pool.GetMinFee(GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFee(nSize);
12381244
if (mempoolRejectFee > 0 && nModifiedFees < mempoolRejectFee) {
12391245
return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "mempool min fee not met", false, strprintf("%d < %d", nFees, mempoolRejectFee));
1240-
} else if (GetBoolArg("-relaypriority", DEFAULT_RELAYPRIORITY) && nModifiedFees < ::minRelayTxFee.GetFee(nSize) && !AllowFree(entry.GetPriority(chainActive.Height() + 1))) {
1241-
// Require that free transactions have sufficient priority to be mined in the next block.
1242-
return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "insufficient priority");
12431246
}
12441247

1245-
// Continuously rate-limit free (really, very-low-fee) transactions
1246-
// This mitigates 'penny-flooding' -- sending thousands of free transactions just to
1247-
// be annoying or make others' transactions take longer to confirm.
1248-
if (fLimitFree && nModifiedFees < ::minRelayTxFee.GetFee(nSize))
1248+
// No transactions are allowed below minRelayTxFee except from disconnected blocks and withdraw lock spends
1249+
if (fLimitFree && nModifiedFees < ::minRelayTxFee.GetFee(nSize) && !fIsWithdrawLockSpender)
12491250
{
1250-
static CCriticalSection csFreeLimiter;
1251-
static double dFreeCount;
1252-
static int64_t nLastTime;
1253-
int64_t nNow = GetTime();
1254-
1255-
LOCK(csFreeLimiter);
1256-
1257-
// Use an exponentially decaying ~10-minute window:
1258-
dFreeCount *= pow(1.0 - 1.0/600.0, (double)(nNow - nLastTime));
1259-
nLastTime = nNow;
1260-
// -limitfreerelay unit is thousand-bytes-per-minute
1261-
// At default rate it would take over a month to fill 1GB
1262-
if (dFreeCount + nSize >= GetArg("-limitfreerelay", DEFAULT_LIMITFREERELAY) * 10 * 1000)
1263-
return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "rate limited free transaction");
1264-
LogPrint("mempool", "Rate limit dFreeCount: %g => %g\n", dFreeCount, dFreeCount+nSize);
1265-
dFreeCount += nSize;
1251+
return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "min relay fee not met");
12661252
}
12671253

12681254
if (nAbsurdFee && nFees > nAbsurdFee)
@@ -1605,8 +1591,10 @@ bool GetLockedOutputs(const uint256 &genesisHash, const CAmount &nAmount, std::v
16051591

16061592
std::vector<std::pair<COutPoint, CAmount> > locksCreated;
16071593
bool locksChanged = false;
1608-
if (!pblocktree->ReadLocksCreated(genesisHash, locksCreated))
1609-
return false;
1594+
// If this fails, we should still try to grab from mempool.
1595+
if (!pblocktree->ReadLocksCreated(genesisHash, locksCreated)) {
1596+
locksCreated.clear();
1597+
}
16101598

16111599
//For faster random esasure
16121600
std::list<std::pair<COutPoint, CAmount> > locksList;
@@ -1646,8 +1634,9 @@ bool GetLockedOutputs(const uint256 &genesisHash, const CAmount &nAmount, std::v
16461634
pblocktree->ReWriteLocksCreated(genesisHash, vChangedLocks);
16471635
}
16481636
//Found single lock large enough
1649-
if (res.size())
1637+
if (res.size()) {
16501638
return true;
1639+
}
16511640

16521641
CAmount nTotal = 0;
16531642
//Gather up smaller locked outputs for aggregation.
@@ -1660,8 +1649,9 @@ bool GetLockedOutputs(const uint256 &genesisHash, const CAmount &nAmount, std::v
16601649
continue;
16611650
}
16621651

1663-
if (mempool.mapNextTx.count(COutPoint(it->first.hash, it->first.n)))
1652+
if (mempool.mapNextTx.count(COutPoint(it->first.hash, it->first.n))) {
16641653
continue;
1654+
}
16651655

16661656
assert(coins.vout[it->first.n].nValue.IsExplicit() && coins.vout[it->first.n].nValue.GetAmount() == it->second);
16671657
res.push_back(*it);
@@ -1689,20 +1679,28 @@ bool GetLockedOutputs(const uint256 &genesisHash, const CAmount &nAmount, std::v
16891679
const CTransaction& tx = *ptx;
16901680
for (unsigned int j = 0; j < tx.vout.size() && nTotal < nAmount; j++) {
16911681
CTxOut txout = tx.vout[j];
1692-
if (!txout.scriptPubKey.IsWithdrawLock())
1682+
if (!txout.scriptPubKey.IsWithdrawLock()) {
16931683
continue;
1684+
}
16941685

16951686
uint256 withdrawGenHash = txout.scriptPubKey.GetWithdrawLockGenesisHash();
1696-
if (genesisHash != withdrawGenHash)
1687+
if (genesisHash != withdrawGenHash) {
16971688
continue;
1698-
if (mempool.mapWithdrawsSpentToTxid.count(std::make_pair(withdrawGenHash, COutPoint(tx.GetHash(), j))))
1689+
}
1690+
1691+
// Only written locks are filtered previously for invalid type
1692+
if (txout.nValue.IsCommitment() || txout.nAsset.IsCommitment() || txout.nAsset.GetAsset() != BITCOINID) {
1693+
continue;
1694+
}
1695+
1696+
if (mempool.mapNextTx.count(COutPoint(tx.GetHash(), j))) {
16991697
continue;
1698+
}
17001699

1701-
if (txout.scriptPubKey.IsWithdrawLock() && txout.nValue.IsExplicit()) {
1702-
res.push_back(std::make_pair(COutPoint(tx.GetHash(), j), txout.nValue.GetAmount()));
1703-
nTotal += txout.nValue.GetAmount();
1704-
if (nTotal >= nAmount)
1705-
return true;
1700+
res.push_back(std::make_pair(COutPoint(tx.GetHash(), j), txout.nValue.GetAmount()));
1701+
nTotal += txout.nValue.GetAmount();
1702+
if (nTotal >= nAmount) {
1703+
return true;
17061704
}
17071705
}
17081706
}
@@ -1712,8 +1710,9 @@ bool GetLockedOutputs(const uint256 &genesisHash, const CAmount &nAmount, std::v
17121710
//res list is already randomized
17131711
nTotal = 0;
17141712
size_t i = 0;
1715-
for (; i < res.size() && nTotal < nAmount; i++)
1713+
for (; i < res.size() && nTotal < nAmount; i++) {
17161714
nTotal += res[i].second;
1715+
}
17171716

17181717
res.resize(i);
17191718
return true;

0 commit comments

Comments
 (0)