Skip to content

Commit

Permalink
Include EVM mnview in rollback. Convert miner fee to Sats. (#1999)
Browse files Browse the repository at this point in the history
* Convert miner fee to Sats

* Move evm_finalize to ProcessDefi and include mnview changes in rollback
  • Loading branch information
Bushstar authored Jun 7, 2023
1 parent 3208586 commit bba09e5
Show file tree
Hide file tree
Showing 8 changed files with 132 additions and 100 deletions.
2 changes: 1 addition & 1 deletion src/amount.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
4 changes: 2 additions & 2 deletions src/masternodes/mn_checks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}
}
Expand Down Expand Up @@ -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));
}
Expand Down
2 changes: 1 addition & 1 deletion src/masternodes/rpc_evm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<uint8_t> input{};
if (!request.params[6].isNull()) {
Expand Down
98 changes: 97 additions & 1 deletion src/masternodes/validation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 <ain_rs_exports.h>
#include <chain.h>
#include <masternodes/accountshistory.h>
#include <masternodes/govvariables/attributes.h>
Expand Down Expand Up @@ -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<std::string> &failedTransactions, const CBlock& block, const Consensus::Params &consensus, const int height, CCustomCSView &mnview) {
std::set<uint256> potentialTxsToUndo;
for (const auto &txStr : failedTransactions) {
potentialTxsToUndo.insert(uint256S(txStr));
}

std::set<uint256> txsToUndo;
for (const auto &tx : block.vtx) {
if (tx && potentialTxsToUndo.count(tx->GetHash())) {
std::vector<unsigned char> 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<CTransferDomainMessage>(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<uint8_t, 20> 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<int>::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<PKHash>(dest)) : CKeyID(std::get<WitnessV0KeyHash>(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<std::string> 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<CAmount>(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
Expand Down Expand Up @@ -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());
Expand Down
2 changes: 1 addition & 1 deletion src/masternodes/validation.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class CCustomCSView;

using CreationTxs = std::map<uint32_t, std::pair<uint256, std::vector<std::pair<DCT_ID, uint256>>>>;

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<CAuctionBatch> CollectAuctionBatches(const CVaultAssets& vaultAssets, const TAmounts& collBalances, const TAmounts& loanBalances);


Expand Down
2 changes: 1 addition & 1 deletion src/miner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc

const auto blockHash = std::vector<uint8_t>(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<std::string> failedTransactions;
for (const auto& rust_string : blockResult.failed_transactions) {
Expand Down
91 changes: 1 addition & 90 deletions src/validation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2012,39 +2012,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<std::string> &failedTransactions, const CBlock& block, const Consensus::Params &consensus, const int height, CCustomCSView &mnview) {
std::set<uint256> potentialTxsToUndo;
for (const auto &txStr : failedTransactions) {
potentialTxsToUndo.insert(uint256S(txStr));
}

std::set<uint256> txsToUndo;
for (const auto &tx : block.vtx) {
if (tx && potentialTxsToUndo.count(tx->GetHash())) {
std::vector<unsigned char> 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<CTransferDomainMessage>(txMessage);
RevertTransferDomain(obj, mnview);
}
}
}
}

Res ApplyGeneralCoinbaseTx(CCustomCSView & mnview, CTransaction const & tx, int height, CAmount nFees, const Consensus::Params& consensus)
{
TAmounts const cbValues = tx.GetValuesOut();
Expand Down Expand Up @@ -2851,7 +2818,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)
Expand All @@ -2871,62 +2838,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<uint8_t, 20> 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<int>::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<PKHash>(dest)) : CKeyID(std::get<WitnessV0KeyHash>(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<std::string> 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<CAmount>(blockResult.miner_fee)});
}

auto &checkpoints = chainparams.Checkpoints().mapCheckpoints;
auto it = checkpoints.lower_bound(pindex->nHeight);
if (it != checkpoints.begin()) {
Expand Down
31 changes: 28 additions & 3 deletions test/functional/feature_evm.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,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)
Expand Down Expand Up @@ -180,6 +187,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)
Expand All @@ -194,16 +209,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))
Expand Down

0 comments on commit bba09e5

Please sign in to comment.