From 8c31e2b6c5668b6e59db0ed5375db1c1b017bedc Mon Sep 17 00:00:00 2001 From: HyperPeek Date: Fri, 21 May 2021 17:29:50 +0200 Subject: [PATCH 01/39] Re-Add Feature: Add P2SH support - Core Protocol Development Proposal 001 (PR #873) Merged, as we want to continue testing on develop. --- .github/ISSUE_TEMPLATE.md | 2 +- src/Makefile.test.include | 1 + src/assets/assets.cpp | 45 +- src/assets/assets.h | 2 +- src/assets/messages.cpp | 2 +- src/bloom.cpp | 3 +- src/chainparams.cpp | 28 +- src/consensus/params.h | 1 + src/consensus/tx_verify.cpp | 14 +- src/core_write.cpp | 3 +- src/policy/policy.cpp | 12 +- src/policy/policy.h | 2 +- src/rpc/blockchain.cpp | 1 + src/rpc/misc.cpp | 3 +- src/rpc/rawtransaction.cpp | 2 +- src/script/interpreter.cpp | 3 +- src/script/ismine.cpp | 63 +-- src/script/script.cpp | 133 ++++-- src/script/script.h | 8 +- src/script/sign.cpp | 100 +++-- src/script/standard.cpp | 38 +- src/script/standard.h | 4 +- src/test/assets/asset_p2sh_tests.cpp | 59 +++ src/test/multisig_tests.cpp | 11 +- src/test/script_standard_tests.cpp | 61 +-- src/txmempool.cpp | 34 +- src/validation.cpp | 96 +++-- src/validation.h | 2 + src/versionbits.cpp | 4 + src/wallet/wallet.cpp | 11 +- src/wallet/wallet.h | 1 + test/functional/feature_assets_p2sh.py | 543 +++++++++++++++++++++++++ test/functional/test_runner.py | 1 + 33 files changed, 1047 insertions(+), 246 deletions(-) create mode 100644 src/test/assets/asset_p2sh_tests.cpp create mode 100755 test/functional/feature_assets_p2sh.py diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index a1e3c60aa3..972cd492a3 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -2,7 +2,7 @@ This issue tracker is only for technical issues related to Ravencoin. -General Ravencoin questions and/or support requests and are best directed to the [Ravencoin Discord](https://discord.gg/jn6uhur)). +General Ravencoin questions and/or support requests and are best directed to the [Ravencoin Discord](https://discord.gg/GwtXdyc). For reporting security issues, please direct message one of the core developers in discord. diff --git a/src/Makefile.test.include b/src/Makefile.test.include index 54775b819c..ff913db6c9 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -27,6 +27,7 @@ GENERATED_TEST_FILES = $(JSON_TEST_FILES:.json=.json.h) $(RAW_TEST_FILES:.raw=.r RAVEN_TESTS =\ test/assets/asset_tests.cpp \ test/assets/serialization_tests.cpp \ + test/assets/asset_p2sh_tests.cpp \ test/assets/asset_tx_tests.cpp \ test/assets/cache_tests.cpp \ test/assets/asset_reissue_tests.cpp \ diff --git a/src/assets/assets.cpp b/src/assets/assets.cpp index 18717c6621..697145196c 100644 --- a/src/assets/assets.cpp +++ b/src/assets/assets.cpp @@ -3169,8 +3169,9 @@ bool IsScriptNewAsset(const CScript& scriptPubKey) bool IsScriptNewAsset(const CScript& scriptPubKey, int& nStartingIndex) { int nType = 0; - bool fIsOwner =false; - if (scriptPubKey.IsAssetScript(nType, fIsOwner, nStartingIndex)) { + int nScriptType = 0; + bool fIsOwner = false; + if (scriptPubKey.IsAssetScript(nType, nScriptType, fIsOwner, nStartingIndex)) { return nType == TX_NEW_ASSET && !fIsOwner; } return false; @@ -3185,8 +3186,9 @@ bool IsScriptNewUniqueAsset(const CScript& scriptPubKey) bool IsScriptNewUniqueAsset(const CScript &scriptPubKey, int &nStartingIndex) { int nType = 0; + int nScriptType = 0; bool fIsOwner = false; - if (!scriptPubKey.IsAssetScript(nType, fIsOwner, nStartingIndex)) + if (!scriptPubKey.IsAssetScript(nType, nScriptType, fIsOwner, nStartingIndex)) return false; CNewAsset asset; @@ -3210,8 +3212,9 @@ bool IsScriptNewMsgChannelAsset(const CScript& scriptPubKey) bool IsScriptNewMsgChannelAsset(const CScript &scriptPubKey, int &nStartingIndex) { int nType = 0; + int nScriptType = 0; bool fIsOwner = false; - if (!scriptPubKey.IsAssetScript(nType, fIsOwner, nStartingIndex)) + if (!scriptPubKey.IsAssetScript(nType, nScriptType, fIsOwner, nStartingIndex)) return false; CNewAsset asset; @@ -3236,8 +3239,9 @@ bool IsScriptOwnerAsset(const CScript& scriptPubKey) bool IsScriptOwnerAsset(const CScript& scriptPubKey, int& nStartingIndex) { int nType = 0; + int nScriptType = 0; bool fIsOwner =false; - if (scriptPubKey.IsAssetScript(nType, fIsOwner, nStartingIndex)) { + if (scriptPubKey.IsAssetScript(nType, nScriptType, fIsOwner, nStartingIndex)) { return nType == TX_NEW_ASSET && fIsOwner; } @@ -3253,8 +3257,9 @@ bool IsScriptReissueAsset(const CScript& scriptPubKey) bool IsScriptReissueAsset(const CScript& scriptPubKey, int& nStartingIndex) { int nType = 0; + int nScriptType = 0; bool fIsOwner =false; - if (scriptPubKey.IsAssetScript(nType, fIsOwner, nStartingIndex)) { + if (scriptPubKey.IsAssetScript(nType, nScriptType, fIsOwner, nStartingIndex)) { return nType == TX_REISSUE_ASSET; } @@ -3270,8 +3275,9 @@ bool IsScriptTransferAsset(const CScript& scriptPubKey) bool IsScriptTransferAsset(const CScript& scriptPubKey, int& nStartingIndex) { int nType = 0; + int nScriptType = 0; bool fIsOwner = false; - if (scriptPubKey.IsAssetScript(nType, fIsOwner, nStartingIndex)) { + if (scriptPubKey.IsAssetScript(nType, nScriptType, fIsOwner, nStartingIndex)) { return nType == TX_TRANSFER_ASSET; } @@ -3287,8 +3293,9 @@ bool IsScriptNewQualifierAsset(const CScript& scriptPubKey) bool IsScriptNewQualifierAsset(const CScript &scriptPubKey, int &nStartingIndex) { int nType = 0; + int nScriptType = 0; bool fIsOwner = false; - if (!scriptPubKey.IsAssetScript(nType, fIsOwner, nStartingIndex)) + if (!scriptPubKey.IsAssetScript(nType, nScriptType, fIsOwner, nStartingIndex)) return false; CNewAsset asset; @@ -3312,8 +3319,9 @@ bool IsScriptNewRestrictedAsset(const CScript& scriptPubKey) bool IsScriptNewRestrictedAsset(const CScript &scriptPubKey, int &nStartingIndex) { int nType = 0; + int nScriptType = 0; bool fIsOwner = false; - if (!scriptPubKey.IsAssetScript(nType, fIsOwner, nStartingIndex)) + if (!scriptPubKey.IsAssetScript(nType, nScriptType, fIsOwner, nStartingIndex)) return false; CNewAsset asset; @@ -3506,12 +3514,15 @@ bool GetAssetData(const CScript& script, CAssetOutputEntry& data) std::string assetName = ""; int nType = 0; + int nScriptType = 0; bool fIsOwner = false; - if (!script.IsAssetScript(nType, fIsOwner)) { + if (!script.IsAssetScript(nType, nScriptType, fIsOwner)) { return false; } txnouttype type = txnouttype(nType); + txnouttype scriptType = txnouttype(nScriptType); + data.scriptType = scriptType; // Get the New Asset or Transfer Asset from the scriptPubKey if (type == TX_NEW_ASSET && !fIsOwner) { @@ -4439,13 +4450,13 @@ void GetTxOutAssetTypes(const std::vector& vout, int& issues, int& reiss } } -bool ParseAssetScript(CScript scriptPubKey, uint160 &hashBytes, std::string &assetName, CAmount &assetAmount) { +bool ParseAssetScript(CScript scriptPubKey, uint160 &hashBytes, int& nScriptType, std::string &assetName, CAmount &assetAmount) { int nType; bool fIsOwner; int _nStartingPoint; std::string _strAddress; bool isAsset = false; - if (scriptPubKey.IsAssetScript(nType, fIsOwner, _nStartingPoint)) { + if (scriptPubKey.IsAssetScript(nType, nScriptType, fIsOwner, _nStartingPoint)) { if (nType == TX_NEW_ASSET) { if (fIsOwner) { if (OwnerAssetFromScript(scriptPubKey, assetName, _strAddress)) { @@ -4489,8 +4500,16 @@ bool ParseAssetScript(CScript scriptPubKey, uint160 &hashBytes, std::string &ass // LogPrintf("%s : Found no asset in script: %s", __func__, HexStr(scriptPubKey)); } if (isAsset) { + if (nScriptType == TX_SCRIPTHASH) { + hashBytes = uint160(std::vector (scriptPubKey.begin()+2, scriptPubKey.begin()+22)); + } else if (nScriptType == TX_PUBKEYHASH) { + hashBytes = uint160(std::vector (scriptPubKey.begin()+3, scriptPubKey.begin()+23)); + } else { + return false; + } + // LogPrintf("%s : Found assets in script at address %s : %s (%s)", __func__, _strAddress, assetName, assetAmount); - hashBytes = uint160(std::vector (scriptPubKey.begin()+3, scriptPubKey.begin()+23)); + return true; } return false; diff --git a/src/assets/assets.h b/src/assets/assets.h index df98c5729d..a64323f54b 100644 --- a/src/assets/assets.h +++ b/src/assets/assets.h @@ -549,7 +549,7 @@ bool VerifyWalletHasAsset(const std::string& asset_name, std::pair& qualifiers); diff --git a/src/assets/messages.cpp b/src/assets/messages.cpp index 09a439e149..ca1ac30988 100644 --- a/src/assets/messages.cpp +++ b/src/assets/messages.cpp @@ -216,7 +216,7 @@ bool ScanForMessageChannels(std::string& strError) } for (auto out : ptx->vout) { - int nType = -1; + int nType = 0; bool fOwner = false; if (vpwallets[0]->IsMine(out) == ISMINE_SPENDABLE) { // Is the out mine if (out.scriptPubKey.IsAssetScript(nType, fOwner)) { diff --git a/src/bloom.cpp b/src/bloom.cpp index bdd095056e..3ace94fc42 100644 --- a/src/bloom.cpp +++ b/src/bloom.cpp @@ -166,8 +166,9 @@ bool CBloomFilter::IsRelevantAndUpdate(const CTransaction& tx) else if ((nFlags & BLOOM_UPDATE_MASK) == BLOOM_UPDATE_P2PUBKEY_ONLY) { txnouttype type; + txnouttype scriptType; std::vector > vSolutions; - if (Solver(txout.scriptPubKey, type, vSolutions) && + if (Solver(txout.scriptPubKey, type, scriptType, vSolutions) && (type == TX_PUBKEY || type == TX_MULTISIG)) insert(COutPoint(hash, i)); } diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 6cad9837fd..947a86f3af 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -161,7 +161,13 @@ class CMainParams : public CChainParams { consensus.vDeployments[Consensus::DEPLOYMENT_COINBASE_ASSETS].nTimeout = 1628877600; // UTC: Fri Aug 13 2021 18:00:00 consensus.vDeployments[Consensus::DEPLOYMENT_COINBASE_ASSETS].nOverrideRuleChangeActivationThreshold = 1411; // Approx 70% of 2016 consensus.vDeployments[Consensus::DEPLOYMENT_COINBASE_ASSETS].nOverrideMinerConfirmationWindow = 2016; - + consensus.vDeployments[Consensus::DEPLOYMENT_P2SH_ASSETS].bit = 11; + consensus.vDeployments[Consensus::DEPLOYMENT_P2SH_ASSETS].nStartTime = 1682956800; // UTC: Mon Mai 01 2023 18:00:00 + consensus.vDeployments[Consensus::DEPLOYMENT_P2SH_ASSETS].nTimeout = 1714579200; // UTC: Wed Mai 01 2024 18:00:00 + consensus.vDeployments[Consensus::DEPLOYMENT_P2SH_ASSETS].nOverrideRuleChangeActivationThreshold = 2016; // 100% required, hardly happens +// consensus.vDeployments[Consensus::DEPLOYMENT_P2SH_ASSETS].nOverrideRuleChangeActivationThreshold = 1411; // Approx 70% of 2016 + consensus.vDeployments[Consensus::DEPLOYMENT_P2SH_ASSETS].nOverrideMinerConfirmationWindow = 2016; + // The best chain should have at least this much work consensus.nMinimumChainWork = uint256S("000000000000000000000000000000000000000000000020d4ac871fb7009b63"); // Block 1186833 @@ -215,7 +221,8 @@ class CMainParams : public CChainParams { { 740000, uint256S("0x00000000000027d11bf1e7a3b57d3c89acc1722f39d6e08f23ac3a07e16e3172")}, { 909251, uint256S("0x000000000000694c9a363eff06518aa7399f00014ce667b9762f9a4e7a49f485")}, { 1040000, uint256S("0x000000000000138e2690b06b1ddd8cf158c3a5cf540ee5278debdcdffcf75839")}, - { 1186833, uint256S("0x0000000000000d4840d4de1f7d943542c2aed532bd5d6527274fc0142fa1a410")} + { 1186833, uint256S("0x0000000000000d4840d4de1f7d943542c2aed532bd5d6527274fc0142fa1a410")}, + { 1610000, uint256S("0x000000000001f1a67604ace3320cf722039f1b706b46a4d95e1d8502729b3046")} } }; @@ -323,6 +330,11 @@ class CTestNetParams : public CChainParams { consensus.vDeployments[Consensus::DEPLOYMENT_COINBASE_ASSETS].nTimeout = 1628877600; // UTC: Fri Aug 13 2021 18:00:00 consensus.vDeployments[Consensus::DEPLOYMENT_COINBASE_ASSETS].nOverrideRuleChangeActivationThreshold = 1411; // Approx 70% of 2016 consensus.vDeployments[Consensus::DEPLOYMENT_COINBASE_ASSETS].nOverrideMinerConfirmationWindow = 2016; + consensus.vDeployments[Consensus::DEPLOYMENT_P2SH_ASSETS].bit = 11; + consensus.vDeployments[Consensus::DEPLOYMENT_P2SH_ASSETS].nStartTime = 1619971200; // UTC: Sun May 02 2021 18:00:00 + consensus.vDeployments[Consensus::DEPLOYMENT_P2SH_ASSETS].nTimeout = 1651507200; // UTC: Mon May 02 2022 18:00:00 + consensus.vDeployments[Consensus::DEPLOYMENT_P2SH_ASSETS].nOverrideRuleChangeActivationThreshold = 1411; // Approx 70% of 2016 + consensus.vDeployments[Consensus::DEPLOYMENT_P2SH_ASSETS].nOverrideMinerConfirmationWindow = 2016; // The best chain should have at least this much work. consensus.nMinimumChainWork = uint256S("0x000000000000000000000000000000000000000000000000000168050db560b4"); @@ -436,7 +448,8 @@ class CTestNetParams : public CChainParams { { 225, uint256S("0x000003465e3e0167322eb8269ce91246bbc211e293bc5fbf6f0a0d12c1ccb363")}, {223408, uint256S("0x000000012a0c09dd6456ab19018cc458648dec762b04f4ddf8ef8108eae69db9")}, {232980, uint256S("0x000000007b16ae547fce76c3308dbeec2090cde75de74ab5dfcd6f60d13f089b")}, - {257610, uint256S("0x000000006272208605c4df3b54d4d5515759105e7ffcb258e8cd8077924ffef1")} + {257610, uint256S("0x000000006272208605c4df3b54d4d5515759105e7ffcb258e8cd8077924ffef1")}, + {587000, uint256S("0x000000040ca00d88c1c3be8fb298365304a6fa13af95b8fa2a689ad6736e37f6")} } }; @@ -531,8 +544,8 @@ class CRegTestParams : public CChainParams { consensus.vDeployments[Consensus::DEPLOYMENT_TRANSFER_SCRIPT_SIZE].bit = 8; consensus.vDeployments[Consensus::DEPLOYMENT_TRANSFER_SCRIPT_SIZE].nStartTime = 0; consensus.vDeployments[Consensus::DEPLOYMENT_TRANSFER_SCRIPT_SIZE].nTimeout = 999999999999ULL; - consensus.vDeployments[Consensus::DEPLOYMENT_TRANSFER_SCRIPT_SIZE].nOverrideRuleChangeActivationThreshold = 208; - consensus.vDeployments[Consensus::DEPLOYMENT_TRANSFER_SCRIPT_SIZE].nOverrideMinerConfirmationWindow = 288; + consensus.vDeployments[Consensus::DEPLOYMENT_TRANSFER_SCRIPT_SIZE].nOverrideRuleChangeActivationThreshold = 108; + consensus.vDeployments[Consensus::DEPLOYMENT_TRANSFER_SCRIPT_SIZE].nOverrideMinerConfirmationWindow = 144; consensus.vDeployments[Consensus::DEPLOYMENT_ENFORCE_VALUE].bit = 9; consensus.vDeployments[Consensus::DEPLOYMENT_ENFORCE_VALUE].nStartTime = 0; consensus.vDeployments[Consensus::DEPLOYMENT_ENFORCE_VALUE].nTimeout = 999999999999ULL; @@ -543,6 +556,11 @@ class CRegTestParams : public CChainParams { consensus.vDeployments[Consensus::DEPLOYMENT_COINBASE_ASSETS].nTimeout = 999999999999ULL; consensus.vDeployments[Consensus::DEPLOYMENT_COINBASE_ASSETS].nOverrideRuleChangeActivationThreshold = 400; consensus.vDeployments[Consensus::DEPLOYMENT_COINBASE_ASSETS].nOverrideMinerConfirmationWindow = 500; + consensus.vDeployments[Consensus::DEPLOYMENT_P2SH_ASSETS].bit = 11; + consensus.vDeployments[Consensus::DEPLOYMENT_P2SH_ASSETS].nStartTime = 0; + consensus.vDeployments[Consensus::DEPLOYMENT_P2SH_ASSETS].nTimeout = 999999999999ULL; + consensus.vDeployments[Consensus::DEPLOYMENT_P2SH_ASSETS].nOverrideRuleChangeActivationThreshold = 108; + consensus.vDeployments[Consensus::DEPLOYMENT_P2SH_ASSETS].nOverrideMinerConfirmationWindow = 144; // The best chain should have at least this much work. consensus.nMinimumChainWork = uint256S("0x00"); diff --git a/src/consensus/params.h b/src/consensus/params.h index 6b6a738c2a..0589d5b0a0 100644 --- a/src/consensus/params.h +++ b/src/consensus/params.h @@ -21,6 +21,7 @@ enum DeploymentPos DEPLOYMENT_TRANSFER_SCRIPT_SIZE, DEPLOYMENT_ENFORCE_VALUE, DEPLOYMENT_COINBASE_ASSETS, + DEPLOYMENT_P2SH_ASSETS, // DEPLOYMENT_CSV, // Deployment of BIP68, BIP112, and BIP113. // DEPLOYMENT_SEGWIT, // Deployment of BIP141, BIP143, and BIP147. // NOTE: Also add new deployments to VersionBitsDeploymentInfo in versionbits.cpp diff --git a/src/consensus/tx_verify.cpp b/src/consensus/tx_verify.cpp index f79e2c875e..add8eed29a 100644 --- a/src/consensus/tx_verify.cpp +++ b/src/consensus/tx_verify.cpp @@ -655,10 +655,16 @@ bool Consensus::CheckTxAssets(const CTransaction& tx, CValidationState& state, c i++; bool fIsAsset = false; int nType = 0; + int nScriptType = 0; bool fIsOwner = false; - if (txout.scriptPubKey.IsAssetScript(nType, fIsOwner)) + if (txout.scriptPubKey.IsAssetScript(nType, nScriptType, fIsOwner)) fIsAsset = true; + if (fIsAsset && nScriptType == TX_SCRIPTHASH) { + if (!AreP2SHAssetsAllowed()) + return state.DoS(0, false, REJECT_INVALID, "bad-txns-p2sh-assets-not-active"); + } + if (assetCache) { if (fIsAsset && !AreAssetsDeployed()) return state.DoS(100, false, REJECT_INVALID, "bad-txns-is-asset-and-asset-not-active"); @@ -687,7 +693,8 @@ bool Consensus::CheckTxAssets(const CTransaction& tx, CValidationState& state, c CAssetTransfer transfer; std::string address = ""; if (!TransferAssetFromScript(txout.scriptPubKey, transfer, address)) - return state.DoS(100, false, REJECT_INVALID, "bad-tx-asset-transfer-bad-deserialize", false, "", tx.GetHash()); + return state.DoS(100, false, REJECT_INVALID, "bad-tx-asset-transfer-bad-deserialize", false, "", + tx.GetHash()); if (!ContextualCheckTransferAsset(assetCache, transfer, address, strError)) return state.DoS(100, false, REJECT_INVALID, strError, false, "", tx.GetHash()); @@ -828,8 +835,9 @@ bool Consensus::CheckTxAssets(const CTransaction& tx, CValidationState& state, c } else { for (auto out : tx.vout) { int nType; + int nScriptType; bool _isOwner; - if (out.scriptPubKey.IsAssetScript(nType, _isOwner)) { + if (out.scriptPubKey.IsAssetScript(nType, nScriptType, _isOwner)) { if (nType != TX_TRANSFER_ASSET) { return state.DoS(100, false, REJECT_INVALID, "bad-txns-bad-asset-transaction", false, "", tx.GetHash()); } diff --git a/src/core_write.cpp b/src/core_write.cpp index 189d96718f..0be766ffa8 100644 --- a/src/core_write.cpp +++ b/src/core_write.cpp @@ -159,6 +159,7 @@ void ScriptPubKeyToUniv(const CScript& scriptPubKey, UniValue& out, bool fIncludeHex) { txnouttype type; + txnouttype scriptType; std::vector addresses; int nRequired; @@ -166,7 +167,7 @@ void ScriptPubKeyToUniv(const CScript& scriptPubKey, if (fIncludeHex) out.pushKV("hex", HexStr(scriptPubKey.begin(), scriptPubKey.end())); - if (!ExtractDestinations(scriptPubKey, type, addresses, nRequired)) { + if (!ExtractDestinations(scriptPubKey, type, scriptType, addresses, nRequired)) { out.pushKV("type", GetTxnOutputType(type)); return; } diff --git a/src/policy/policy.cpp b/src/policy/policy.cpp index e4df304922..e9826887bb 100644 --- a/src/policy/policy.cpp +++ b/src/policy/policy.cpp @@ -59,9 +59,9 @@ bool IsDust(const CTxOut& txout, const CFeeRate& dustRelayFeeIn) return (txout.nValue < GetDustThreshold(txout, dustRelayFeeIn)); } -bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType, const bool witnessEnabled) { +bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType, txnouttype& scriptType, const bool witnessEnabled) { std::vector > vSolutions; - if (!Solver(scriptPubKey, whichType, vSolutions)) + if (!Solver(scriptPubKey, whichType, scriptType, vSolutions)) return false; @@ -123,8 +123,9 @@ bool IsStandardTx(const CTransaction& tx, std::string& reason, const bool witnes unsigned int nDataOut = 0; unsigned int nAssetDataOut = 0; txnouttype whichType; + txnouttype scriptType; for (const CTxOut& txout : tx.vout) { - if (!::IsStandard(txout.scriptPubKey, whichType, witnessEnabled)) { + if (!::IsStandard(txout.scriptPubKey, whichType, scriptType, witnessEnabled)) { reason = "scriptpubkey"; return false; } @@ -184,12 +185,13 @@ bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs) std::vector > vSolutions; txnouttype whichType; + txnouttype scriptType; // get the scriptPubKey corresponding to this input: const CScript& prevScript = prev.scriptPubKey; - if (!Solver(prevScript, whichType, vSolutions)) + if (!Solver(prevScript, whichType, scriptType, vSolutions)) return false; - if (whichType == TX_SCRIPTHASH) + if (whichType == TX_SCRIPTHASH || scriptType == TX_SCRIPTHASH) { std::vector > stack; // convert the scriptSig into a stack, so we can inspect the redeemScript diff --git a/src/policy/policy.h b/src/policy/policy.h index 371400181f..2058fc2eda 100644 --- a/src/policy/policy.h +++ b/src/policy/policy.h @@ -78,7 +78,7 @@ CAmount GetDustThreshold(const CTxOut& txout, const CFeeRate& dustRelayFee); bool IsDust(const CTxOut& txout, const CFeeRate& dustRelayFee); -bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType, const bool witnessEnabled = false); +bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType, txnouttype& scriptType, const bool witnessEnabled = false); /** * Check for standard transaction types * @return True if all outputs (scriptPubKeys) use only standard transaction forms diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 53dce9c5ba..2e21c3394b 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1504,6 +1504,7 @@ UniValue getblockchaininfo(const JSONRPCRequest& request) BIP9SoftForkDescPushBack(bip9_softforks, "transfer_script", consensusParams, Consensus::DEPLOYMENT_TRANSFER_SCRIPT_SIZE); BIP9SoftForkDescPushBack(bip9_softforks, "enforce", consensusParams, Consensus::DEPLOYMENT_ENFORCE_VALUE); BIP9SoftForkDescPushBack(bip9_softforks, "coinbase", consensusParams, Consensus::DEPLOYMENT_COINBASE_ASSETS); + BIP9SoftForkDescPushBack(bip9_softforks, "p2sh_assets", consensusParams, Consensus::DEPLOYMENT_P2SH_ASSETS); obj.push_back(Pair("softforks", softforks)); obj.push_back(Pair("bip9_softforks", bip9_softforks)); diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp index 5ce38013ac..7d573d7b2b 100644 --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -150,8 +150,9 @@ class DescribeAddressVisitor : public boost::static_visitor if (pwallet && pwallet->GetCScript(scriptID, subscript)) { std::vector addresses; txnouttype whichType; + txnouttype scriptType; int nRequired; - ExtractDestinations(subscript, whichType, addresses, nRequired); + ExtractDestinations(subscript, whichType, scriptType, addresses, nRequired); obj.push_back(Pair("script", GetTxnOutputType(whichType))); obj.push_back(Pair("hex", HexStr(subscript.begin(), subscript.end()))); UniValue a(UniValue::VARR); diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 986a326980..f63658a306 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -1955,7 +1955,7 @@ UniValue signrawtransaction(const JSONRPCRequest& request) // if redeemScript given and not using the local wallet (private keys // given), add redeemScript to the tempKeystore so it can be signed: - if (fGivenKeys && (scriptPubKey.IsPayToScriptHash() || scriptPubKey.IsPayToWitnessScriptHash())) { + if (fGivenKeys && (scriptPubKey.IsPayToScriptHash() || scriptPubKey.IsPayToWitnessScriptHash() || scriptPubKey.IsP2SHAssetScript())) { RPCTypeCheckObj(prevOut, { {"txid", UniValueType(UniValue::VSTR)}, diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index e1620996d3..8fa5778360 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -297,6 +297,7 @@ bool EvalScript(std::vector > &stack, const CScript & CScript::const_iterator pc = script.begin(); CScript::const_iterator pend = script.end(); + CScript::const_iterator pbegincodehash = script.begin(); opcodetype opcode; valtype vchPushValue; @@ -1602,7 +1603,7 @@ bool VerifyScript(const CScript &scriptSig, const CScript &scriptPubKey, const C } // Additional validation for spend-to-script-hash transactions: - if ((flags & SCRIPT_VERIFY_P2SH) && scriptPubKey.IsPayToScriptHash()) + if ((flags & SCRIPT_VERIFY_P2SH) && (scriptPubKey.IsPayToScriptHash() || scriptPubKey.IsP2SHAssetScript())) { // scriptSig must be literals-only or validation fails if (!scriptSig.IsPushOnly()) diff --git a/src/script/ismine.cpp b/src/script/ismine.cpp index c6c0c15b09..c9989146b5 100644 --- a/src/script/ismine.cpp +++ b/src/script/ismine.cpp @@ -53,7 +53,8 @@ isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey, bool& std::vector vSolutions; txnouttype whichType; - if (!Solver(scriptPubKey, whichType, vSolutions)) { + txnouttype scriptType; + if (!Solver(scriptPubKey, whichType, scriptType, vSolutions)) { if (keystore.HaveWatchOnly(scriptPubKey)) return ISMINE_WATCH_UNSOLVABLE; return ISMINE_NO; @@ -145,52 +146,32 @@ isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey, bool& break; } /** RVN START */ - case TX_NEW_ASSET: { + case TX_NEW_ASSET: + case TX_TRANSFER_ASSET: + case TX_REISSUE_ASSET: { if (!AreAssetsDeployed()) return ISMINE_NO; - keyID = CKeyID(uint160(vSolutions[0])); - if (sigversion != SIGVERSION_BASE) { - CPubKey pubkey; - if (keystore.GetPubKey(keyID, pubkey) && !pubkey.IsCompressed()) { - isInvalid = true; - return ISMINE_NO; - } - } - if (keystore.HaveKey(keyID)) - return ISMINE_SPENDABLE; - break; - - } - case TX_TRANSFER_ASSET: { - if (!AreAssetsDeployed()) - return ISMINE_NO; - keyID = CKeyID(uint160(vSolutions[0])); - if (sigversion != SIGVERSION_BASE) { - CPubKey pubkey; - if (keystore.GetPubKey(keyID, pubkey) && !pubkey.IsCompressed()) { - isInvalid = true; - return ISMINE_NO; + if (scriptType == TX_PUBKEYHASH) { + keyID = CKeyID(uint160(vSolutions[0])); + if (sigversion != SIGVERSION_BASE) { + CPubKey pubkey; + if (keystore.GetPubKey(keyID, pubkey) && !pubkey.IsCompressed()) { + isInvalid = true; + return ISMINE_NO; + } } - } - if (keystore.HaveKey(keyID)) - return ISMINE_SPENDABLE; - break; - } - - case TX_REISSUE_ASSET: { - if (!AreAssetsDeployed()) - return ISMINE_NO; - keyID = CKeyID(uint160(vSolutions[0])); - if (sigversion != SIGVERSION_BASE) { - CPubKey pubkey; - if (keystore.GetPubKey(keyID, pubkey) && !pubkey.IsCompressed()) { - isInvalid = true; - return ISMINE_NO; + if (keystore.HaveKey(keyID)) + return ISMINE_SPENDABLE; + } else if (scriptType == TX_SCRIPTHASH) { + CScriptID scriptID = CScriptID(uint160(vSolutions[0])); + CScript subscript; + if (keystore.GetCScript(scriptID, subscript)) { + isminetype ret = IsMine(keystore, subscript, isInvalid); + if (ret == ISMINE_SPENDABLE || ret == ISMINE_WATCH_SOLVABLE || (ret == ISMINE_NO && isInvalid)) + return ret; } } - if (keystore.HaveKey(keyID)) - return ISMINE_SPENDABLE; break; } /** RVN END*/ diff --git a/src/script/script.cpp b/src/script/script.cpp index 2e154834ff..0b75e4c909 100644 --- a/src/script/script.cpp +++ b/src/script/script.cpp @@ -231,56 +231,84 @@ bool CScript::IsPayToScriptHash() const bool CScript::IsAssetScript() const { int nType = 0; + int nScriptType = 0; bool isOwner = false; int start = 0; - return IsAssetScript(nType, isOwner, start); + return IsAssetScript(nType, nScriptType, isOwner, start); } -bool CScript::IsAssetScript(int& nType, bool& isOwner) const +bool CScript::IsAssetScript(int& nType, bool& fIsOwner) const { int start = 0; - return IsAssetScript(nType, isOwner, start); + int nScriptType = 0; + return IsAssetScript(nType, nScriptType, fIsOwner, start); } -bool CScript::IsAssetScript(int& nType, bool& fIsOwner, int& nStartingIndex) const +bool CScript::IsAssetScript(int& nType, int& nScriptType, bool& isOwner) const +{ + int start = 0; + return IsAssetScript(nType, nScriptType, isOwner, start); +} + +bool CScript::IsAssetScript(int& nType, int& nScriptType, bool& fIsOwner, int& nStartingIndex) const { if (this->size() > 31) { - if ((*this)[25] == OP_RVN_ASSET) { // OP_RVN_ASSET is always in the 25 index of the script if it exists - int index = -1; - if ((*this)[27] == RVN_R) { // Check to see if RVN starts at 27 ( this->size() < 105) - if ((*this)[28] == RVN_V) - if ((*this)[29] == RVN_N) - index = 30; - } else { - if ((*this)[28] == RVN_R) // Check to see if RVN starts at 28 ( this->size() >= 105) - if ((*this)[29] == RVN_V) - if ((*this)[30] == RVN_N) - index = 31; - } + // Extra-fast test for pay-to-script-hash CScripts: + if ( (*this)[0] == OP_HASH160 && (*this)[1] == 0x14 && (*this)[22] == OP_EQUAL) { + + // If this is of the P2SH type, we need to return this type so we know how to interact and solve it + nScriptType = TX_SCRIPTHASH; + } else { + // If this is of the P2PKH type, we need to return this type so we know how to interact and solve it + nScriptType = TX_PUBKEYHASH; + } + + // Initialize the index + int index = -1; - if (index > 0) { - nStartingIndex = index + 1; // Set the index where the asset data begins. Use to serialize the asset data into asset objects - if ((*this)[index] == RVN_T) { // Transfer first anticipating more transfers than other assets operations - nType = TX_TRANSFER_ASSET; - return true; - } else if ((*this)[index] == RVN_Q && this->size() > 39) { - nType = TX_NEW_ASSET; - fIsOwner = false; - return true; - } else if ((*this)[index] == RVN_O) { - nType = TX_NEW_ASSET; - fIsOwner = true; - return true; - } else if ((*this)[index] == RVN_R) { - nType = TX_REISSUE_ASSET; - return true; - } + // OP_RVN_ASSET is always in the 23 index of the P2SH script if it exists + if (nScriptType == TX_SCRIPTHASH && (*this)[23] == OP_RVN_ASSET) { + // We have a potential asset interacting with a P2SH + index = SearchForRVN(*this, 25); + + } + else if ((*this)[25] == OP_RVN_ASSET) { // OP_RVN_ASSET is always in the 25 index of the P2PKH script if it exists + // We have a potential asset interacting with a P2PKH + index = SearchForRVN(*this, 27); + } + + if (index > 0) { + nStartingIndex = index + 1; // Set the index where the asset data begins. Use to serialize the asset data into asset objects + if ((*this)[index] == RVN_T) { // Transfer first anticipating more transfers than other assets operations + nType = TX_TRANSFER_ASSET; + return true; + } else if ((*this)[index] == RVN_Q && this->size() > 39) { + nType = TX_NEW_ASSET; + fIsOwner = false; + return true; + } else if ((*this)[index] == RVN_O) { + nType = TX_NEW_ASSET; + fIsOwner = true; + return true; + } else if ((*this)[index] == RVN_R) { + nType = TX_REISSUE_ASSET; + return true; } } } + return false; } +bool CScript::IsP2SHAssetScript() const +{ + int nType = 0; + int nScriptType = 0; + bool isOwner = false; + IsAssetScript(nType, nScriptType, isOwner); + return nScriptType == TX_SCRIPTHASH; +} + bool CScript::IsNewAsset() const { @@ -465,8 +493,9 @@ bool GetAssetAmountFromScript(const CScript& script, CAmount& nAmount) std::string assetName = ""; int nType = 0; + int nScriptType = 0; bool fIsOwner = false; - if (!script.IsAssetScript(nType, fIsOwner)) { + if (!script.IsAssetScript(nType, nScriptType, fIsOwner)) { return false; } @@ -496,8 +525,9 @@ bool GetAssetAmountFromScript(const CScript& script, CAmount& nAmount) bool ScriptNewAsset(const CScript& scriptPubKey, int& nStartingIndex) { int nType = 0; - bool fIsOwner =false; - if (scriptPubKey.IsAssetScript(nType, fIsOwner, nStartingIndex)) { + int nScriptType = 0; + bool fIsOwner = false; + if (scriptPubKey.IsAssetScript(nType, nScriptType, fIsOwner, nStartingIndex)) { return nType == TX_NEW_ASSET && !fIsOwner; } @@ -507,8 +537,9 @@ bool ScriptNewAsset(const CScript& scriptPubKey, int& nStartingIndex) bool ScriptTransferAsset(const CScript& scriptPubKey, int& nStartingIndex) { int nType = 0; - bool fIsOwner =false; - if (scriptPubKey.IsAssetScript(nType, fIsOwner, nStartingIndex)) { + int nScriptType = 0; + bool fIsOwner = false; + if (scriptPubKey.IsAssetScript(nType, nScriptType, fIsOwner, nStartingIndex)) { return nType == TX_TRANSFER_ASSET; } @@ -518,8 +549,9 @@ bool ScriptTransferAsset(const CScript& scriptPubKey, int& nStartingIndex) bool ScriptReissueAsset(const CScript& scriptPubKey, int& nStartingIndex) { int nType = 0; - bool fIsOwner =false; - if (scriptPubKey.IsAssetScript(nType, fIsOwner, nStartingIndex)) { + int nScriptType = 0; + bool fIsOwner = false; + if (scriptPubKey.IsAssetScript(nType, nScriptType, fIsOwner, nStartingIndex)) { return nType == TX_REISSUE_ASSET; } @@ -592,6 +624,27 @@ bool AmountFromReissueScript(const CScript& scriptPubKey, CAmount& nAmount) nAmount = asset.nAmount; return true; } + +int SearchForRVN(const CScript& script, const int startingValue) { + + // Initialize the start value + int index = -1; + + // Search for RVN at the two places in the script it can be depending on the size of the script + if (script[startingValue] == RVN_R) { // Check to see if RVN starts at the starting value ( this->size() < 105) + if (script[startingValue + 1] == RVN_V) + if (script[startingValue + 2] == RVN_N) + index = startingValue + 3; + } else { + if (script[startingValue + 1] == RVN_R) // Check to see if RVN starts at starting value + 1 ( this->size() >= 105) + if (script[startingValue + 2] == RVN_V) + if (script[startingValue + 3] == RVN_N) + index = startingValue + 4; + } + + return index; + +} //!--------------------------------------------------------------------------------------------------------------------------!// diff --git a/src/script/script.h b/src/script/script.h index 744b993645..1481f7c8b5 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -664,9 +664,11 @@ class CScript : public CScriptBase /** RVN START */ enum class txnouttype; - bool IsAssetScript(int& nType, bool& fIsOwner, int& nStartingIndex) const; - bool IsAssetScript(int& nType, bool& fIsOwner) const; bool IsAssetScript() const; + bool IsAssetScript(int& nType, bool& fIsOwner) const; + bool IsAssetScript(int& nType, int& nScriptType, bool& fIsOwner) const; + bool IsAssetScript(int& nTXType, int& nScriptType, bool& fIsOwner, int& nStartingIndex) const; + bool IsP2SHAssetScript() const; bool IsNewAsset() const; bool IsOwnerAsset() const; bool IsReissueAsset() const; @@ -739,4 +741,6 @@ bool ScriptNewAsset(const CScript& scriptPubKey, int& nStartingIndex); bool ScriptTransferAsset(const CScript& scriptPubKey, int& nStartingIndex); bool ScriptReissueAsset(const CScript& scriptPubKey, int& nStartingIndex); +int SearchForRVN(const CScript& script, const int startingValue); + #endif // RAVEN_SCRIPT_SCRIPT_H diff --git a/src/script/sign.cpp b/src/script/sign.cpp index 7a76302993..8414f3f1d5 100644 --- a/src/script/sign.cpp +++ b/src/script/sign.cpp @@ -65,14 +65,14 @@ static bool SignN(const std::vector& multisigdata, const BaseSignatureC * Returns false if scriptPubKey could not be completely satisfied. */ static bool SignStep(const BaseSignatureCreator& creator, const CScript& scriptPubKey, - std::vector& ret, txnouttype& whichTypeRet, SigVersion sigversion) + std::vector& ret, txnouttype& whichTypeRet, txnouttype& whichScriptTypeRet, SigVersion sigversion) { CScript scriptRet; uint160 h160; ret.clear(); std::vector vSolutions; - if (!Solver(scriptPubKey, whichTypeRet, vSolutions)) + if (!Solver(scriptPubKey, whichTypeRet, whichScriptTypeRet, vSolutions)) return false; CKeyID keyID; @@ -85,39 +85,27 @@ static bool SignStep(const BaseSignatureCreator& creator, const CScript& scriptP return false; /** RVN START */ case TX_NEW_ASSET: - keyID = CKeyID(uint160(vSolutions[0])); - if (!Sign1(keyID, creator, scriptPubKey, ret, sigversion)) - return false; - else - { - CPubKey vch; - creator.KeyStore().GetPubKey(keyID, vch); - ret.push_back(ToByteVector(vch)); - } - return true; case TX_TRANSFER_ASSET: - keyID = CKeyID(uint160(vSolutions[0])); - if (!Sign1(keyID, creator, scriptPubKey, ret, sigversion)) + case TX_REISSUE_ASSET: { + if (whichScriptTypeRet == TX_PUBKEYHASH) { + keyID = CKeyID(uint160(vSolutions[0])); + if (!Sign1(keyID, creator, scriptPubKey, ret, sigversion)) + return false; + else { + CPubKey vch; + creator.KeyStore().GetPubKey(keyID, vch); + ret.push_back(ToByteVector(vch)); + } + return true; + } else if (whichScriptTypeRet == TX_SCRIPTHASH) { + if (creator.KeyStore().GetCScript(uint160(vSolutions[0]), scriptRet)) { + ret.push_back(std::vector(scriptRet.begin(), scriptRet.end())); + return true; + } return false; - else - { - CPubKey vch; - creator.KeyStore().GetPubKey(keyID, vch); - ret.push_back(ToByteVector(vch)); } - return true; + } - case TX_REISSUE_ASSET: - keyID = CKeyID(uint160(vSolutions[0])); - if (!Sign1(keyID, creator, scriptPubKey, ret, sigversion)) - return false; - else - { - CPubKey vch; - creator.KeyStore().GetPubKey(keyID, vch); - ret.push_back(ToByteVector(vch)); - } - return true; /** RVN END */ case TX_PUBKEY: keyID = CPubKey(vSolutions[0]).GetID(); @@ -179,20 +167,39 @@ static CScript PushAll(const std::vector& values) bool ProduceSignature(const BaseSignatureCreator& creator, const CScript& fromPubKey, SignatureData& sigdata) { CScript script = fromPubKey; + CScript modifiedScript = fromPubKey; + + // If this is a P2SH Asset Script, grab the P2SH section of the script + if(fromPubKey.IsP2SHAssetScript()) { + modifiedScript = CScript(fromPubKey.begin(), fromPubKey.begin() + 23); + script = modifiedScript; + } + std::vector result; txnouttype whichType; - bool solved = SignStep(creator, script, result, whichType, SIGVERSION_BASE); + txnouttype whichScriptType; + bool solved = SignStep(creator, script, result, whichType, whichScriptType, SIGVERSION_BASE); bool P2SH = false; CScript subscript; sigdata.scriptWitness.stack.clear(); - if (solved && whichType == TX_SCRIPTHASH) + if (solved && whichType == TX_SCRIPTHASH ) { // Solver returns the subscript that needs to be evaluated; // the final scriptSig is the signatures from that // and then the serialized subscript: script = subscript = CScript(result[0].begin(), result[0].end()); - solved = solved && SignStep(creator, script, result, whichType, SIGVERSION_BASE) && whichType != TX_SCRIPTHASH; + solved = solved && SignStep(creator, script, result, whichType, whichScriptType, SIGVERSION_BASE) && whichType != TX_SCRIPTHASH; + P2SH = true; + } + + if (solved && whichScriptType == TX_SCRIPTHASH ) + { + // Solver returns the subscript that needs to be evaluated; + // the final scriptSig is the signatures from that + // and then the serialized subscript: + script = subscript = CScript(result[0].begin(), result[0].end()); + solved = solved && SignStep(creator, script, result, whichType, whichScriptType, SIGVERSION_BASE) && whichType != TX_SCRIPTHASH; P2SH = true; } @@ -201,7 +208,7 @@ bool ProduceSignature(const BaseSignatureCreator& creator, const CScript& fromPu CScript witnessscript; witnessscript << OP_DUP << OP_HASH160 << ToByteVector(result[0]) << OP_EQUALVERIFY << OP_CHECKSIG; txnouttype subType; - solved = solved && SignStep(creator, witnessscript, result, subType, SIGVERSION_WITNESS_V0); + solved = solved && SignStep(creator, witnessscript, result, subType, whichScriptType, SIGVERSION_WITNESS_V0); sigdata.scriptWitness.stack = result; result.clear(); } @@ -209,7 +216,7 @@ bool ProduceSignature(const BaseSignatureCreator& creator, const CScript& fromPu { CScript witnessscript(result[0].begin(), result[0].end()); txnouttype subType; - solved = solved && SignStep(creator, witnessscript, result, subType, SIGVERSION_WITNESS_V0) && subType != TX_SCRIPTHASH && subType != TX_WITNESS_V0_SCRIPTHASH && subType != TX_WITNESS_V0_KEYHASH; + solved = solved && SignStep(creator, witnessscript, result, subType, whichScriptType, SIGVERSION_WITNESS_V0) && subType != TX_SCRIPTHASH && subType != TX_WITNESS_V0_SCRIPTHASH && subType != TX_WITNESS_V0_KEYHASH; result.push_back(std::vector(witnessscript.begin(), witnessscript.end())); sigdata.scriptWitness.stack = result; result.clear(); @@ -221,7 +228,7 @@ bool ProduceSignature(const BaseSignatureCreator& creator, const CScript& fromPu sigdata.scriptSig = PushAll(result); // Test solution - return solved && VerifyScript(sigdata.scriptSig, fromPubKey, &sigdata.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS, creator.Checker()); + return solved && VerifyScript(sigdata.scriptSig, modifiedScript, &sigdata.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS, creator.Checker()); } SignatureData DataFromTransaction(const CMutableTransaction& tx, unsigned int nIn) @@ -380,8 +387,9 @@ static Stacks CombineSignatures(const CScript& scriptPubKey, const BaseSignature CScript pubKey2(spk.begin(), spk.end()); txnouttype txType2; + txnouttype scriptType2; std::vector > vSolutions2; - Solver(pubKey2, txType2, vSolutions2); + Solver(pubKey2, txType2, scriptType2, vSolutions2); sigs1.script.pop_back(); sigs2.script.pop_back(); Stacks result = CombineSignatures(pubKey2, checker, txType2, vSolutions2, sigs1, sigs2, sigversion); @@ -400,8 +408,9 @@ static Stacks CombineSignatures(const CScript& scriptPubKey, const BaseSignature // Recur to combine: CScript pubKey2(sigs1.witness.back().begin(), sigs1.witness.back().end()); txnouttype txType2; + txnouttype scriptType2; std::vector vSolutions2; - Solver(pubKey2, txType2, vSolutions2); + Solver(pubKey2, txType2, scriptType2, vSolutions2); sigs1.witness.pop_back(); sigs1.script = sigs1.witness; sigs1.witness.clear(); @@ -439,10 +448,19 @@ SignatureData CombineSignatures(const CScript& scriptPubKey, const BaseSignature const SignatureData& scriptSig1, const SignatureData& scriptSig2) { txnouttype txType; + txnouttype scriptType; std::vector > vSolutions; - Solver(scriptPubKey, txType, vSolutions); - return CombineSignatures(scriptPubKey, checker, txType, vSolutions, Stacks(scriptSig1), Stacks(scriptSig2), SIGVERSION_BASE).Output(); + CScript modifiedScript = scriptPubKey; + + // If this is a P2SH Asset Script, grab the P2SH section of the script + if(scriptPubKey.IsP2SHAssetScript()) { + modifiedScript = CScript(scriptPubKey.begin(), scriptPubKey.begin() + 23); + } + + Solver(modifiedScript, txType, scriptType, vSolutions); + + return CombineSignatures(modifiedScript, checker, txType, vSolutions, Stacks(scriptSig1), Stacks(scriptSig2), SIGVERSION_BASE).Output(); } namespace { diff --git a/src/script/standard.cpp b/src/script/standard.cpp index fd2a15a194..78ea29e0d3 100644 --- a/src/script/standard.cpp +++ b/src/script/standard.cpp @@ -44,7 +44,7 @@ const char* GetTxnOutputType(txnouttype t) return nullptr; } -bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector >& vSolutionsRet) +bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, txnouttype& scriptTypeRet, std::vector >& vSolutionsRet) { // Templates static std::multimap mTemplates; @@ -73,12 +73,22 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector hashBytes(scriptPubKey.begin()+3, scriptPubKey.begin()+23); - vSolutionsRet.push_back(hashBytes); - return true; + scriptTypeRet = (txnouttype)nScriptType; + + if (scriptTypeRet == TX_SCRIPTHASH) { + std::vector hashBytes(scriptPubKey.begin()+2, scriptPubKey.begin()+22); + vSolutionsRet.push_back(hashBytes); + return true; + } else if (scriptTypeRet == TX_PUBKEYHASH) { + std::vector hashBytes(scriptPubKey.begin()+3, scriptPubKey.begin()+23); + vSolutionsRet.push_back(hashBytes); + return true; + } + return false; } /** RVN END */ @@ -149,6 +159,7 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector n || vSolutionsRet.size()-2 != n) return false; } + return true; } if (!script1.GetOp(pc1, opcode1, vch1)) @@ -211,7 +222,8 @@ bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet) { std::vector vSolutions; txnouttype whichType; - if (!Solver(scriptPubKey, whichType, vSolutions)) { + txnouttype scriptType; + if (!Solver(scriptPubKey, whichType, scriptType, vSolutions)) { return false; } @@ -235,7 +247,11 @@ bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet) return true; /** RVN START */ } else if (whichType == TX_NEW_ASSET || whichType == TX_REISSUE_ASSET || whichType == TX_TRANSFER_ASSET) { - addressRet = CKeyID(uint160(vSolutions[0])); + if (scriptType == TX_SCRIPTHASH) { + addressRet = CScriptID(uint160(vSolutions[0])); + } else { + addressRet = CKeyID(uint160(vSolutions[0])); + } return true; } else if (whichType == TX_RESTRICTED_ASSET_DATA) { if (vSolutions.size()) { @@ -248,12 +264,13 @@ bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet) return false; } -bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, std::vector& addressRet, int& nRequiredRet) +bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, txnouttype& scriptType, std::vector& addressRet, int& nRequiredRet) { addressRet.clear(); typeRet = TX_NONSTANDARD; + scriptType = TX_NONSTANDARD; std::vector vSolutions; - if (!Solver(scriptPubKey, typeRet, vSolutions)) + if (!Solver(scriptPubKey, typeRet, scriptType, vSolutions)) return false; if (typeRet == TX_NULL_DATA) { // This is data, not addresses @@ -381,8 +398,9 @@ CScript GetScriptForWitness(const CScript& redeemscript) CScript ret; txnouttype typ; + txnouttype scriptTyp; std::vector > vSolutions; - if (Solver(redeemscript, typ, vSolutions)) { + if (Solver(redeemscript, typ, scriptTyp, vSolutions)) { if (typ == TX_PUBKEY) { unsigned char h160[20]; CHash160().Write(&vSolutions[0][0], vSolutions[0].size()).Finalize(h160); diff --git a/src/script/standard.h b/src/script/standard.h index 21d8e872db..a9094c9ae1 100644 --- a/src/script/standard.h +++ b/src/script/standard.h @@ -105,7 +105,7 @@ const char* GetTxnOutputType(txnouttype t); * @param[out] vSolutionsRet Vector of parsed pubkeys and hashes * @return True if script matches standard template */ -bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector >& vSolutionsRet); +bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, txnouttype& scriptTypeRet, std::vector >& vSolutionsRet); /** * Parse a standard scriptPubKey for the destination address. Assigns result to @@ -123,7 +123,7 @@ bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet) * Returns true if successful. Currently does not extract address from * pay-to-witness scripts. */ -bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, std::vector& addressRet, int& nRequiredRet); +bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, txnouttype& scriptType, std::vector& addressRet, int& nRequiredRet); /** * Generate a Raven scriptPubKey for the given CTxDestination. Returns a P2PKH diff --git a/src/test/assets/asset_p2sh_tests.cpp b/src/test/assets/asset_p2sh_tests.cpp new file mode 100644 index 0000000000..fc6bbaafb1 --- /dev/null +++ b/src/test/assets/asset_p2sh_tests.cpp @@ -0,0 +1,59 @@ +// Copyright (c) 2017-2021 The Raven Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include + +#include + +#include + +#include +#include