Skip to content

Commit 870e416

Browse files
committed
Enforce coinbase maturity on bitcoin peg-ins
1 parent f084479 commit 870e416

File tree

3 files changed

+53
-7
lines changed

3 files changed

+53
-7
lines changed

src/pegins.cpp

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ static bool CheckPeginTx(const std::vector<unsigned char>& tx_data, T& pegtx, co
189189
}
190190

191191
template<typename T>
192-
static bool GetBlockAndTxFromMerkleBlock(uint256& block_hash, uint256& tx_hash, T& merkle_block, const std::vector<unsigned char>& merkle_block_raw)
192+
static bool GetBlockAndTxFromMerkleBlock(uint256& block_hash, uint256& tx_hash, unsigned int& tx_index, T& merkle_block, const std::vector<unsigned char>& merkle_block_raw)
193193
{
194194
try {
195195
std::vector<uint256> tx_hashes;
@@ -205,6 +205,7 @@ static bool GetBlockAndTxFromMerkleBlock(uint256& block_hash, uint256& tx_hash,
205205
return false;
206206
}
207207
tx_hash = tx_hashes[0];
208+
tx_index = tx_indices[0];
208209
} catch (std::exception& e) {
209210
// Invalid encoding of merkle block
210211
return false;
@@ -295,10 +296,11 @@ bool IsValidPeginWitness(const CScriptWitness& pegin_witness, const COutPoint& p
295296
uint256 block_hash;
296297
uint256 tx_hash;
297298
int num_txs;
299+
unsigned int tx_index = 0;
298300
// Get txout proof
299301
if (Params().GetConsensus().ParentChainHasPow()) {
300302
Sidechain::Bitcoin::CMerkleBlock merkle_block_pow;
301-
if (!GetBlockAndTxFromMerkleBlock(block_hash, tx_hash, merkle_block_pow, stack[5])) {
303+
if (!GetBlockAndTxFromMerkleBlock(block_hash, tx_hash, tx_index, merkle_block_pow, stack[5])) {
302304
err_msg = "Could not extract block and tx from merkleblock.";
303305
return false;
304306
}
@@ -316,7 +318,7 @@ bool IsValidPeginWitness(const CScriptWitness& pegin_witness, const COutPoint& p
316318
num_txs = merkle_block_pow.txn.GetNumTransactions();
317319
} else {
318320
CMerkleBlock merkle_block;
319-
if (!GetBlockAndTxFromMerkleBlock(block_hash, tx_hash, merkle_block, stack[5])) {
321+
if (!GetBlockAndTxFromMerkleBlock(block_hash, tx_hash, tx_index, merkle_block, stack[5])) {
320322
err_msg = "Could not extract block and tx from merkleblock.";
321323
return false;
322324
}
@@ -354,7 +356,13 @@ bool IsValidPeginWitness(const CScriptWitness& pegin_witness, const COutPoint& p
354356

355357
// Finally, validate peg-in via rpc call
356358
if (check_depth && gArgs.GetBoolArg("-validatepegin", Params().GetConsensus().has_parent_chain)) {
357-
if (!IsConfirmedBitcoinBlock(block_hash, Params().GetConsensus().pegin_min_depth, num_txs)) {
359+
unsigned int required_depth = Params().GetConsensus().pegin_min_depth;
360+
// Don't allow coinbase output claims before coinbase maturity
361+
if (tx_index == 0) {
362+
required_depth = std::max(required_depth, (unsigned int)COINBASE_MATURITY);
363+
}
364+
LogPrintf("Required depth: %d\n", required_depth);
365+
if (!IsConfirmedBitcoinBlock(block_hash, required_depth, num_txs)) {
358366
err_msg = "Needs more confirmations.";
359367
return false;
360368
}

src/wallet/rpcwallet.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5240,7 +5240,11 @@ static UniValue createrawpegin(const JSONRPCRequest& request, T_tx_ref& txBTCRef
52405240

52415241
// Additional block lee-way to avoid bitcoin block races
52425242
if (gArgs.GetBoolArg("-validatepegin", Params().GetConsensus().has_parent_chain)) {
5243-
ret.pushKV("mature", IsConfirmedBitcoinBlock(merkleBlock.header.GetHash(), Params().GetConsensus().pegin_min_depth+2, merkleBlock.txn.GetNumTransactions()));
5243+
unsigned int required_depth = Params().GetConsensus().pegin_min_depth + 2;
5244+
if (txIndices[0] == 0) {
5245+
required_depth = std::max(required_depth, (unsigned int)COINBASE_MATURITY+2);
5246+
}
5247+
ret.pushKV("mature", IsConfirmedBitcoinBlock(merkleBlock.header.GetHash(), required_depth, merkleBlock.txn.GetNumTransactions()));
52445248
}
52455249

52465250
return ret;

test/functional/feature_fedpeg.py

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
get_datadir_path,
1111
rpc_port,
1212
p2p_port,
13+
assert_raises_rpc_error,
14+
assert_equal,
1315
)
1416
from decimal import Decimal
1517

@@ -53,9 +55,10 @@ def setup_network(self, split=False):
5355
else:
5456
extra_args.extend([
5557
"-validatepegin=0",
56-
"-initialfreecoins=2100000000000000",
58+
"-initialfreecoins=0",
5759
"-anyonecanspendaremine",
5860
"-signblockscript=51", # OP_TRUE
61+
'-con_blocksubsidy=5000000000',
5962
])
6063

6164
self.add_nodes(1, [extra_args], chain=[parent_chain], binary=parent_binary)
@@ -390,7 +393,38 @@ def run_test(self):
390393
# Make sure balance went down
391394
assert(bal_2 + 1 < bal_1)
392395

393-
sidechain.sendtomainchain(some_btc_addr, bal_2, True)
396+
# Have bitcoin output go directly into a claim output
397+
pegin_info = sidechain.getpeginaddress()
398+
mainchain_addr = pegin_info["mainchain_address"]
399+
claim_script = pegin_info["claim_script"]
400+
# Watch the address so we can get tx without txindex
401+
parent.importaddress(mainchain_addr)
402+
claim_block = parent.generatetoaddress(50, mainchain_addr)[0]
403+
block_coinbase = parent.getblock(claim_block, 2)["tx"][0]
404+
claim_txid = block_coinbase["txid"]
405+
claim_tx = block_coinbase["hex"]
406+
claim_proof = parent.gettxoutproof([claim_txid], claim_block)
407+
408+
# Can't claim something even though it has 50 confirms since it's coinbase
409+
assert_raises_rpc_error(-8, "Peg-in Bitcoin transaction needs more confirmations to be sent.", sidechain.claimpegin, claim_tx, claim_proof)
410+
# If done via raw API, still doesn't work
411+
coinbase_pegin = sidechain.createrawpegin(claim_tx, claim_proof)
412+
assert_equal(coinbase_pegin["mature"], False)
413+
signed_pegin = sidechain.signrawtransactionwithwallet(coinbase_pegin["hex"])["hex"]
414+
assert_raises_rpc_error(-26, "bad-pegin-witness, Needs more confirmations.", sidechain.sendrawtransaction, signed_pegin)
415+
416+
# 50 more blocks to allow wallet to make it succeed by relay and consensus
417+
parent.generatetoaddress(50, parent.getnewaddress())
418+
# Wallet still doesn't want to for 2 more confirms
419+
assert_equal(sidechain.createrawpegin(claim_tx, claim_proof)["mature"], False)
420+
# But we can just shoot it off
421+
claim_txid = sidechain.sendrawtransaction(signed_pegin)
422+
sidechain.generatetoaddress(1, sidechain.getnewaddress())
423+
assert_equal(sidechain.gettransaction(claim_txid)["confirmations"], 1)
424+
425+
426+
# Send rest of coins using subtractfee from output arg
427+
sidechain.sendtomainchain(some_btc_addr, sidechain.getwalletinfo()["balance"]['bitcoin'], True)
394428

395429
assert(sidechain.getwalletinfo()["balance"]['bitcoin'] == 0)
396430

0 commit comments

Comments
 (0)