diff --git a/src/pegins.cpp b/src/pegins.cpp index 19ef359e1f..74349edbc8 100644 --- a/src/pegins.cpp +++ b/src/pegins.cpp @@ -189,7 +189,7 @@ static bool CheckPeginTx(const std::vector& tx_data, T& pegtx, co } template -static bool GetBlockAndTxFromMerkleBlock(uint256& block_hash, uint256& tx_hash, T& merkle_block, const std::vector& merkle_block_raw) +static bool GetBlockAndTxFromMerkleBlock(uint256& block_hash, uint256& tx_hash, unsigned int& tx_index, T& merkle_block, const std::vector& merkle_block_raw) { try { std::vector tx_hashes; @@ -205,6 +205,7 @@ static bool GetBlockAndTxFromMerkleBlock(uint256& block_hash, uint256& tx_hash, return false; } tx_hash = tx_hashes[0]; + tx_index = tx_indices[0]; } catch (std::exception& e) { // Invalid encoding of merkle block return false; @@ -295,10 +296,11 @@ bool IsValidPeginWitness(const CScriptWitness& pegin_witness, const COutPoint& p uint256 block_hash; uint256 tx_hash; int num_txs; + unsigned int tx_index = 0; // Get txout proof if (Params().GetConsensus().ParentChainHasPow()) { Sidechain::Bitcoin::CMerkleBlock merkle_block_pow; - if (!GetBlockAndTxFromMerkleBlock(block_hash, tx_hash, merkle_block_pow, stack[5])) { + if (!GetBlockAndTxFromMerkleBlock(block_hash, tx_hash, tx_index, merkle_block_pow, stack[5])) { err_msg = "Could not extract block and tx from merkleblock."; return false; } @@ -316,7 +318,7 @@ bool IsValidPeginWitness(const CScriptWitness& pegin_witness, const COutPoint& p num_txs = merkle_block_pow.txn.GetNumTransactions(); } else { CMerkleBlock merkle_block; - if (!GetBlockAndTxFromMerkleBlock(block_hash, tx_hash, merkle_block, stack[5])) { + if (!GetBlockAndTxFromMerkleBlock(block_hash, tx_hash, tx_index, merkle_block, stack[5])) { err_msg = "Could not extract block and tx from merkleblock."; return false; } @@ -354,7 +356,13 @@ bool IsValidPeginWitness(const CScriptWitness& pegin_witness, const COutPoint& p // Finally, validate peg-in via rpc call if (check_depth && gArgs.GetBoolArg("-validatepegin", Params().GetConsensus().has_parent_chain)) { - if (!IsConfirmedBitcoinBlock(block_hash, Params().GetConsensus().pegin_min_depth, num_txs)) { + unsigned int required_depth = Params().GetConsensus().pegin_min_depth; + // Don't allow coinbase output claims before coinbase maturity + if (tx_index == 0) { + required_depth = std::max(required_depth, (unsigned int)COINBASE_MATURITY); + } + LogPrintf("Required depth: %d\n", required_depth); + if (!IsConfirmedBitcoinBlock(block_hash, required_depth, num_txs)) { err_msg = "Needs more confirmations."; return false; } diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 3e2572c91c..0266c1f97a 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -5240,7 +5240,11 @@ static UniValue createrawpegin(const JSONRPCRequest& request, T_tx_ref& txBTCRef // Additional block lee-way to avoid bitcoin block races if (gArgs.GetBoolArg("-validatepegin", Params().GetConsensus().has_parent_chain)) { - ret.pushKV("mature", IsConfirmedBitcoinBlock(merkleBlock.header.GetHash(), Params().GetConsensus().pegin_min_depth+2, merkleBlock.txn.GetNumTransactions())); + unsigned int required_depth = Params().GetConsensus().pegin_min_depth + 2; + if (txIndices[0] == 0) { + required_depth = std::max(required_depth, (unsigned int)COINBASE_MATURITY+2); + } + ret.pushKV("mature", IsConfirmedBitcoinBlock(merkleBlock.header.GetHash(), required_depth, merkleBlock.txn.GetNumTransactions())); } return ret; diff --git a/test/functional/feature_fedpeg.py b/test/functional/feature_fedpeg.py index e8ea452c3a..f130767099 100755 --- a/test/functional/feature_fedpeg.py +++ b/test/functional/feature_fedpeg.py @@ -10,6 +10,8 @@ from test_framework.util import ( get_datadir_path, rpc_port, p2p_port, + assert_raises_rpc_error, + assert_equal, ) from decimal import Decimal @@ -53,9 +55,10 @@ class FedPegTest(BitcoinTestFramework): else: extra_args.extend([ "-validatepegin=0", - "-initialfreecoins=2100000000000000", + "-initialfreecoins=0", "-anyonecanspendaremine", "-signblockscript=51", # OP_TRUE + '-con_blocksubsidy=5000000000', ]) self.add_nodes(1, [extra_args], chain=[parent_chain], binary=parent_binary) @@ -390,7 +393,38 @@ class FedPegTest(BitcoinTestFramework): # Make sure balance went down assert(bal_2 + 1 < bal_1) - sidechain.sendtomainchain(some_btc_addr, bal_2, True) + # Have bitcoin output go directly into a claim output + pegin_info = sidechain.getpeginaddress() + mainchain_addr = pegin_info["mainchain_address"] + claim_script = pegin_info["claim_script"] + # Watch the address so we can get tx without txindex + parent.importaddress(mainchain_addr) + claim_block = parent.generatetoaddress(50, mainchain_addr)[0] + block_coinbase = parent.getblock(claim_block, 2)["tx"][0] + claim_txid = block_coinbase["txid"] + claim_tx = block_coinbase["hex"] + claim_proof = parent.gettxoutproof([claim_txid], claim_block) + + # Can't claim something even though it has 50 confirms since it's coinbase + assert_raises_rpc_error(-8, "Peg-in Bitcoin transaction needs more confirmations to be sent.", sidechain.claimpegin, claim_tx, claim_proof) + # If done via raw API, still doesn't work + coinbase_pegin = sidechain.createrawpegin(claim_tx, claim_proof) + assert_equal(coinbase_pegin["mature"], False) + signed_pegin = sidechain.signrawtransactionwithwallet(coinbase_pegin["hex"])["hex"] + assert_raises_rpc_error(-26, "bad-pegin-witness, Needs more confirmations.", sidechain.sendrawtransaction, signed_pegin) + + # 50 more blocks to allow wallet to make it succeed by relay and consensus + parent.generatetoaddress(50, parent.getnewaddress()) + # Wallet still doesn't want to for 2 more confirms + assert_equal(sidechain.createrawpegin(claim_tx, claim_proof)["mature"], False) + # But we can just shoot it off + claim_txid = sidechain.sendrawtransaction(signed_pegin) + sidechain.generatetoaddress(1, sidechain.getnewaddress()) + assert_equal(sidechain.gettransaction(claim_txid)["confirmations"], 1) + + + # Send rest of coins using subtractfee from output arg + sidechain.sendtomainchain(some_btc_addr, sidechain.getwalletinfo()["balance"]['bitcoin'], True) assert(sidechain.getwalletinfo()["balance"]['bitcoin'] == 0)