Skip to content

Commit

Permalink
Add ICX feature flag. Fix bug on mainnet. (#2059)
Browse files Browse the repository at this point in the history
* Add ICX feature flag. Fix bug on mainnet.

* Remove free standing errors. Pass local consensus instead of taking global state.

* Fix compile warnings

---------

Co-authored-by: Prasanna Loganathar <[email protected]>
  • Loading branch information
Mixa84 and prasannavl authored Jun 19, 2023
1 parent 1248c50 commit 7c9f749
Show file tree
Hide file tree
Showing 10 changed files with 274 additions and 79 deletions.
4 changes: 4 additions & 0 deletions src/masternodes/errors.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ class DeFiErrors {
return Res::Err("Masternode %s is not in 'ENABLED' state", nodeRefString);
}

static Res ICXDisabled() {
return Res::Err("Cannot create tx, ICX is not enabled");
}

static Res ICXBTCBelowMinSwap(const CAmount amount, const CAmount minSwap) {
// TODO: Change error in later version to include amount. Retaining old msg for compatibility
return Res::Err("Below minimum swapable amount, must be at least %s BTC", GetDecimalString(minSwap));
Expand Down
5 changes: 4 additions & 1 deletion src/masternodes/govvariables/attributes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ const std::map<uint8_t, std::map<std::string, uint8_t>> &ATTRIBUTES::allowedKeys
{"gov", DFIPKeys::GovernanceEnabled},
{"consortium", DFIPKeys::ConsortiumEnabled},
{"evm", DFIPKeys::EVMEnabled},
{"icx", DFIPKeys::ICXEnabled},
{"members", DFIPKeys::Members},
{"gov-payout", DFIPKeys::CFPPayout},
{"emission-unused-fund", DFIPKeys::EmissionUnusedFund},
Expand Down Expand Up @@ -267,6 +268,7 @@ const std::map<uint8_t, std::map<uint8_t, std::string>> &ATTRIBUTES::displayKeys
{DFIPKeys::GovernanceEnabled, "gov"},
{DFIPKeys::ConsortiumEnabled, "consortium"},
{DFIPKeys::EVMEnabled, "evm"},
{DFIPKeys::ICXEnabled, "icx"},
{DFIPKeys::Members, "members"},
{DFIPKeys::CFPPayout, "gov-payout"},
{DFIPKeys::EmissionUnusedFund, "emission-unused-fund"},
Expand Down Expand Up @@ -595,6 +597,7 @@ const std::map<uint8_t, std::map<uint8_t, std::function<ResVal<CAttributeValue>(
{DFIPKeys::GovernanceEnabled, VerifyBool},
{DFIPKeys::ConsortiumEnabled, VerifyBool},
{DFIPKeys::EVMEnabled, VerifyBool},
{DFIPKeys::ICXEnabled, VerifyBool},
{DFIPKeys::CFPPayout, VerifyBool},
{DFIPKeys::EmissionUnusedFund, VerifyBool},
{DFIPKeys::MintTokens, VerifyBool},
Expand Down Expand Up @@ -808,7 +811,7 @@ Res ATTRIBUTES::ProcessVariable(const std::string &key,
typeKey != DFIPKeys::MNSetOwnerAddress && typeKey != DFIPKeys::GovernanceEnabled &&
typeKey != DFIPKeys::ConsortiumEnabled && typeKey != DFIPKeys::CFPPayout &&
typeKey != DFIPKeys::EmissionUnusedFund && typeKey != DFIPKeys::MintTokens &&
typeKey != DFIPKeys::EVMEnabled) {
typeKey != DFIPKeys::EVMEnabled && typeKey != DFIPKeys::ICXEnabled) {
return DeFiErrors::GovVarVariableUnsupportedFeatureType(typeKey);
}
} else if (typeId == ParamIDs::Foundation) {
Expand Down
1 change: 1 addition & 0 deletions src/masternodes/govvariables/attributes.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ enum DFIPKeys : uint8_t {
EmissionUnusedFund = 's',
MintTokens = 't',
EVMEnabled = 'u',
ICXEnabled = 'v',
};

enum GovernanceKeys : uint8_t {
Expand Down
146 changes: 86 additions & 60 deletions src/masternodes/mn_checks.cpp

Large diffs are not rendered by default.

10 changes: 7 additions & 3 deletions src/masternodes/mn_checks.h
Original file line number Diff line number Diff line change
Expand Up @@ -515,10 +515,14 @@ Res SwapToDFIorDUSD(CCustomCSView &mnview,
const CScript &from,
const CScript &to,
uint32_t height,
const Consensus::Params &consensus,
bool forceLoanSwap = false);
Res storeGovVars(const CGovernanceHeightMessage &obj, CCustomCSView &view);
bool IsRegtestNetwork();
bool IsTestNetwork();
bool IsEVMEnabled(const int height, const CCustomCSView &view);
bool IsMainNetwork();
bool IsICXEnabled(const int height, const CCustomCSView &view, const Consensus::Params &consensus);
bool IsEVMEnabled(const int height, const CCustomCSView &view, const Consensus::Params &consensus);
Res HasAuth(const CTransaction &tx, const CCoinsViewCache &coins, const CScript &auth, AuthStrategy strategy = AuthStrategy::DirectPubKeyMatch);
Res ValidateTransferDomain(const CTransaction &tx,
uint32_t height,
Expand Down Expand Up @@ -639,8 +643,8 @@ class CPoolSwap {
: obj(obj),
height(height) {}

std::vector<DCT_ID> CalculateSwaps(CCustomCSView &view, bool testOnly = false);
Res ExecuteSwap(CCustomCSView &view, std::vector<DCT_ID> poolIDs, bool testOnly = false);
std::vector<DCT_ID> CalculateSwaps(CCustomCSView &view, const Consensus::Params &consensus, bool testOnly = false);
Res ExecuteSwap(CCustomCSView &view, std::vector<DCT_ID> poolIDs, const Consensus::Params &consensus, bool testOnly = false);
std::vector<std::vector<DCT_ID>> CalculatePoolPaths(CCustomCSView &view);
CTokenAmount GetResult() { return CTokenAmount{obj.idTokenTo, result}; };
};
Expand Down
8 changes: 5 additions & 3 deletions src/masternodes/rpc_poolpair.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1071,7 +1071,7 @@ UniValue compositeswap(const JSONRPCRequest &request) {
auto directPool = pcustomcsview->GetPoolPair(poolSwapMsg.idTokenFrom, poolSwapMsg.idTokenTo);
if (!directPool || !directPool->second.status) {
auto compositeSwap = CPoolSwap(poolSwapMsg, targetHeight);
poolSwapMsgV2.poolIDs = compositeSwap.CalculateSwaps(*pcustomcsview);
poolSwapMsgV2.poolIDs = compositeSwap.CalculateSwaps(*pcustomcsview, Params().GetConsensus());

// No composite or direct pools found
if (poolSwapMsgV2.poolIDs.empty()) {
Expand Down Expand Up @@ -1199,6 +1199,8 @@ UniValue testpoolswap(const JSONRPCRequest &request) {
CPoolSwapMessage poolSwapMsg{};
CheckAndFillPoolSwapMessage(request, poolSwapMsg);

const Consensus::Params &consensus = Params().GetConsensus();

// test execution and get amount
Res res = Res::Ok();
{
Expand All @@ -1221,7 +1223,7 @@ UniValue testpoolswap(const JSONRPCRequest &request) {

poolIds.push_back(poolPair->first);
} else if (path == "auto" || path == "composite") {
poolIds = poolSwap.CalculateSwaps(mnview_dummy, true);
poolIds = poolSwap.CalculateSwaps(mnview_dummy, consensus,true);
} else {
path = "custom";

Expand All @@ -1237,7 +1239,7 @@ UniValue testpoolswap(const JSONRPCRequest &request) {
}
}

res = poolSwap.ExecuteSwap(mnview_dummy, poolIds, true);
res = poolSwap.ExecuteSwap(mnview_dummy, poolIds, consensus, true);
if (!res) {
std::string errorMsg{"Cannot find usable pool pair."};
if (!poolSwap.errors.empty()) {
Expand Down
6 changes: 3 additions & 3 deletions src/masternodes/validation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -708,7 +708,7 @@ static void ProcessLoanEvents(const CBlockIndex* pindex, CCustomCSView& cache, c
CScript tmpAddress(vaultId.begin(), vaultId.end());
view.AddBalance(tmpAddress, {bidTokenAmount.nTokenId, amountToBurn});
SwapToDFIorDUSD(view, bidTokenAmount.nTokenId, amountToBurn, tmpAddress,
chainparams.GetConsensus().burnAddress, pindex->nHeight);
chainparams.GetConsensus().burnAddress, pindex->nHeight, chainparams.GetConsensus());
}

view.CalculateOwnerRewards(bidOwner, pindex->nHeight);
Expand All @@ -725,7 +725,7 @@ static void ProcessLoanEvents(const CBlockIndex* pindex, CCustomCSView& cache, c
CScript tmpAddress(vaultId.begin(), vaultId.end());
view.AddBalance(tmpAddress, {bidTokenAmount.nTokenId, amountToFill});

SwapToDFIorDUSD(view, bidTokenAmount.nTokenId, amountToFill, tmpAddress, tmpAddress, pindex->nHeight);
SwapToDFIorDUSD(view, bidTokenAmount.nTokenId, amountToFill, tmpAddress, tmpAddress, pindex->nHeight, chainparams.GetConsensus());
auto amount = view.GetBalance(tmpAddress, DCT_ID{0});
view.SubBalance(tmpAddress, amount);
view.AddVaultCollateral(vaultId, amount);
Expand Down Expand Up @@ -2383,7 +2383,7 @@ static void RevertFailedTransferDomainTxs(const std::vector<std::string> &failed

static void ProcessEVMQueue(const CBlock &block, const CBlockIndex *pindex, CCustomCSView &cache, const CChainParams& chainparams, const uint64_t evmContext, std::array<uint8_t, 20>& beneficiary) {

if (IsEVMEnabled(pindex->nHeight, cache)) {
if (IsEVMEnabled(pindex->nHeight, cache, chainparams.GetConsensus())) {
CKeyID minter;
assert(block.ExtractMinterKey(minter));
CScript minerAddress;
Expand Down
4 changes: 2 additions & 2 deletions src/miner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc
}

XVM xvm{};
if (IsEVMEnabled(nHeight, mnview)) {
if (IsEVMEnabled(nHeight, mnview, consensus)) {
std::array<uint8_t, 20> beneficiary{};
std::copy(nodePtr->ownerAuthAddress.begin(), nodePtr->ownerAuthAddress.end(), beneficiary.begin());
auto blockResult = evm_finalize(evmContext, false, pos::GetNextWorkRequired(pindexPrev, pblock->nTime, consensus), beneficiary, blockTime);
Expand Down Expand Up @@ -364,7 +364,7 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc
coinbaseTx.vout[0].nValue = CalculateCoinbaseReward(blockReward, consensus.dist.masternode);
}

if (IsEVMEnabled(nHeight, mnview) && !xvm.evm.blockHash.IsNull()) {
if (IsEVMEnabled(nHeight, mnview, consensus) && !xvm.evm.blockHash.IsNull()) {
const auto headerIndex = coinbaseTx.vout.size();
coinbaseTx.vout.resize(headerIndex + 1);
coinbaseTx.vout[headerIndex].nValue = 0;
Expand Down
2 changes: 1 addition & 1 deletion src/validation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3308,7 +3308,7 @@ bool CChainState::ConnectTip(CValidationState& state, const CChainParams& chainp
}
nTime3 = GetTimeMicros(); nTimeConnectTotal += nTime3 - nTime2;
LogPrint(BCLog::BENCH, " - Connect total: %.2fms [%.2fs (%.2fms/blk)]\n", (nTime3 - nTime2) * MILLI, nTimeConnectTotal * MICRO, nTimeConnectTotal * MILLI / nBlocksTotal);
if (IsEVMEnabled(pindexNew->nHeight, mnview)) {
if (IsEVMEnabled(pindexNew->nHeight, mnview, chainparams.GetConsensus())) {
evm_finalize(evmContext, true, blockConnecting.nBits, beneficiary, blockConnecting.GetBlockTime());
}
bool flushed = view.Flush() && mnview.Flush();
Expand Down
167 changes: 161 additions & 6 deletions test/functional/feature_icx_orderbook.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@

from test_framework.test_framework import DefiTestFramework

from test_framework.util import assert_equal
from test_framework.util import (
assert_equal,
assert_raises_rpc_error
)

from decimal import Decimal

Expand All @@ -17,11 +20,9 @@ def set_test_params(self):
self.num_nodes = 2
self.setup_clean_chain = True
self.extra_args = [
['-txnotokens=0', '-amkheight=50', '-bayfrontheight=50', '-eunosheight=50', '-eunospayaheight=50',
'-txindex=1'],
['-txnotokens=0', '-amkheight=50', '-bayfrontheight=50', '-eunosheight=50', '-eunospayaheight=50',
'-txindex=1']]

['-dummypos=0', '-txnotokens=0', '-amkheight=50', '-bayfrontheight=50', '-eunosheight=50', '-eunospayaheight=50', '-fortcanningheight=82', '-fortcanninghillheight=84', '-fortcanningroadheight=86', '-fortcanningcrunchheight=88', '-fortcanningspringheight=90', '-fortcanninggreatworldheight=94', '-fortcanningepilogueheight=96', '-grandcentralheight=101', '-nextnetworkupgradeheight=3505', '-txindex=1'],
['-dummypos=0', '-txnotokens=0', '-amkheight=50', '-bayfrontheight=50', '-eunosheight=50', '-eunospayaheight=50', '-fortcanningheight=82', '-fortcanninghillheight=84', '-fortcanningroadheight=86', '-fortcanningcrunchheight=88', '-fortcanningspringheight=90', '-fortcanninggreatworldheight=94', '-fortcanningepilogueheight=96', '-grandcentralheight=101', '-nextnetworkupgradeheight=3505', '-txindex=1']
]
def run_test(self):
assert_equal(len(self.nodes[0].listtokens()), 1) # only one token == DFI

Expand Down Expand Up @@ -1207,6 +1208,160 @@ def run_test(self):
"0.01125")) # 0.045 lock refund + 0.25 * 0.045 taker fee refund
assert_equal(self.nodes[1].getaccount(accountBTC, {}, True)[idDFI], beforeClaim1) # no refunds for taker

self.nodes[1].generate(1)
self.sync_blocks()

assert_raises_rpc_error(-32600, "Cannot create tx, ICX is not enabled", self.nodes[0].icx_createorder, {
'tokenFrom': idDFI,
'chainTo': "BTC",
'ownerAddress': accountDFI,
'receivePubkey': '037f9563f30c609b19fd435a19b8bde7d6db703012ba1aba72e9f42a87366d1941',
'amountFrom': 15,
'orderPrice': 0.01})

assert_raises_rpc_error(-32600, "Cannot create tx, ICX is not enabled", self.nodes[1].icx_makeoffer, {
'orderTx': orderTx,
'amount': 0.10,
'ownerAddress': accountBTC})

# Activate ICX
self.nodes[0].setgov({"ATTRIBUTES": {'v0/params/feature/icx': 'true'}})
self.nodes[0].generate(1)
self.sync_blocks()

# DFI/BTC
orderTx = self.nodes[0].icx_createorder({
'tokenFrom': idDFI,
'chainTo': "BTC",
'ownerAddress': accountDFI,
'receivePubkey': '037f9563f30c609b19fd435a19b8bde7d6db703012ba1aba72e9f42a87366d1941',
'amountFrom': 15,
'orderPrice': 0.01})["txid"]

self.nodes[0].generate(1)
self.sync_blocks()

beforeFee = self.nodes[0].getaccount(accountDFI, {}, True)[idDFI]
beforeFee1 = self.nodes[1].getaccount(accountBTC, {}, True)[idDFI]

offerTx = self.nodes[1].icx_makeoffer({
'orderTx': orderTx,
'amount': 0.10,
'ownerAddress': accountBTC})["txid"]

self.nodes[1].generate(1)
self.sync_blocks()

# Check fee burn
assert_equal(self.nodes[1].getaccount(accountBTC, {}, True)[idDFI], beforeFee1 - Decimal('0.01'))

dfchtlcTx = self.nodes[0].icx_submitdfchtlc({
'offerTx': offerTx,
'amount': 10,
'hash': '957fc0fd643f605b2938e0631a61529fd70bd35b2162a21d978c41e5241a5220'})["txid"]

self.nodes[0].generate(1)
self.sync_blocks()

# Check fee burn
assert_equal(self.nodes[0].getaccount(accountDFI, {}, True)[idDFI], beforeFee - Decimal('0.01'))

exthtlcTx = self.nodes[1].icx_submitexthtlc({
'offerTx': offerTx,
'amount': 0.10,
'hash': '957fc0fd643f605b2938e0631a61529fd70bd35b2162a21d978c41e5241a5220',
'htlcScriptAddress': '13sJQ9wBWh8ssihHUgAaCmNWJbBAG5Hr9N',
'ownerPubkey': '036494e7c9467c8c7ff3bf29e841907fb0fa24241866569944ea422479ec0e6252',
'timeout': 24})["txid"]

self.nodes[1].generate(1)
self.sync_blocks()

beforeClaim0DFI = self.nodes[0].getaccount(accountDFI, {}, True)[idDFI]
beforeClaim0BTC = self.nodes[0].getaccount(accountDFI, {}, True)[idBTC]
beforeClaim1DFI = self.nodes[1].getaccount(accountBTC, {}, True)[idDFI]
beforeClaim1BTC = self.nodes[1].getaccount(accountBTC, {}, True)[idBTC]

claimTx = self.nodes[1].icx_claimdfchtlc({
'dfchtlcTx': dfchtlcTx,
'seed': 'f75a61ad8f7a6e0ab701d5be1f5d4523a9b534571e4e92e0c4610c6a6784ccef'})["txid"]

self.nodes[1].generate(1)
self.sync_blocks()

# makerIncentive + makerDeposit
assert_equal(self.nodes[0].getaccount(accountDFI, {}, True)[idDFI], beforeClaim0DFI + Decimal('0.0125'))
assert_equal(self.nodes[0].getaccount(accountDFI, {}, True)[idBTC], beforeClaim0BTC)
# claimed DFI on taker address
assert_equal(self.nodes[1].getaccount(accountBTC, {}, True)[idDFI], beforeClaim1DFI + Decimal('10.00000000'))
assert_equal(self.nodes[1].getaccount(accountBTC, {}, True)[idBTC], beforeClaim1BTC)

# dBTC/BTC
orderTx = self.nodes[0].icx_createorder({
'tokenFrom': idBTC,
'chainTo': "BTC",
'ownerAddress': accountDFI,
'receivePubkey': '037f9563f30c609b19fd435a19b8bde7d6db703012ba1aba72e9f42a87366d1941',
'amountFrom': 1,
'orderPrice': 1})["txid"]

self.nodes[0].generate(1)
self.sync_blocks()

beforeFee = self.nodes[0].getaccount(accountDFI, {}, True)[idDFI]
beforeFee1 = self.nodes[1].getaccount(accountBTC, {}, True)[idDFI]

offerTx = self.nodes[1].icx_makeoffer({
'orderTx': orderTx,
'amount': 1,
'ownerAddress': accountBTC})["txid"]

self.nodes[1].generate(1)
self.sync_blocks()


dfchtlcTx = self.nodes[0].icx_submitdfchtlc({
'offerTx': offerTx,
'amount': 1,
'hash': '957fc0fd643f605b2938e0631a61529fd70bd35b2162a21d978c41e5241a5220'})["txid"]

self.nodes[0].generate(1)
self.sync_blocks()

exthtlcTx = self.nodes[1].icx_submitexthtlc({
'offerTx': offerTx,
'amount': 1,
'hash': '957fc0fd643f605b2938e0631a61529fd70bd35b2162a21d978c41e5241a5220',
'htlcScriptAddress': '13sJQ9wBWh8ssihHUgAaCmNWJbBAG5Hr9N',
'ownerPubkey': '036494e7c9467c8c7ff3bf29e841907fb0fa24241866569944ea422479ec0e6252',
'timeout': 24})["txid"]

self.nodes[1].generate(1)
self.sync_blocks()

# Check fee burn
assert_equal(self.nodes[1].getaccount(accountBTC, {}, True)[idDFI], beforeFee1 - Decimal('0.1'))
assert_equal(self.nodes[0].getaccount(accountDFI, {}, True)[idDFI], beforeFee - Decimal('0.1'))

beforeClaim0DFI = self.nodes[0].getaccount(accountDFI, {}, True)[idDFI]
self.nodes[0].getaccount(accountDFI, {}, True)[idDFIBTC]
assert_equal(len(self.nodes[0].getaccount(accountDFI, {}, True)), 2)
beforeClaim1DFI = self.nodes[1].getaccount(accountBTC, {}, True)[idDFI]
beforeClaim1BTC = self.nodes[1].getaccount(accountBTC, {}, True)[idBTC]

claimTx = self.nodes[1].icx_claimdfchtlc({
'dfchtlcTx': dfchtlcTx,
'seed': 'f75a61ad8f7a6e0ab701d5be1f5d4523a9b534571e4e92e0c4610c6a6784ccef'})["txid"]

self.nodes[1].generate(1)
self.sync_blocks()

# makerIncentive + makerBonus + makerDeposit
assert_equal(self.nodes[0].getaccount(accountDFI, {}, True)[idDFI], beforeClaim0DFI + Decimal('0.175'))
assert_equal(len(self.nodes[0].getaccount(accountDFI, {}, True)), 2)
# claimed DFI on taker address
assert_equal(self.nodes[1].getaccount(accountBTC, {}, True)[idDFI], beforeClaim1DFI)
assert_equal(self.nodes[1].getaccount(accountBTC, {}, True)[idBTC], beforeClaim1BTC + Decimal('1.00000000'))

if __name__ == '__main__':
ICXOrderbookTest().main()

0 comments on commit 7c9f749

Please sign in to comment.