diff --git a/src/validation.cpp b/src/validation.cpp index 62ed1fea63..75074503ff 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -68,6 +68,8 @@ #include #include #include +#include +#include #include #include @@ -154,6 +156,11 @@ bool CheckInputScripts(const CTransaction& tx, TxValidationState& state, std::vector* pvChecks = nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs_main); +int64_t FutureDrift(uint32_t nTime, int nHeight, const Consensus::Params& consensusParams) +{ + return nTime + consensusParams.StakeTimestampMask(nHeight); +} + bool CheckFinalTxAtTip(const CBlockIndex& active_chain_tip, const CTransaction& tx) { AssertLockHeld(cs_main); @@ -1654,6 +1661,23 @@ PackageMempoolAcceptResult ProcessNewPackage(Chainstate& active_chainstate, CTxM return result; } +bool IsConfirmedInNPrevBlocks(const CDiskTxPos& txindex, const CBlockIndex* pindexFrom, int nMaxDepth, int& nActualDepth) +{ + for (const CBlockIndex* pindex = pindexFrom; pindex && pindexFrom->nHeight - pindex->nHeight < nMaxDepth; pindex = pindex->pprev) + { + if (pindex->nDataPos == txindex.nPos && pindex->nFile == txindex.nFile) + { + nActualDepth = pindexFrom->nHeight - pindex->nHeight; + return true; + } + } + return false; +} + +bool CheckHeaderProof(const CBlockHeader& block, const Consensus::Params& consensusParams, Chainstate& chainstate){ + return {}; +} + bool CheckIndexProof(const CBlockIndex& block, const Consensus::Params& consensusParams) { return {}; @@ -2006,9 +2030,11 @@ int ApplyTxInUndo(Coin&& undo, CCoinsViewCache& view, const COutPoint& out) /** Undo the effects of this block (with given index) on the UTXO set represented by coins. * When FAILED is returned, view is left in an indeterminate state. */ -DisconnectResult Chainstate::DisconnectBlock(const CBlock& block, const CBlockIndex* pindex, CCoinsViewCache& view) +DisconnectResult Chainstate::DisconnectBlock(const CBlock& block, const CBlockIndex* pindex, CCoinsViewCache& view, bool* pfClean) { AssertLockHeld(::cs_main); + if (pfClean) + *pfClean = false; bool fClean = true; CBlockUndo blockUndo; @@ -2022,20 +2048,25 @@ DisconnectResult Chainstate::DisconnectBlock(const CBlock& block, const CBlockIn return DISCONNECT_FAILED; } + /////////////////////////////////////////////////////////// // qtum + std::vector > addressIndex; + std::vector > addressUnspentIndex; + /////////////////////////////////////////////////////////// + // Ignore blocks that contain transactions which are 'overwritten' by later transactions, // unless those are already completely spent. // See https://github.com/bitcoin/bitcoin/issues/22596 for additional information. // Note: the blocks specified here are different than the ones used in ConnectBlock because DisconnectBlock // unwinds the blocks in reverse. As a result, the inconsistency is not discovered until the earlier // blocks with the duplicate coinbase transactions are disconnected. - bool fEnforceBIP30 = !((pindex->nHeight==91722 && pindex->GetBlockHash() == uint256S("0x00000000000271a2dc26e7667f8419f2e15416dc6955e5a6c6cdf3f2574dd08e")) || - (pindex->nHeight==91812 && pindex->GetBlockHash() == uint256S("0x00000000000af0aed4792b1acee3d966af36cf5def14935db8de83d6f9306f2f"))); + bool fEnforceBIP30 = !IsBIP30Unspendable(*pindex); // undo transactions in reverse order for (int i = block.vtx.size() - 1; i >= 0; i--) { const CTransaction &tx = *(block.vtx[i]); uint256 hash = tx.GetHash(); bool is_coinbase = tx.IsCoinBase(); + bool is_coinstake = tx.IsCoinStake(); bool is_bip30_exception = (is_coinbase && !fEnforceBIP30); // Check that all outputs are available and match the outputs in the block itself @@ -2045,7 +2076,7 @@ DisconnectResult Chainstate::DisconnectBlock(const CBlock& block, const CBlockIn COutPoint out(hash, o); Coin coin; bool is_spent = view.SpendCoin(out, &coin); - if (!is_spent || tx.vout[o] != coin.out || pindex->nHeight != coin.nHeight || is_coinbase != coin.fCoinBase) { + if (!is_spent || tx.vout[o] != coin.out || pindex->nHeight != coin.nHeight || is_coinbase != coin.fCoinBase || is_coinstake != coin.fCoinStake) { if (!is_bip30_exception) { fClean = false; // transaction output mismatch } @@ -2156,6 +2187,17 @@ static unsigned int GetBlockScriptFlags(const CBlockIndex& block_index, const Ch return flags; } +unsigned int GetContractScriptFlags(int nHeight, const Consensus::Params& consensusparams) { + unsigned int flags = SCRIPT_EXEC_BYTE_CODE; + + // Start support sender address in contract output + if (nHeight >= consensusparams.QIP5Height) { + flags |= SCRIPT_OUTPUT_SENDER; + } + + return flags; +} + static SteadyClock::duration time_check{}; static SteadyClock::duration time_forks{}; @@ -2166,10 +2208,409 @@ static SteadyClock::duration time_index{}; static SteadyClock::duration time_total{}; static int64_t num_blocks_total = 0; +bool GetSpentCoinFromBlock(const CBlockIndex* pindex, COutPoint prevout, Coin* coin) { + return {}; +} + +bool GetSpentCoinFromMainChain(const CBlockIndex* pforkPrev, COutPoint prevoutStake, Coin* coin, CChain& chain) { + const CBlockIndex* pforkBase = chain.FindFork(pforkPrev); + return {}; +} + +bool CheckOpSender(const CTransaction& tx, const CChainParams& chainparams, int nHeight){ + if(!tx.HasOpSender()) + return true; + + if(!(nHeight >= chainparams.GetConsensus().QIP5Height)) + return false; + + // Check that the sender address inside the output is only valid for contract outputs + for (const CTxOut& txout : tx.vout) + { + bool hashOpSender = txout.scriptPubKey.HasOpSender(); + if(hashOpSender && + !(txout.scriptPubKey.HasOpCreate() || + txout.scriptPubKey.HasOpCall())) + { + return false; + } + + // Solve the script that match the sender templates + if(hashOpSender && !ExtractSenderData(txout.scriptPubKey, nullptr, nullptr)) + return false; + } + + return true; +} + +bool CheckSenderScript(const CCoinsViewCache& view, const CTransaction& tx){ + // Check for the sender that pays the coins + CScript script = view.AccessCoin(tx.vin[0].prevout).out.scriptPubKey; + if(!script.IsPayToPubkeyHash() && !script.IsPayToPubkey()){ + return false; + } + + // Check for additional VM sender + if(!tx.HasOpSender()) + return true; + + // Check for the VM sender that is encoded into the output + for (const CTxOut& txout : tx.vout) + { + if(txout.scriptPubKey.HasOpSender()) + { + // Extract the sender data + CScript senderPubKey, senderSig; + if(!ExtractSenderData(txout.scriptPubKey, &senderPubKey, &senderSig)) + return false; + + // Check that the pub key is valid sender that can be used in the VM + if(!senderPubKey.IsPayToPubkeyHash() && !senderPubKey.IsPayToPubkey()) + return false; + + // Get the signature stack + std::vector > stack; + if (!EvalScript(stack, senderSig, SCRIPT_VERIFY_NONE, BaseSignatureChecker(), SigVersion::BASE)) + return false; + + // Check that the signature script contains only signature and public key (2 items) + if(stack.size() != STANDARD_SENDER_STACK_ITEMS) + return false; + + // Check that the items size is no more than 80 bytes + for(size_t i=0; i < stack.size(); i++) + { + if(stack[i].size() > MAX_STANDARD_SENDER_STACK_ITEM_SIZE) + return false; + } + } + } + + return true; +} + std::vector CallContract(const dev::Address& addrContract, std::vector opcode, Chainstate& chainstate, const dev::Address& sender, uint64_t gasLimit, CAmount nAmount){ return {}; } +bool CheckMinGasPrice(std::vector& etps, const uint64_t& minGasPrice){ + for(EthTransactionParams& etp : etps){ + if(etp.gasPrice < dev::u256(minGasPrice)) + return false; + } + return true; +} + +bool CheckReward(const CBlock& block, BlockValidationState& state, int nHeight, const Consensus::Params& consensusParams, CAmount nFees, CAmount gasRefunds, CAmount nActualStakeReward, const std::vector& vouts, CAmount nValueCoinPrev, bool delegateOutputExist, CChain& chain, node::BlockManager& blockman) +{ + return {}; +} + +valtype GetSenderAddress(const CTransaction& tx, const CCoinsViewCache* coinsView, const std::vector* blockTxs, Chainstate& chainstate, const CTxMemPool* mempool, int nOut = -1){ + return {}; +} + +UniValue vmLogToJSON(const ResultExecute& execRes, const CTransaction& tx, const CBlock& block, CChain& chain){ + UniValue result(UniValue::VOBJ); + if(tx != CTransaction()) + result.pushKV("txid", tx.GetHash().GetHex()); + result.pushKV("address", execRes.execRes.newAddress.hex()); + if(block.GetHash() != CBlock().GetHash()){ + result.pushKV("time", block.GetBlockTime()); + result.pushKV("blockhash", block.GetHash().GetHex()); + result.pushKV("blockheight", chain.Tip()->nHeight + 1); + } else { + result.pushKV("time", GetAdjustedTimeSeconds()); + result.pushKV("blockheight", chain.Tip()->nHeight); + } + UniValue logEntries(UniValue::VARR); + dev::eth::LogEntries logs = execRes.txRec.log(); + for(dev::eth::LogEntry log : logs){ + UniValue logEntrie(UniValue::VOBJ); + logEntrie.pushKV("address", log.address.hex()); + UniValue topics(UniValue::VARR); + for(dev::h256 l : log.topics){ + UniValue topicPair(UniValue::VOBJ); + topicPair.pushKV("raw", l.hex()); + topics.push_back(topicPair); + //TODO add "pretty" field for human readable data + } + UniValue dataPair(UniValue::VOBJ); + dataPair.pushKV("raw", HexStr(log.data)); + logEntrie.pushKV("data", dataPair); + logEntrie.pushKV("topics", topics); + logEntries.push_back(logEntrie); + } + result.pushKV("entries", logEntries); + return result; +} + +void writeVMlog(const std::vector& res, CChain& chain, const CTransaction& tx, const CBlock& block){ + fs::path qtumDir = gArgs.GetDataDirNet() / "vmExecLogs.json"; + std::stringstream ss; + if(fIsVMlogFile){ + ss << ","; + } else { + std::ofstream file(PathToString(qtumDir), std::ios::out | std::ios::app); + file << "{\"logs\":[]}"; + file.close(); + } + + for(size_t i = 0; i < res.size(); i++){ + ss << vmLogToJSON(res[i], tx, block, chain).write(); + if(i != res.size() - 1){ + ss << ","; + } else { + ss << "]}"; + } + } + + std::ofstream file(PathToString(qtumDir), std::ios::in | std::ios::out); + file.seekp(-2, std::ios::end); + file << ss.str(); + file.close(); + fIsVMlogFile = true; +} + +LastHashes::LastHashes() +{} + +void LastHashes::set(const CBlockIndex *tip) +{ + clear(); + + m_lastHashes.resize(256); + for(int i=0;i<256;i++){ + if(!tip) + break; + m_lastHashes[i]= uintToh256(*tip->phashBlock); + tip = tip->pprev; + } +} + +dev::h256s LastHashes::precedingHashes(const dev::h256 &) const +{ + return m_lastHashes; +} + +void LastHashes::clear() +{ + m_lastHashes.clear(); +} + +bool ByteCodeExec::performByteCode(dev::eth::Permanence type){ + for(QtumTransaction& tx : txs){ + //validate VM version + if(tx.getVersion().toRaw() != VersionVM::GetEVMDefault().toRaw()){ + return false; + } + dev::eth::EnvInfo envInfo(BuildEVMEnvironment()); + if(!tx.isCreation() && !globalState->addressInUse(tx.receiveAddress())){ + dev::eth::ExecutionResult execRes; + execRes.excepted = dev::eth::TransactionException::Unknown; + result.push_back(ResultExecute{execRes, QtumTransactionReceipt(dev::h256(), dev::h256(), dev::u256(), dev::eth::LogEntries()), CTransaction()}); + continue; + } + result.push_back(globalState->execute(envInfo, *globalSealEngine.get(), tx, chain, type, OnOpFunc())); + } + globalState->db().commit(); + globalState->dbUtxo().commit(); + globalSealEngine.get()->deleteAddresses.clear(); + return true; +} + +bool ByteCodeExec::processingResults(ByteCodeExecResult& resultBCE){ + const Consensus::Params& consensusParams = Params().GetConsensus(); + for(size_t i = 0; i < result.size(); i++){ + uint64_t gasUsed = (uint64_t) result[i].execRes.gasUsed; + + if(result[i].execRes.excepted != dev::eth::TransactionException::None){ + // refund coins sent to the contract to the sender + if(txs[i].value() > 0){ + CMutableTransaction tx; + tx.vin.push_back(CTxIn(h256Touint(txs[i].getHashWith()), txs[i].getNVout(), CScript() << OP_SPEND)); + CScript script(CScript() << OP_DUP << OP_HASH160 << txs[i].sender().asBytes() << OP_EQUALVERIFY << OP_CHECKSIG); + tx.vout.push_back(CTxOut(CAmount(txs[i].value()), script)); + resultBCE.valueTransfers.push_back(CTransaction(tx)); + } + if(!(chain.Height() >= consensusParams.QIP7Height && result[i].execRes.excepted == dev::eth::TransactionException::RevertInstruction)){ + resultBCE.usedGas += gasUsed; + } + } + + if(result[i].execRes.excepted == dev::eth::TransactionException::None || (chain.Height() >= consensusParams.QIP7Height && result[i].execRes.excepted == dev::eth::TransactionException::RevertInstruction)){ + if(txs[i].gas() > UINT64_MAX || + result[i].execRes.gasUsed > UINT64_MAX || + txs[i].gasPrice() > UINT64_MAX){ + return false; + } + uint64_t gas = (uint64_t) txs[i].gas(); + uint64_t gasPrice = (uint64_t) txs[i].gasPrice(); + + resultBCE.usedGas += gasUsed; + int64_t amount = (gas - gasUsed) * gasPrice; + if(amount < 0){ + return false; + } + if(amount > 0){ + // Refund the rest of the amount to the sender that provide the coins for the contract + CScript script(CScript() << OP_DUP << OP_HASH160 << txs[i].getRefundSender().asBytes() << OP_EQUALVERIFY << OP_CHECKSIG); + resultBCE.refundOutputs.push_back(CTxOut(amount, script)); + resultBCE.refundSender += amount; + } + } + + if(result[i].tx != CTransaction()){ + resultBCE.valueTransfers.push_back(result[i].tx); + } + } + return true; +} + +dev::eth::EnvInfo ByteCodeExec::BuildEVMEnvironment(){ + CBlockIndex* tip = pindex; + dev::eth::BlockHeader header; + header.setNumber(tip->nHeight + 1); + header.setTimestamp(block.nTime); + header.setDifficulty(dev::u256(block.nBits)); + header.setGasLimit(blockGasLimit); + + lastHashes.set(tip); + + if(block.IsProofOfStake()){ + header.setAuthor(EthAddrFromScript(block.vtx[1]->vout[1].scriptPubKey)); + }else { + header.setAuthor(EthAddrFromScript(block.vtx[0]->vout[0].scriptPubKey)); + } + dev::u256 gasUsed; + int &chainID = const_cast(globalSealEngine->chainParams().chainID); + chainID = qtumutils::eth_getChainId(tip->nHeight); + dev::eth::EnvInfo env(header, lastHashes, gasUsed, chainID); + return env; +} + +dev::Address ByteCodeExec::EthAddrFromScript(const CScript& script){ + return {}; +} + +bool QtumTxConverter::extractionQtumTransactions(ExtractQtumTX& qtumtx){ + // Get the address of the sender that pay the coins for the contract transactions + refundSender = dev::Address(GetSenderAddress(txBit, view, blockTransactions, chainstate, mempool)); + + // Extract contract transactions + std::vector resultTX; + std::vector resultETP; + for(size_t i = 0; i < txBit.vout.size(); i++){ + if(txBit.vout[i].scriptPubKey.HasOpCreate() || txBit.vout[i].scriptPubKey.HasOpCall()){ + if(receiveStack(txBit.vout[i].scriptPubKey)){ + EthTransactionParams params; + if(parseEthTXParams(params)){ + resultTX.push_back(createEthTX(params, i)); + resultETP.push_back(params); + }else{ + return false; + } + }else{ + return false; + } + } + } + qtumtx = std::make_pair(resultTX, resultETP); + return true; +} + +bool QtumTxConverter::receiveStack(const CScript& scriptPubKey){ + sender = false; + EvalScript(stack, scriptPubKey, nFlags, BaseSignatureChecker(), SigVersion::BASE, nullptr); + if (stack.empty()) + return false; + + CScript scriptRest(stack.back().begin(), stack.back().end()); + stack.pop_back(); + sender = scriptPubKey.HasOpSender(); + + opcode = (opcodetype)(*scriptRest.begin()); + if((opcode == OP_CREATE && stack.size() < correctedStackSize(4)) || (opcode == OP_CALL && stack.size() < correctedStackSize(5))){ + stack.clear(); + sender = false; + return false; + } + + return true; +} + +bool QtumTxConverter::parseEthTXParams(EthTransactionParams& params){ + try{ + dev::Address receiveAddress; + valtype vecAddr; + if (opcode == OP_CALL) + { + vecAddr = stack.back(); + stack.pop_back(); + receiveAddress = dev::Address(vecAddr); + } + if(stack.size() < correctedStackSize(4)) + return false; + + if(stack.back().size() < 1){ + return false; + } + valtype code(stack.back()); + stack.pop_back(); + uint64_t gasPrice = CScriptNum::vch_to_uint64(stack.back()); + stack.pop_back(); + uint64_t gasLimit = CScriptNum::vch_to_uint64(stack.back()); + stack.pop_back(); + if(gasPrice > INT64_MAX || gasLimit > INT64_MAX){ + return false; + } + //we track this as CAmount in some places, which is an int64_t, so constrain to INT64_MAX + if(gasPrice !=0 && gasLimit > INT64_MAX / gasPrice){ + //overflows past 64bits, reject this tx + return false; + } + if(stack.back().size() > 4){ + return false; + } + VersionVM version = VersionVM::fromRaw((uint32_t)CScriptNum::vch_to_uint64(stack.back())); + stack.pop_back(); + params.version = version; + params.gasPrice = dev::u256(gasPrice); + params.receiveAddress = receiveAddress; + params.code = code; + params.gasLimit = dev::u256(gasLimit); + return true; + } + catch(const scriptnum_error& err){ + LogPrintf("Incorrect parameters to VM."); + return false; + } +} + +QtumTransaction QtumTxConverter::createEthTX(const EthTransactionParams& etp, uint32_t nOut){ + QtumTransaction txEth; + if (etp.receiveAddress == dev::Address() && opcode != OP_CALL){ + txEth = QtumTransaction(txBit.vout[nOut].nValue, etp.gasPrice, etp.gasLimit, etp.code, dev::u256(0)); + } + else{ + txEth = QtumTransaction(txBit.vout[nOut].nValue, etp.gasPrice, etp.gasLimit, etp.receiveAddress, etp.code, dev::u256(0)); + } + dev::Address sender(GetSenderAddress(txBit, view, blockTransactions, chainstate, mempool, (int)nOut)); + txEth.forceSender(sender); + txEth.setHashWith(uintToh256(txBit.GetHash())); + txEth.setNVout(nOut); + txEth.setVersion(etp.version); + txEth.setRefundSender(refundSender); + + return txEth; +} + +size_t QtumTxConverter::correctedStackSize(size_t size){ + // OP_SENDER add 3 more parameters in stack besides those for OP_CREATE or OP_CALL + return sender ? size + 3 : size; +} +/////////////////////////////////////////////////////////////////////// + /** Apply the effects of this block (with given index) on the UTXO set represented by coins. * Validity checks that depend on the UTXO set are also done; ConnectBlock() * can fail if those validity checks fail (among other reasons). */ @@ -2814,7 +3255,7 @@ bool Chainstate::DisconnectTip(BlockValidationState& state, DisconnectedBlockTra { CCoinsViewCache view(&CoinsTip()); assert(view.GetBestBlock() == pindexDelete->GetBlockHash()); - if (DisconnectBlock(block, pindexDelete, view) != DISCONNECT_OK) + if (DisconnectBlock(block, pindexDelete, view, nullptr) != DISCONNECT_OK) return error("DisconnectTip(): DisconnectBlock %s failed", pindexDelete->GetBlockHash().ToString()); bool flushed = view.Flush(); assert(flushed); @@ -3640,6 +4081,73 @@ void ChainstateManager::ReceivedBlockTransactions(const CBlock& block, CBlockInd } } +bool CheckFirstCoinstakeOutput(const CBlock& block) +{ + // Coinbase output should be empty if proof-of-stake block + int commitpos = GetWitnessCommitmentIndex(block); + if(commitpos < 0) + { + if (block.vtx[0]->vout.size() != 1 || !block.vtx[0]->vout[0].IsEmpty()) + return false; + } + else + { + if (block.vtx[0]->vout.size() != 2 || !block.vtx[0]->vout[0].IsEmpty() || block.vtx[0]->vout[1].nValue) + return false; + } + + return true; +} + +bool GetBlockPublicKey(const CBlock& block, std::vector& vchPubKey) +{ + if (block.IsProofOfWork()) + return false; + + if (block.vchBlockSigDlgt.empty()) + return false; + + std::vector vSolutions; + const CTxOut& txout = block.vtx[1]->vout[1]; + TxoutType whichType = Solver(txout.scriptPubKey, vSolutions); + + if (whichType == TxoutType::NONSTANDARD) + return false; + + if (whichType == TxoutType::PUBKEY) + { + vchPubKey = vSolutions[0]; + return true; + } + else + { + // Block signing key also can be encoded in the nonspendable output + // This allows to not pollute UTXO set with useless outputs e.g. in case of multisig staking + + const CScript& script = txout.scriptPubKey; + CScript::const_iterator pc = script.begin(); + opcodetype opcode; + valtype vchPushValue; + + if (!script.GetOp(pc, opcode, vchPubKey)) + return false; + if (opcode != OP_RETURN) + return false; + if (!script.GetOp(pc, opcode, vchPubKey)) + return false; + if (!IsCompressedOrUncompressedPubKey(vchPubKey)) + return false; + return true; + } + + return false; +} + +bool GetBlockDelegation(const CBlock& block, const uint160& staker, uint160& address, uint8_t& fee, CCoinsViewCache& view, Chainstate& chainstate) +{ + return {}; +} + static bool CheckBlockHeader(const CBlockHeader& block, BlockValidationState& state, const Consensus::Params& consensusParams, bool fCheckPOW = true) { // Check proof of work matches claimed amount @@ -3735,13 +4243,13 @@ void ChainstateManager::UpdateUncommittedBlockStructures(CBlock& block, const CB } } -std::vector ChainstateManager::GenerateCoinbaseCommitment(CBlock& block, const CBlockIndex* pindexPrev) const +std::vector ChainstateManager::GenerateCoinbaseCommitment(CBlock& block, const CBlockIndex* pindexPrev, bool fProofOfStake) const { std::vector commitment; int commitpos = GetWitnessCommitmentIndex(block); std::vector ret(32, 0x00); if (commitpos == NO_WITNESS_COMMITMENT) { - uint256 witnessroot = BlockWitnessMerkleRoot(block, nullptr); + uint256 witnessroot = BlockWitnessMerkleRoot(block, nullptr, &fProofOfStake); CHash256().Write(witnessroot).Write(ret).Finalize(witnessroot); CTxOut out; out.nValue = 0; @@ -3765,7 +4273,7 @@ std::vector ChainstateManager::GenerateCoinbaseCommitment(CBlock& bool HasValidProofOfWork(const std::vector& headers, const Consensus::Params& consensusParams) { return std::all_of(headers.cbegin(), headers.cend(), - [&](const auto& header) { return CheckProofOfWork(header.GetHash(), header.nBits, consensusParams);}); + [&](const auto& header) { return header.IsProofOfStake() ? true : CheckProofOfWork(header.GetHash(), header.nBits, consensusParams);}); } arith_uint256 CalculateHeadersWork(const std::vector& headers) @@ -3918,6 +4426,11 @@ static bool ContextualCheckBlock(const CBlock& block, BlockValidationState& stat return true; } +bool Chainstate::UpdateHashProof(const CBlock& block, BlockValidationState& state, const Consensus::Params& consensusParams, CBlockIndex* pindex, CCoinsViewCache& view) +{ + return {}; +} + bool ChainstateManager::AcceptBlockHeader(const CBlockHeader& block, BlockValidationState& state, CBlockIndex** ppindex, bool min_pow_checked) { AssertLockHeld(cs_main); @@ -4029,7 +4542,7 @@ bool ChainstateManager::AcceptBlockHeader(const CBlockHeader& block, BlockValida } // Exposed wrapper for AcceptBlockHeader -bool ChainstateManager::ProcessNewBlockHeaders(const std::vector& headers, bool min_pow_checked, BlockValidationState& state, const CBlockIndex** ppindex) +bool ChainstateManager::ProcessNewBlockHeaders(const std::vector& headers, bool min_pow_checked, BlockValidationState& state, const CBlockIndex** ppindex, const CBlockIndex** pindexFirst) { AssertLockNotHeld(cs_main); { @@ -4176,6 +4689,30 @@ bool ChainstateManager::AcceptBlock(const std::shared_ptr& pblock, return true; } +bool IsCanonicalBlockSignature(const CBlockHeader* pblock, bool checkLowS) +{ + if (pblock->IsProofOfWork()) { + return pblock->vchBlockSigDlgt.empty(); + } + + return checkLowS ? IsLowDERSignature(pblock->vchBlockSigDlgt, NULL, false) : IsDERSignature(pblock->vchBlockSigDlgt, NULL, false); +} + +bool CheckCanonicalBlockSignature(const CBlockHeader* pblock) +{ + // Check compact signature size + if(pblock->IsProofOfStake() && pblock->GetBlockSignature().size() == CPubKey::COMPACT_SIGNATURE_SIZE) + return pblock->HasProofOfDelegation() ? pblock->GetProofOfDelegation().size() == CPubKey::COMPACT_SIGNATURE_SIZE : true; + + //block signature encoding + bool ret = IsCanonicalBlockSignature(pblock, false); + + //block signature encoding (low-s) + if(ret) ret = IsCanonicalBlockSignature(pblock, true); + + return ret; +} + bool ChainstateManager::ProcessNewBlock(const std::shared_ptr& block, bool force_processing, bool min_pow_checked, bool* new_block) { AssertLockNotHeld(cs_main); @@ -4393,7 +4930,8 @@ VerifyDBResult CVerifyDB::VerifyDB( if (nCheckLevel >= 3) { if (curr_coins_usage <= chainstate.m_coinstip_cache_size_bytes) { assert(coins.GetBestBlock() == pindex->GetBlockHash()); - DisconnectResult res = chainstate.DisconnectBlock(block, pindex, coins); + bool fClean=true; + DisconnectResult res = chainstate.DisconnectBlock(block, pindex, coins, &fClean); if (res == DISCONNECT_FAILED) { LogPrintf("Verification error: irrecoverable inconsistency in block data at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString()); return VerifyDBResult::CORRUPTED_BLOCK_DB; @@ -4518,7 +5056,8 @@ bool Chainstate::ReplayBlocks() return error("RollbackBlock(): ReadBlockFromDisk() failed at %d, hash=%s", pindexOld->nHeight, pindexOld->GetBlockHash().ToString()); } LogPrintf("Rolling back %s (%i)\n", pindexOld->GetBlockHash().ToString(), pindexOld->nHeight); - DisconnectResult res = DisconnectBlock(block, pindexOld, cache); + bool fClean=true; + DisconnectResult res = DisconnectBlock(block, pindexOld, cache, &fClean); if (res == DISCONNECT_FAILED) { return error("RollbackBlock(): DisconnectBlock failed at %d, hash=%s", pindexOld->nHeight, pindexOld->GetBlockHash().ToString()); } @@ -5119,6 +5658,42 @@ bool Chainstate::ResizeCoinsCaches(size_t coinstip_size, size_t coinsdb_size) return ret; } +bool Chainstate::RemoveBlockIndex(CBlockIndex *pindex) +{ + AssertLockHeld(cs_main); + // Check if the block index is present in any variable and remove it + if(m_chainman.m_best_invalid == pindex) + m_chainman.m_best_invalid = nullptr; + + if(m_chainman.m_best_header == pindex) + m_chainman.m_best_header = nullptr; + + // Check if the block index is present in any list and remove it + for (auto it=m_blockman.m_blocks_unlinked.begin(); it!=m_blockman.m_blocks_unlinked.end();){ + if(it->first == pindex || it->second == pindex) + { + it = m_blockman.m_blocks_unlinked.erase(it); + } + else{ + it++; + } + } + + setBlockIndexCandidates.erase(pindex); + + m_chainman.m_failed_blocks.erase(pindex); + + m_blockman.m_dirty_blockindex.erase(pindex); + + for (int b = 0; b < VERSIONBITS_NUM_BITS; b++) { + m_chainman.m_warningcache[b].erase(pindex); + } + + m_chainman.m_versionbitscache.Erase(pindex); + + return true; +} + //! Guess how far we are in the verification process at the given block index //! require cs_main if pindex has not been validated yet (because nChainTx might be unset) double GuessVerificationProgress(const ChainTxData& data, const CBlockIndex *pindex) { @@ -5138,6 +5713,30 @@ double GuessVerificationProgress(const ChainTxData& data, const CBlockIndex *pin return std::min(pindex->nChainTx / fTxTotal, 1.0); } +std::string exceptedMessage(const dev::eth::TransactionException& excepted, const dev::bytes& output) +{ + std::string message; + try + { + // Process the revert message from the output + if(excepted == dev::eth::TransactionException::RevertInstruction) + { + // Get function: Error(string) + dev::bytesConstRef oRawData(&output); + dev::bytes errorFunc = oRawData.cropped(0, 4).toBytes(); + if(dev::toHex(errorFunc) == "08c379a0") + { + dev::bytesConstRef oData = oRawData.cropped(4); + message = dev::eth::ABIDeserialiser::deserialise(oData); + } + } + } + catch(...) + {} + + return message; +} + std::optional ChainstateManager::SnapshotBlockhash() const { LOCK(::cs_main); @@ -6108,6 +6707,11 @@ CAmount GetTxGasFee(const CMutableTransaction& _tx, const CTxMemPool& mempool, C return {}; } +bool GetAddressWeight(uint256 addressHash, int type, const std::map& immatureStakes, int32_t nHeight, uint64_t& nWeight, node::BlockManager& blockman) +{ + return {}; +} + std::map GetImmatureStakes(ChainstateManager& chainman) { std::map immatureStakes; diff --git a/src/validation.h b/src/validation.h index ef2d04b4b8..8ee9bc1e0c 100644 --- a/src/validation.h +++ b/src/validation.h @@ -149,6 +149,8 @@ extern const std::vector CHECKLEVEL_DOC; /** The set of all seen COutPoint entries for proof of stake. */ extern std::set> setStakeSeen; +int64_t FutureDrift(uint32_t nTime, int nHeight, const Consensus::Params& consensusParams); + /** Run instances of script checking worker threads */ void StartScriptCheckWorkerThreads(int threads_num); /** Stop all of the script checking worker threads */ @@ -164,6 +166,12 @@ double GuessVerificationProgress(const ChainTxData& data, const CBlockIndex* pin /** Prune block files up to a given height */ void PruneBlockFilesManual(Chainstate& active_chainstate, int nManualPruneHeight); +/** Check if the transaction is confirmed in N previous blocks */ +bool IsConfirmedInNPrevBlocks(const CDiskTxPos& txindex, const CBlockIndex* pindexFrom, int nMaxDepth, int& nActualDepth); + +/** Check if the header proof is valid */ +bool CheckHeaderProof(const CBlockHeader& block, const Consensus::Params& consensusParams, Chainstate& chainstate); + /** * Validation result for a single transaction mempool acceptance. */ @@ -370,10 +378,13 @@ class CScriptCheck bool cacheStore; ScriptError error{SCRIPT_ERR_UNKNOWN_ERROR}; PrecomputedTransactionData *txdata; + int nOut; public: CScriptCheck(const CTxOut& outIn, const CTransaction& txToIn, unsigned int nInIn, unsigned int nFlagsIn, bool cacheIn, PrecomputedTransactionData* txdataIn) : - m_tx_out(outIn), ptxTo(&txToIn), nIn(nInIn), nFlags(nFlagsIn), cacheStore(cacheIn), txdata(txdataIn) { } + m_tx_out(outIn), ptxTo(&txToIn), nIn(nInIn), nFlags(nFlagsIn), cacheStore(cacheIn), txdata(txdataIn), nOut(-1) { } + CScriptCheck(const CTransaction& txToIn, int nOutIn, unsigned int nFlagsIn, bool cacheIn, PrecomputedTransactionData* txdataIn) : + ptxTo(&txToIn), nIn(0), nFlags(nFlagsIn), cacheStore(cacheIn), txdata(txdataIn), nOut(nOutIn){ } CScriptCheck(const CScriptCheck&) = delete; CScriptCheck& operator=(const CScriptCheck&) = delete; @@ -383,6 +394,8 @@ class CScriptCheck bool operator()(); ScriptError GetScriptError() const { return error; } + + bool checkOutput() const { return nOut > -1; } }; // CScriptCheck is used a lot in std::vector, make sure that's efficient @@ -405,6 +418,8 @@ bool GetAddressUnspent(uint256 addressHash, int type, bool GetTimestampIndex(const unsigned int &high, const unsigned int &low, const bool fActiveOnly, std::vector > &hashes, ChainstateManager& chainman); +bool GetAddressWeight(uint256 addressHash, int type, const std::map& immatureStakes, int32_t nHeight, uint64_t& nWeight, node::BlockManager& blockman); + std::map GetImmatureStakes(ChainstateManager& chainman); ///////////////////////////////////////////////////////////////// @@ -414,6 +429,10 @@ bool CheckIndexProof(const CBlockIndex& block, const Consensus::Params& consensu /** Context-independent validity checks */ bool CheckBlock(const CBlock& block, BlockValidationState& state, const Consensus::Params& consensusParams, Chainstate& chainstate, bool fCheckPOW = true, bool fCheckMerkleRoot = true, bool fCheckSig=true); +bool CheckFirstCoinstakeOutput(const CBlock& block); +bool GetBlockPublicKey(const CBlock& block, std::vector& vchPubKey); +bool GetBlockDelegation(const CBlock& block, const uint160& staker, uint160& address, uint8_t& fee, CCoinsViewCache& view, Chainstate& chainstate); +bool CheckCanonicalBlockSignature(const CBlockHeader* pblock); /** Check a block is completely valid from start to finish (only works on top of our current best block) */ bool TestBlockValidity(BlockValidationState& state, @@ -456,8 +475,128 @@ class CVerifyDB int nCheckDepth) EXCLUSIVE_LOCKS_REQUIRED(cs_main); }; +bool CheckReward(const CBlock& block, BlockValidationState& state, int nHeight, const Consensus::Params& consensusParams, CAmount nFees, CAmount gasRefunds, CAmount nActualStakeReward, const std::vector& vouts, CAmount nValueCoinPrev, bool delegateOutputExist, CChain& chain, node::BlockManager& blockman); + +//////////////////////////////////////////////////////// qtum +bool GetSpentCoinFromBlock(const CBlockIndex* pindex, COutPoint prevout, Coin* coin); + +bool GetSpentCoinFromMainChain(const CBlockIndex* pforkPrev, COutPoint prevoutStake, Coin* coin, CChain& chain); + +unsigned int GetContractScriptFlags(int nHeight, const Consensus::Params& consensusparams); + std::vector CallContract(const dev::Address& addrContract, std::vector opcode, Chainstate& chainstate, const dev::Address& sender = dev::Address(), uint64_t gasLimit=0, CAmount nAmount=0); +bool CheckOpSender(const CTransaction& tx, const CChainParams& chainparams, int nHeight); + +bool CheckSenderScript(const CCoinsViewCache& view, const CTransaction& tx); + +bool CheckMinGasPrice(std::vector& etps, const uint64_t& minGasPrice); + +void writeVMlog(const std::vector& res, CChain& chain, const CTransaction& tx = CTransaction(), const CBlock& block = CBlock()); + +std::string exceptedMessage(const dev::eth::TransactionException& excepted, const dev::bytes& output); + +struct EthTransactionParams{ + VersionVM version; + dev::u256 gasLimit; + dev::u256 gasPrice; + valtype code; + dev::Address receiveAddress; + + bool operator!=(EthTransactionParams etp){ + if(this->version.toRaw() != etp.version.toRaw() || this->gasLimit != etp.gasLimit || + this->gasPrice != etp.gasPrice || this->code != etp.code || + this->receiveAddress != etp.receiveAddress) + return true; + return false; + } +}; + +struct ByteCodeExecResult{ + uint64_t usedGas = 0; + CAmount refundSender = 0; + std::vector refundOutputs; + std::vector valueTransfers; +}; + +class QtumTxConverter{ + +public: + + QtumTxConverter(CTransaction tx, Chainstate& _chainstate, const CTxMemPool* _mempool, CCoinsViewCache* v = NULL, const std::vector* blockTxs = NULL, unsigned int flags = SCRIPT_EXEC_BYTE_CODE) : txBit(tx), view(v), blockTransactions(blockTxs), sender(false), nFlags(flags), chainstate(_chainstate), mempool(_mempool){} + + bool extractionQtumTransactions(ExtractQtumTX& qtumTx); + +private: + + bool receiveStack(const CScript& scriptPubKey); + + bool parseEthTXParams(EthTransactionParams& params); + + QtumTransaction createEthTX(const EthTransactionParams& etp, const uint32_t nOut); + + size_t correctedStackSize(size_t size); + + const CTransaction txBit; + const CCoinsViewCache* view; + std::vector stack; + opcodetype opcode; + const std::vector *blockTransactions; + bool sender; + dev::Address refundSender; + unsigned int nFlags; + Chainstate& chainstate; + const CTxMemPool* mempool; +}; + +class LastHashes: public dev::eth::LastBlockHashesFace +{ +public: + explicit LastHashes(); + + void set(CBlockIndex const* tip); + + dev::h256s precedingHashes(dev::h256 const&) const override; + + void clear() override; + +private: + dev::h256s m_lastHashes; +}; + +class ByteCodeExec { + +public: + + ByteCodeExec(const CBlock& _block, std::vector _txs, const uint64_t _blockGasLimit, CBlockIndex* _pindex, CChain& _chain) : txs(_txs), block(_block), blockGasLimit(_blockGasLimit), pindex(_pindex), chain(_chain) {} + + bool performByteCode(dev::eth::Permanence type = dev::eth::Permanence::Committed); + + bool processingResults(ByteCodeExecResult& result); + + std::vector& getResult(){ return result; } + +private: + + dev::eth::EnvInfo BuildEVMEnvironment(); + + dev::Address EthAddrFromScript(const CScript& scriptIn); + + std::vector txs; + + std::vector result; + + const CBlock& block; + + const uint64_t blockGasLimit; + + CBlockIndex* pindex; + + LastHashes lastHashes; + + CChain& chain; +}; + enum DisconnectResult { DISCONNECT_OK, // All good. @@ -735,10 +874,11 @@ class Chainstate LOCKS_EXCLUDED(::cs_main); // Block (dis)connection on a given view: - DisconnectResult DisconnectBlock(const CBlock& block, const CBlockIndex* pindex, CCoinsViewCache& view) + DisconnectResult DisconnectBlock(const CBlock& block, const CBlockIndex* pindex, CCoinsViewCache& view, bool* pfClean) EXCLUSIVE_LOCKS_REQUIRED(::cs_main); bool ConnectBlock(const CBlock& block, BlockValidationState& state, CBlockIndex* pindex, CCoinsViewCache& view, bool fJustCheck = false) EXCLUSIVE_LOCKS_REQUIRED(cs_main); + bool UpdateHashProof(const CBlock& block, BlockValidationState& state, const Consensus::Params& consensusParams, CBlockIndex* pindex, CCoinsViewCache& view); // Apply the effects of a block disconnection on the UTXO set. bool DisconnectTip(BlockValidationState& state, DisconnectedBlockTransactions* disconnectpool) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_mempool->cs); @@ -774,6 +914,8 @@ class Chainstate void ClearBlockIndexCandidates() EXCLUSIVE_LOCKS_REQUIRED(::cs_main); + bool RemoveBlockIndex(CBlockIndex *pindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main); + /** Find the last common block of this chain and a locator. */ const CBlockIndex* FindForkInGlobalIndex(const CBlockLocator& locator) const EXCLUSIVE_LOCKS_REQUIRED(cs_main); @@ -1216,7 +1358,7 @@ class ChainstateManager * @param[out] state This may be set to an Error state if any error occurred processing them * @param[out] ppindex If set, the pointer will be set to point to the last new block index object for the given headers */ - bool ProcessNewBlockHeaders(const std::vector& block, bool min_pow_checked, BlockValidationState& state, const CBlockIndex** ppindex = nullptr) LOCKS_EXCLUDED(cs_main); + bool ProcessNewBlockHeaders(const std::vector& block, bool min_pow_checked, BlockValidationState& state, const CBlockIndex** ppindex = nullptr, const CBlockIndex** pindexFirst=nullptr) LOCKS_EXCLUDED(cs_main); /** * Sufficiently validate a block for disk storage (and store on disk). @@ -1261,7 +1403,7 @@ class ChainstateManager void UpdateUncommittedBlockStructures(CBlock& block, const CBlockIndex* pindexPrev) const; /** Produce the necessary coinbase commitment for a block (modifies the hash, don't call for mined blocks). */ - std::vector GenerateCoinbaseCommitment(CBlock& block, const CBlockIndex* pindexPrev) const; + std::vector GenerateCoinbaseCommitment(CBlock& block, const CBlockIndex* pindexPrev, bool fProofOfStake=false) const; /** This is used by net_processing to report pre-synchronization progress of headers, as * headers are not yet fed to validation during that time, but validation is (for now)