diff --git a/src/amount.h b/src/amount.h index 07d553b0ef..868de715fa 100644 --- a/src/amount.h +++ b/src/amount.h @@ -78,7 +78,7 @@ struct DCT_ID { static constexpr CAmount COIN = 100000000; static constexpr CAmount CENT = 1000000; static constexpr int64_t WEI_IN_GWEI = 1000000000; -static constexpr int64_t CAMOUNT_TO_WEI = 10; +static constexpr int64_t CAMOUNT_TO_GWEI = 10; //Converts the given value to decimal format string with COIN precision. inline std::string GetDecimalString(CAmount nValue) diff --git a/src/masternodes/mn_checks.cpp b/src/masternodes/mn_checks.cpp index a0c7153847..eeb9e2c379 100644 --- a/src/masternodes/mn_checks.cpp +++ b/src/masternodes/mn_checks.cpp @@ -3859,7 +3859,7 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { } arith_uint256 balanceIn = amount; - balanceIn *= CAMOUNT_TO_WEI * WEI_IN_GWEI; + balanceIn *= CAMOUNT_TO_GWEI * WEI_IN_GWEI; evm_add_balance(evmContext, HexStr(toAddress.begin(), toAddress.end()), ArithToUint256(balanceIn).ToArrayReversed(), tx.GetHash().ToArrayReversed()); } } @@ -3894,7 +3894,7 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { } arith_uint256 balanceIn = amount; - balanceIn *= CAMOUNT_TO_WEI * WEI_IN_GWEI; + balanceIn *= CAMOUNT_TO_GWEI * WEI_IN_GWEI; if (!evm_sub_balance(evmContext, HexStr(fromAddress.begin(), fromAddress.end()), ArithToUint256(balanceIn).ToArrayReversed(), tx.GetHash().ToArrayReversed())) { return Res::Err("Not enough balance in %s to cover \"evmout\" transfer", EncodeDestination(dest)); } diff --git a/src/masternodes/rpc_evm.cpp b/src/masternodes/rpc_evm.cpp index 559b2d1a12..ff8d28aa34 100644 --- a/src/masternodes/rpc_evm.cpp +++ b/src/masternodes/rpc_evm.cpp @@ -77,7 +77,7 @@ UniValue evmtx(const JSONRPCRequest& request) { } const arith_uint256 valueParam = AmountFromValue(request.params[5]); - const auto value = ArithToUint256(valueParam * CAMOUNT_TO_WEI * WEI_IN_GWEI); + const auto value = ArithToUint256(valueParam * CAMOUNT_TO_GWEI * WEI_IN_GWEI); rust::Vec input{}; if (!request.params[6].isNull()) { diff --git a/src/masternodes/validation.cpp b/src/masternodes/validation.cpp index ab65e70b49..4b0b7bbfe7 100644 --- a/src/masternodes/validation.cpp +++ b/src/masternodes/validation.cpp @@ -2,6 +2,7 @@ // Distributed under the MIT software license, see the accompanying // file LICENSE or http://www.opensource.org/licenses/mit-license.php. +#include #include #include #include @@ -2352,7 +2353,99 @@ static void ProcessGrandCentralEvents(const CBlockIndex* pindex, CCustomCSView& cache.SetVariable(*attributes); } -void ProcessDeFiEvent(const CBlock &block, const CBlockIndex* pindex, CCustomCSView& mnview, const CCoinsViewCache& view, const CChainParams& chainparams, const CreationTxs &creationTxs) { +static void RevertTransferDomain(const CTransferDomainMessage &obj, CCustomCSView &mnview) { + if (obj.type == CTransferDomainType::DVMTokenToEVM) { + for (const auto& [owner, balance] : obj.from) { + mnview.AddBalances(owner, balance); + } + } else { + for (const auto& [owner, balance] : obj.to) { + mnview.SubBalances(owner, balance); + } + } +} + +static void RevertFailedTransferDomainTxs(const std::vector &failedTransactions, const CBlock& block, const Consensus::Params &consensus, const int height, CCustomCSView &mnview) { + std::set potentialTxsToUndo; + for (const auto &txStr : failedTransactions) { + potentialTxsToUndo.insert(uint256S(txStr)); + } + + std::set txsToUndo; + for (const auto &tx : block.vtx) { + if (tx && potentialTxsToUndo.count(tx->GetHash())) { + std::vector metadata; + const auto txType = GuessCustomTxType(*tx, metadata, false); + if (txType == CustomTxType::TransferDomain) { + auto txMessage = customTypeToMessage(txType); + assert(CustomMetadataParse(height, consensus, metadata, txMessage)); + auto obj = std::get(txMessage); + RevertTransferDomain(obj, mnview); + } + } + } +} + +static void ProcessEVMQueue(const CBlock &block, const CBlockIndex *pindex, CCustomCSView &cache, const CChainParams& chainparams, const uint64_t evmContext) { + + if (IsEVMEnabled(pindex->nHeight, cache)) { + CKeyID minter; + assert(block.ExtractMinterKey(minter)); + std::array beneficiary{}; + CScript minerAddress; + + if (!fMockNetwork) { + const auto id = cache.GetMasternodeIdByOperator(minter); + assert(id); + const auto node = cache.GetMasternode(*id); + assert(node); + + auto height = node->creationHeight; + auto mnID = *id; + if (!node->collateralTx.IsNull()) { + const auto idHeight = cache.GetNewCollateral(node->collateralTx); + assert(idHeight); + height = idHeight->blockHeight - GetMnResignDelay(std::numeric_limits::max()); + mnID = node->collateralTx; + } + + const auto blockindex = ::ChainActive()[height]; + assert(blockindex); + + CTransactionRef tx; + uint256 hash_block; + assert(GetTransaction(mnID, tx, Params().GetConsensus(), hash_block, blockindex)); + assert(tx->vout.size() >= 2); + + CTxDestination dest; + assert(ExtractDestination(tx->vout[1].scriptPubKey, dest)); + assert(dest.index() == PKHashType || dest.index() == WitV0KeyHashType); + + const auto keyID = dest.index() == PKHashType ? CKeyID(std::get(dest)) : CKeyID(std::get(dest)); + std::copy(keyID.begin(), keyID.end(), beneficiary.begin()); + minerAddress = GetScriptForDestination(dest); + } else { + std::copy(minter.begin(), minter.end(), beneficiary.begin()); + const auto dest = PKHash(minter); + minerAddress = GetScriptForDestination(dest); + } + + const auto blockResult = evm_finalize(evmContext, true, block.nBits, beneficiary, block.GetBlockTime()); + + if (!blockResult.failed_transactions.empty()) { + std::vector failedTransactions; + for (const auto& rust_string : blockResult.failed_transactions) { + failedTransactions.emplace_back(rust_string.data(), rust_string.length()); + } + + RevertFailedTransferDomainTxs(failedTransactions, block, chainparams.GetConsensus(), pindex->nHeight, cache); + } + + cache.AddBalance(minerAddress, {DCT_ID{}, static_cast(blockResult.miner_fee / CAMOUNT_TO_GWEI)}); + } +} + +void ProcessDeFiEvent(const CBlock &block, const CBlockIndex* pindex, CCustomCSView& mnview, const CCoinsViewCache& view, const CChainParams& chainparams, const CreationTxs &creationTxs, const uint64_t evmContext) { CCustomCSView cache(mnview); // calculate rewards to current block @@ -2406,6 +2499,9 @@ void ProcessDeFiEvent(const CBlock &block, const CBlockIndex* pindex, CCustomCSV // Migrate foundation members to attributes ProcessGrandCentralEvents(pindex, cache, chainparams); + // Execute EVM Queue + ProcessEVMQueue(block, pindex, cache, chainparams, evmContext); + // construct undo auto& flushable = cache.GetStorage(); auto undo = CUndo::Construct(mnview.GetStorage(), flushable.GetRaw()); diff --git a/src/masternodes/validation.h b/src/masternodes/validation.h index 73c1db4eb2..4de96efb18 100644 --- a/src/masternodes/validation.h +++ b/src/masternodes/validation.h @@ -17,7 +17,7 @@ class CCustomCSView; using CreationTxs = std::map>>>; -void ProcessDeFiEvent(const CBlock &block, const CBlockIndex* pindex, CCustomCSView& mnview, const CCoinsViewCache& view, const CChainParams& chainparams, const CreationTxs &creationTxs); +void ProcessDeFiEvent(const CBlock &block, const CBlockIndex* pindex, CCustomCSView& mnview, const CCoinsViewCache& view, const CChainParams& chainparams, const CreationTxs &creationTxs, const uint64_t evmContext); std::vector CollectAuctionBatches(const CVaultAssets& vaultAssets, const TAmounts& collBalances, const TAmounts& loanBalances); diff --git a/src/miner.cpp b/src/miner.cpp index 932c906d5c..7dedda35eb 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -274,7 +274,7 @@ std::unique_ptr BlockAssembler::CreateNewBlock(const CScript& sc const auto blockHash = std::vector(blockResult.block_hash.begin(), blockResult.block_hash.end()); - xvm = XVM{{uint256(blockHash), blockResult.miner_fee}}; + xvm = XVM{{uint256(blockHash), blockResult.miner_fee / CAMOUNT_TO_GWEI}}; std::vector failedTransactions; for (const auto& rust_string : blockResult.failed_transactions) { diff --git a/src/validation.cpp b/src/validation.cpp index 520dd104a6..4d6c895483 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -2008,39 +2008,6 @@ static unsigned int GetBlockScriptFlags(const CBlockIndex* pindex, const Consens return flags; } -static void RevertTransferDomain(const CTransferDomainMessage &obj, CCustomCSView &mnview) { - if (obj.type == CTransferDomainType::DVMTokenToEVM) { - for (const auto& [owner, balance] : obj.from) { - mnview.AddBalances(owner, balance); - } - } else { - for (const auto& [owner, balance] : obj.to) { - mnview.SubBalances(owner, balance); - } - } -} - -static void RevertFailedTransferDomainTxs(const std::vector &failedTransactions, const CBlock& block, const Consensus::Params &consensus, const int height, CCustomCSView &mnview) { - std::set potentialTxsToUndo; - for (const auto &txStr : failedTransactions) { - potentialTxsToUndo.insert(uint256S(txStr)); - } - - std::set txsToUndo; - for (const auto &tx : block.vtx) { - if (tx && potentialTxsToUndo.count(tx->GetHash())) { - std::vector metadata; - const auto txType = GuessCustomTxType(*tx, metadata, false); - if (txType == CustomTxType::TransferDomain) { - auto txMessage = customTypeToMessage(txType); - assert(CustomMetadataParse(height, consensus, metadata, txMessage)); - auto obj = std::get(txMessage); - RevertTransferDomain(obj, mnview); - } - } - } -} - Res ApplyGeneralCoinbaseTx(CCustomCSView & mnview, CTransaction const & tx, int height, CAmount nFees, const Consensus::Params& consensus) { TAmounts const cbValues = tx.GetValuesOut(); @@ -2847,7 +2814,7 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl // add this block to the view's block chain view.SetBestBlock(pindex->GetBlockHash()); - ProcessDeFiEvent(block, pindex, mnview, view, chainparams, creationTxs); + ProcessDeFiEvent(block, pindex, mnview, view, chainparams, creationTxs, evmContext); // Write any UTXO burns for (const auto& [key, value] : writeBurnEntries) @@ -2867,62 +2834,6 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl } mnview.SetLastHeight(pindex->nHeight); - if (IsEVMEnabled(pindex->nHeight, mnview)) { - CKeyID minter; - assert(block.ExtractMinterKey(minter)); - std::array beneficiary{}; - CScript minerAddress; - - if (!fMockNetwork) { - const auto id = mnview.GetMasternodeIdByOperator(minter); - assert(id); - const auto node = mnview.GetMasternode(*id); - assert(node); - - auto height = node->creationHeight; - auto mnID = *id; - if (!node->collateralTx.IsNull()) { - const auto idHeight = mnview.GetNewCollateral(node->collateralTx); - assert(idHeight); - height = idHeight->blockHeight - GetMnResignDelay(std::numeric_limits::max()); - mnID = node->collateralTx; - } - - const auto blockindex = ::ChainActive()[height]; - assert(blockindex); - - CTransactionRef tx; - uint256 hash_block; - assert(GetTransaction(mnID, tx, Params().GetConsensus(), hash_block, blockindex)); - assert(tx->vout.size() >= 2); - - CTxDestination dest; - assert(ExtractDestination(tx->vout[1].scriptPubKey, dest)); - assert(dest.index() == PKHashType || dest.index() == WitV0KeyHashType); - - const auto keyID = dest.index() == PKHashType ? CKeyID(std::get(dest)) : CKeyID(std::get(dest)); - std::copy(keyID.begin(), keyID.end(), beneficiary.begin()); - minerAddress = GetScriptForDestination(dest); - } else { - std::copy(minter.begin(), minter.end(), beneficiary.begin()); - const auto dest = PKHash(minter); - minerAddress = GetScriptForDestination(dest); - } - - const auto blockResult = evm_finalize(evmContext, true, block.nBits, beneficiary, block.GetBlockTime()); - - if (!blockResult.failed_transactions.empty()) { - std::vector failedTransactions; - for (const auto& rust_string : blockResult.failed_transactions) { - failedTransactions.emplace_back(rust_string.data(), rust_string.length()); - } - - RevertFailedTransferDomainTxs(failedTransactions, block, chainparams.GetConsensus(), pindex->nHeight, mnview); - } - - mnview.AddBalance(minerAddress, {DCT_ID{}, static_cast(blockResult.miner_fee)}); - } - auto &checkpoints = chainparams.Checkpoints().mapCheckpoints; auto it = checkpoints.lower_bound(pindex->nHeight); if (it != checkpoints.begin()) { diff --git a/test/functional/feature_evm.py b/test/functional/feature_evm.py index bcff856e89..cce450a317 100755 --- a/test/functional/feature_evm.py +++ b/test/functional/feature_evm.py @@ -144,6 +144,13 @@ def run_test(self): # Try and send a TX with a high nonce assert_raises_rpc_error(-32600, "evm tx failed to validate", self.nodes[0].evmtx, ethAddress, 1, 21, 21000, to_address, 1) + # Check Eth balances before transfer + assert_equal(int(self.nodes[0].eth_getBalance(ethAddress)[2:], 16), 10000000000000000000) + assert_equal(int(self.nodes[0].eth_getBalance(to_address)[2:], 16), 0) + + # Get miner DFI balance before transaction + miner_before = Decimal(self.nodes[0].getaccount(self.nodes[0].get_genesis_keys().ownerAuthAddress)[0].split('@')[0]) + # Test EVM Tx tx = self.nodes[0].evmtx(ethAddress, 0, 21, 21000, to_address, 1) raw_tx = self.nodes[0].getrawtransaction(tx) @@ -171,6 +178,14 @@ def run_test(self): assert_equal(self.nodes[1].getrawmempool(), [tx]) self.nodes[0].generate(1) + # Check Eth balances before transfer + assert_equal(int(self.nodes[0].eth_getBalance(ethAddress)[2:], 16), 9000000000000000000) + assert_equal(int(self.nodes[0].eth_getBalance(to_address)[2:], 16), 1000000000000000000) + + # Check miner account balance after transfer + miner_after = Decimal(self.nodes[0].getaccount(self.nodes[0].get_genesis_keys().ownerAuthAddress)[0].split('@')[0]) + miner_fee = miner_after - miner_before + # Check EVM Tx is in block block = self.nodes[0].getblock(self.nodes[0].getblockhash(self.nodes[0].getblockcount())) assert_equal(block['tx'][1], tx) @@ -185,16 +200,26 @@ def run_test(self): # Try and send EVM TX a second time assert_raises_rpc_error(-26, "evm tx failed to validate", self.nodes[0].sendrawtransaction, raw_tx) - # Check EVM blockhash and miner fee shown + # Check EVM blockhash eth_block = self.nodes[0].eth_getBlockByNumber('latest') eth_hash = eth_block['hash'][2:] eth_fee = eth_block['gasUsed'][2:] block = self.nodes[0].getblock(self.nodes[0].getblockhash(self.nodes[0].getblockcount())) raw_tx = self.nodes[0].getrawtransaction(block['tx'][0], 1) block_hash = raw_tx['vout'][1]['scriptPubKey']['hex'][4:68] - fee_amount = raw_tx['vout'][1]['scriptPubKey']['hex'][68:] assert_equal(block_hash, eth_hash) - assert_equal(fee_amount[2:4] + fee_amount[0:2], eth_fee) + + # Check EVM miner fee + opreturn_fee_amount = raw_tx['vout'][1]['scriptPubKey']['hex'][68:] + opreturn_fee_sats = Decimal(int(opreturn_fee_amount[2:4] + opreturn_fee_amount[0:2], 16)) / 100000000 + eth_fee_sats = Decimal(int(eth_fee, 16)) / 1000000000 + assert_equal(opreturn_fee_sats, eth_fee_sats) + assert_equal(opreturn_fee_sats, miner_fee) + + # Test rollback of EVM TX + self.nodes[0].invalidateblock(self.nodes[0].getblockhash(self.nodes[0].getblockcount())) + miner_rollback = Decimal(self.nodes[0].getaccount(self.nodes[0].get_genesis_keys().ownerAuthAddress)[0].split('@')[0]) + assert_equal(miner_before, miner_rollback) # Test rollback of EVM related TXs self.nodes[0].invalidateblock(self.nodes[0].getblockhash(101))