Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Include EVM mnview in rollback. Convert miner fee to Sats. #1999

Merged
merged 2 commits into from
Jun 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -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<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 @@ -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)
Expand All @@ -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<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 @@ -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)
Expand Down Expand Up @@ -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)
Expand All @@ -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))
Expand Down