diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 60b7fe411a..bb1d0f199b 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -130,6 +130,9 @@ protected: consensus.defaultAssumeValid = uint256S(GetArg("-con_defaultassumevalid", "0x00")); consensus.pegin_min_depth = GetArg("-peginconfirmationdepth", DEFAULT_PEGIN_CONFIRMATION_DEPTH); consensus.mandatory_coinbase_destination = StrHexToScriptWithDefault(GetArg("-con_mandatorycoinbase", ""), CScript()); // Blank script allows any coinbase destination + consensus.parent_chain_signblockscript = StrHexToScriptWithDefault(GetArg("-con_parent_chain_signblockscript", ""), CScript()); + consensus.parent_pegged_asset.SetHex(GetArg("-con_parent_pegged_asset", "0x00")); + // bitcoin regtest is the parent chain by default parentGenesisBlockHash = uint256S(GetArg("-parentgenesisblockhash", "0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206")); initialFreeCoins = GetArg("-initialfreecoins", 0); diff --git a/src/consensus/params.h b/src/consensus/params.h index 2fedac1898..5414ad0eb1 100644 --- a/src/consensus/params.h +++ b/src/consensus/params.h @@ -73,6 +73,9 @@ struct Params { CScript mandatory_coinbase_destination; CScript signblockscript; bool has_parent_chain; + CScript parent_chain_signblockscript; + CAsset parent_pegged_asset; + bool ParentChainHasPow() const { return parent_chain_signblockscript == CScript();} }; } // namespace Consensus diff --git a/src/init.cpp b/src/init.cpp index 983d96c25c..15b6e6eccc 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -518,7 +518,8 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-defaultpeggedassetname", strprintf("The name of the default asset created in the genesis block. (default: bitcoin)")); strUsage += HelpMessageOpt("-parentpubkeyprefix", strprintf(_("The byte prefix, in decimal, of the parent chain's base58 pubkey address. (default: %d)"), 111)); strUsage += HelpMessageOpt("-parentscriptprefix", strprintf(_("The byte prefix, in decimal, of the parent chain's base58 script address. (default: %d)"), 196)); - + strUsage += HelpMessageOpt("-con_parent_chain_signblockscript", _("Whether parent chain uses pow or signed blocks. If the parent chain uses signed blocks, the challenge (scriptPubKey) script. If not, an empty string. (default: empty script [ie parent uses pow])")); + strUsage += HelpMessageOpt("-con_parent_pegged_asset=", _("Asset ID (hex) for pegged asset for when parent chain has CA. (default: 0x00)")); } strUsage += HelpMessageOpt("-validatepegin", strprintf(_("Validate pegin claims. All functionaries must run this. (default: %u)"), DEFAULT_VALIDATE_PEGIN)); strUsage += HelpMessageOpt("-mainchainrpchost=", strprintf("The address which the daemon will try to connect to validate peg-ins, if enabled. (default: cookie auth)")); @@ -1024,7 +1025,7 @@ bool AppInitParameterInteraction() nConnectTimeout = DEFAULT_CONNECT_TIMEOUT; policyAsset = CAsset(uint256S(GetArg("-feeasset", chainparams.GetConsensus().pegged_asset.GetHex()))); - + // Fee-per-kilobyte amount considered the same as "free" // If you are mining, be careful setting this: // if you set it to zero then diff --git a/src/pow.cpp b/src/pow.cpp index 38643feb64..4e591aa094 100644 --- a/src/pow.cpp +++ b/src/pow.cpp @@ -57,7 +57,7 @@ bool CheckBitcoinProof(uint256 hash, unsigned int nBits) return true; } -bool CheckProof(const CBlockHeader& block, const Consensus::Params& params) +static bool CheckProofGeneric(const CBlockHeader& block, const Consensus::Params& params, const CScript& challenge) { if (block.GetHash() == params.hashGenesisBlock) return true; @@ -82,7 +82,17 @@ bool CheckProof(const CBlockHeader& block, const Consensus::Params& params) | SCRIPT_VERIFY_LOW_S // Stop easiest signature fiddling | SCRIPT_VERIFY_WITNESS // Required for cleanstack eval in VerifyScript | SCRIPT_NO_SIGHASH_BYTE; // non-Check(Multi)Sig signatures will not have sighash byte - return GenericVerifyScript(block.proof.solution, params.signblockscript, proof_flags, block); + return GenericVerifyScript(block.proof.solution, challenge, proof_flags, block); +} + +bool CheckProofSignedParent(const CBlockHeader& block, const Consensus::Params& params) +{ + return CheckProofGeneric(block, params, params.parent_chain_signblockscript); +} + +bool CheckProof(const CBlockHeader& block, const Consensus::Params& params) +{ + return CheckProofGeneric(block, params, params.signblockscript); } bool MaybeGenerateProof(const Consensus::Params& params, CBlockHeader *pblock, CWallet *pwallet) diff --git a/src/pow.h b/src/pow.h index 18b832486e..66327c4c91 100644 --- a/src/pow.h +++ b/src/pow.h @@ -21,6 +21,7 @@ class uint256; /** Check whether a block hash satisfies the proof-of-work requirement specified by nBits */ bool CheckBitcoinProof(uint256 hash, unsigned int nBits); +bool CheckProofSignedParent(const CBlockHeader& block, const Consensus::Params& params); bool CheckProof(const CBlockHeader& block, const Consensus::Params&); /** Scans nonces looking for a hash with at least some zero bits */ bool MaybeGenerateProof(const Consensus::Params& params, CBlockHeader* pblock, CWallet* pwallet); diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index b9a1c20909..b685b5d475 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -63,7 +63,7 @@ UniValue blockheaderToJSON(const CBlockIndex* blockindex) result.push_back(Pair("time", (int64_t)blockindex->nTime)); result.push_back(Pair("mediantime", (int64_t)blockindex->GetMedianTimePast())); result.push_back(Pair("signblock_witness_asm", ScriptToAsmStr(blockindex->proof.solution))); - result.push_back(Pair("signblock_witness_hex", HexStr(blockindex->proof.solution.begin(), blockindex->proof.solution.end()))); + result.push_back(Pair("signblock_witness_hex", HexStr(blockindex->proof.solution))); if (blockindex->pprev) result.push_back(Pair("previousblockhash", blockindex->pprev->GetBlockHash().GetHex())); @@ -105,7 +105,7 @@ UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool tx result.push_back(Pair("time", block.GetBlockTime())); result.push_back(Pair("mediantime", (int64_t)blockindex->GetMedianTimePast())); result.push_back(Pair("signblock_witness_asm", ScriptToAsmStr(blockindex->proof.solution))); - result.push_back(Pair("signblock_witness_hex", HexStr(blockindex->proof.solution.begin(), blockindex->proof.solution.end()))); + result.push_back(Pair("signblock_witness_hex", HexStr(blockindex->proof.solution))); if (blockindex->pprev) result.push_back(Pair("previousblockhash", blockindex->pprev->GetBlockHash().GetHex())); @@ -1105,10 +1105,14 @@ UniValue getsidechaininfo(const JSONRPCRequest& request) "Returns an object containing various state info regarding sidechain functionality.\n" "\nResult:\n" "{\n" - " \"fedpegscript\": \"xxxx\", (string) The fedpegscript in hex\n" + " \"fedpegscript\": \"xxxx\", (string) The fedpegscript in hex\n" " \"pegged_asset\" : \"xxxx\", (string) Pegged asset type in hex\n" " \"min_peg_diff\" : \"xxxx\", (string) The minimum difficulty parent chain header target. Peg-in headers that have less work will be rejected as an anti-Dos measure.\n" " \"parent_blockhash\" : \"xxxx\", (string) The parent genesis blockhash as source of pegged-in funds.\n" + " \"parent_chain_has_pow\": \"xxxx\", (boolean) Whether parent chain has pow or signed blocks.\n" + " \"parent_chain_signblockscript_asm\": \"xxxx\", (string) If the parent chain has signed blocks, its signblockscript in ASM.\n" + " \"parent_chain_signblockscript_hex\": \"xxxx\", (string) If the parent chain has signed blocks, its signblockscript in hex.\n" + " \"parent_pegged_asset\": \"xxxx\", (boolean) If the parent chain has Confidential Assets, the asset id of the pegged asset in that chain.\n" "}\n" "\nExamples:\n" + HelpExampleCli("getsidechaininfo", "") @@ -1125,6 +1129,12 @@ UniValue getsidechaininfo(const JSONRPCRequest& request) obj.push_back(Pair("pegged_asset", consensus.pegged_asset.GetHex())); obj.push_back(Pair("min_peg_diff", consensus.parentChainPowLimit.GetHex())); obj.push_back(Pair("parent_blockhash", parent_blockhash.GetHex())); + obj.push_back(Pair("parent_chain_has_pow", consensus.ParentChainHasPow())); + if (!consensus.ParentChainHasPow()) { + obj.push_back(Pair("parent_chain_signblockscript_asm", ScriptToAsmStr(consensus.parent_chain_signblockscript))); + obj.push_back(Pair("parent_chain_signblockscript_hex", HexStr(consensus.parent_chain_signblockscript))); + obj.push_back(Pair("parent_pegged_asset", HexStr(consensus.parent_pegged_asset))); + } return obj; } diff --git a/src/validation.cpp b/src/validation.cpp index d6b479b64f..f368785d2c 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -17,6 +17,7 @@ #include "crypto/hmac_sha256.h" #include "init.h" #include "issuance.h" +#include "merkleblock.h" #include "policy/fees.h" #include "policy/policy.h" #include "pow.h" @@ -2374,6 +2375,21 @@ bool GetAmountFromParentChainPegin(CAmount& amount, const Sidechain::Bitcoin::CT return true; } +bool GetAmountFromParentChainPegin(CAmount& amount, const CTransaction& txBTC, unsigned int nOut) +{ + if (!txBTC.vout[nOut].nValue.IsExplicit()) { + return false; + } + if (!txBTC.vout[nOut].nAsset.IsExplicit()) { + return false; + } + if (txBTC.vout[nOut].nAsset.GetAsset() != Params().GetConsensus().parent_pegged_asset) { + return false; + } + amount = txBTC.vout[nOut].nValue.GetAmount(); + return true; +} + template static bool GetBlockAndTxFromMerkleBlock(uint256& block_hash, uint256& tx_hash, T& merkle_block, const std::vector& merkle_block_raw) { @@ -2497,20 +2513,36 @@ bool IsValidPeginWitness(const CScriptWitness& pegin_witness, const COutPoint& p uint256 block_hash; uint256 tx_hash; - // Get txout proof - Sidechain::Bitcoin::CMerkleBlock merkle_block; - if (!GetBlockAndTxFromMerkleBlock(block_hash, tx_hash, merkle_block, stack[5])) { - return false; - } - if (!CheckBitcoinProof(block_hash, merkle_block.header.nBits)) { - return false; - } + if (Params().GetConsensus().ParentChainHasPow()) { - // Get serialized transaction - Sidechain::Bitcoin::CTransactionRef pegtx; - if (!CheckPeginTx(stack[4], pegtx, prevout, value, claim_script)) { - return false; + Sidechain::Bitcoin::CMerkleBlock merkle_block_pow; + if (!GetBlockAndTxFromMerkleBlock(block_hash, tx_hash, merkle_block_pow, stack[5])) { + return false; + } + if (!CheckBitcoinProof(block_hash, merkle_block_pow.header.nBits)) { + return false; + } + + Sidechain::Bitcoin::CTransactionRef pegtx; + if (!CheckPeginTx(stack[4], pegtx, prevout, value, claim_script)) { + return false; + } + } else { + + CMerkleBlock merkle_block; + if (!GetBlockAndTxFromMerkleBlock(block_hash, tx_hash, merkle_block, stack[5])) { + return false; + } + + if (!CheckProofSignedParent(merkle_block.header, Params().GetConsensus())) { + return false; + } + + CTransactionRef pegtx; + if (!CheckPeginTx(stack[4], pegtx, prevout, value, claim_script)) { + return false; + } } // Check that the merkle proof corresponds to the txid diff --git a/src/validation.h b/src/validation.h index ded31539fc..c3c4d58607 100644 --- a/src/validation.h +++ b/src/validation.h @@ -267,6 +267,7 @@ void ThreadScriptCheck(); /** Check if bitcoind connection via RPC is correctly working*/ bool BitcoindRPCCheck(bool init); bool GetAmountFromParentChainPegin(CAmount& amount, const Sidechain::Bitcoin::CTransaction& txBTC, unsigned int nOut); +bool GetAmountFromParentChainPegin(CAmount& amount, const CTransaction& txBTC, unsigned int nOut); /** Checks pegin witness for validity */ bool IsValidPeginWitness(const CScriptWitness& pegin_witness, const COutPoint& prevout, bool check_depth = true); /** Extracts an output from pegin witness for evaluation as a normal output */ diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index db5dcb1b35..acfd861315 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -2082,7 +2082,7 @@ UniValue gettransaction(const JSONRPCRequest& request) " \"fee\": x.xxx, (numeric) The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the \n" " 'send' category of transactions.\n" " \"abandoned\": xxx (bool) 'true' if the transaction has been abandoned (inputs are respendable). Only available for the \n" - " 'send' category of transactions.\n" + " 'send' category of transactions.\n" " }\n" " ,...\n" " ],\n" @@ -3512,7 +3512,8 @@ UniValue sendtomainchain(const JSONRPCRequest& request) extern UniValue signrawtransaction(const JSONRPCRequest& request); extern UniValue sendrawtransaction(const JSONRPCRequest& request); -unsigned int GetPeginTxnOutputIndex(const Sidechain::Bitcoin::CTransaction& txn, const CScript& witnessProgram) +template +unsigned int GetPeginTxnOutputIndex(const T_tx& txn, const CScript& witnessProgram) { unsigned int nOut = 0; //Call contracthashtool @@ -3523,7 +3524,8 @@ unsigned int GetPeginTxnOutputIndex(const Sidechain::Bitcoin::CTransaction& txn, return nOut; } -UniValue createrawpegin(const JSONRPCRequest& request) +template +static UniValue createrawpegin(const JSONRPCRequest& request, T_tx_ref& txBTCRef, T_tx& tx_aux, T_merkle_block& merkleBlock) { if (request.fHelp || request.params.size() < 2 || request.params.size() > 3) throw std::runtime_error( @@ -3552,18 +3554,16 @@ UniValue createrawpegin(const JSONRPCRequest& request) std::vector txData = ParseHex(request.params[0].get_str()); CDataStream ssTx(txData, SER_NETWORK, PROTOCOL_VERSION); - Sidechain::Bitcoin::CTransactionRef txBTCRef; try { ssTx >> txBTCRef; } catch (...) { throw JSONRPCError(RPC_TYPE_ERROR, "The included bitcoinTx is malformed. Are you sure that is the whole string?"); } - Sidechain::Bitcoin::CTransaction txBTC(*txBTCRef); + T_tx txBTC(*txBTCRef); std::vector txOutProofData = ParseHex(request.params[1].get_str()); CDataStream ssTxOutProof(txOutProofData, SER_NETWORK, PROTOCOL_VERSION); - Sidechain::Bitcoin::CMerkleBlock merkleBlock; try { ssTxOutProof >> merkleBlock; } @@ -3571,8 +3571,9 @@ UniValue createrawpegin(const JSONRPCRequest& request) throw JSONRPCError(RPC_TYPE_ERROR, "The included txoutproof is malformed. Are you sure that is the whole string?"); } - if (!ssTxOutProof.empty() || !CheckBitcoinProof(merkleBlock.header.GetHash(), merkleBlock.header.nBits)) + if (!ssTxOutProof.empty()) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid tx out proof"); + } std::vector txHashes; std::vector txIndices; @@ -3622,7 +3623,11 @@ UniValue createrawpegin(const JSONRPCRequest& request) throw JSONRPCError(RPC_INVALID_PARAMETER, "Given or recovered script is not a witness program."); } - CAmount value = txBTC.vout[nOut].nValue; + CAmount value = 0; + if (!GetAmountFromParentChainPegin(value, txBTC, nOut)) { + throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Amounts to pegin must be explicit and asset must be %s", + Params().GetConsensus().parent_pegged_asset.GetHex())); + } CDataStream stream(0, 0); try { @@ -3703,6 +3708,29 @@ UniValue createrawpegin(const JSONRPCRequest& request) return ret; } +UniValue createrawpegin(const JSONRPCRequest& request) +{ + UniValue ret(UniValue::VOBJ); + if (Params().GetConsensus().ParentChainHasPow()) { + Sidechain::Bitcoin::CTransactionRef txBTCRef; + Sidechain::Bitcoin::CTransaction tx_aux; + Sidechain::Bitcoin::CMerkleBlock merkleBlock; + ret = createrawpegin(request, txBTCRef, tx_aux, merkleBlock); + if (!CheckBitcoinProof(merkleBlock.header.GetHash(), merkleBlock.header.nBits)) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid tx out proof"); + } + } else { + CTransactionRef txBTCRef; + CTransaction tx_aux; + CMerkleBlock merkleBlock; + ret = createrawpegin(request, txBTCRef, tx_aux, merkleBlock); + if (!CheckProofSignedParent(merkleBlock.header, Params().GetConsensus())) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid tx out proof"); + } + } + return ret; +} + UniValue claimpegin(const JSONRPCRequest& request) {