diff --git a/src/masternodes/icxorder.cpp b/src/masternodes/icxorder.cpp index 2bd014ffe88..4be88e04c19 100644 --- a/src/masternodes/icxorder.cpp +++ b/src/masternodes/icxorder.cpp @@ -295,6 +295,21 @@ std::unique_ptr CICXOrderView::HasICXSubmi return {}; } +bool CICXOrderView::ExistedICXSubmitDFCHTLC(uint256 const & offertxid, bool isPreEunosPaya) +{ + bool result = false; + + if (HasICXSubmitDFCHTLCOpen(offertxid)) + result = true; + if (isPreEunosPaya) + return (result); + auto it = LowerBound(TxidPairKey{offertxid, {}}); + if (it.Valid() && it.Key().first == offertxid) + result = true; + + return (result); +} + std::unique_ptr CICXOrderView::GetICXSubmitEXTHTLCByCreationTx(const uint256 & txid) const { auto submitexthtlc = ReadBy(txid); @@ -358,6 +373,21 @@ std::unique_ptr CICXOrderView::HasICXSubmi return {}; } +bool CICXOrderView::ExistedICXSubmitEXTHTLC(uint256 const & offertxid, bool isPreEunosPaya) +{ + bool result = false; + + if (HasICXSubmitEXTHTLCOpen(offertxid)) + result = true; + if (isPreEunosPaya) + return (result); + auto it = LowerBound(TxidPairKey{offertxid, {}}); + if (it.Valid() && it.Key().first == offertxid) + result = true; + + return (result); +} + std::unique_ptr CICXOrderView::GetICXClaimDFCHTLCByCreationTx(uint256 const & txid) const { auto claimdfchtlc = ReadBy(txid); diff --git a/src/masternodes/icxorder.h b/src/masternodes/icxorder.h index 18f46b6c233..883d54ad1fd 100644 --- a/src/masternodes/icxorder.h +++ b/src/masternodes/icxorder.h @@ -413,6 +413,8 @@ class CICXOrderView : public virtual CStorageView { void ForEachICXSubmitDFCHTLCClose(std::function callback, uint256 const & offertxid = uint256()); void ForEachICXSubmitDFCHTLCExpire(std::function callback, uint32_t const & height = 0); std::unique_ptr HasICXSubmitDFCHTLCOpen(uint256 const & offertxid); + bool ExistedICXSubmitDFCHTLC(uint256 const & offertxid, bool isPreEunosPaya); + //SubmitEXTHTLC std::unique_ptr GetICXSubmitEXTHTLCByCreationTx(uint256 const & txid) const; @@ -422,6 +424,7 @@ class CICXOrderView : public virtual CStorageView { void ForEachICXSubmitEXTHTLCClose(std::function callback, uint256 const & offertxid = uint256()); void ForEachICXSubmitEXTHTLCExpire(std::function callback, uint32_t const & height = 0); std::unique_ptr HasICXSubmitEXTHTLCOpen(uint256 const & offertxid); + bool ExistedICXSubmitEXTHTLC(uint256 const & offertxid, bool isPreEunosPaya); //ClaimDFCHTLC std::unique_ptr GetICXClaimDFCHTLCByCreationTx(uint256 const & txid) const; diff --git a/src/masternodes/mn_checks.cpp b/src/masternodes/mn_checks.cpp index 9deb32b7858..c51b24a18aa 100644 --- a/src/masternodes/mn_checks.cpp +++ b/src/masternodes/mn_checks.cpp @@ -172,6 +172,13 @@ class CCustomMetadataParseVisitor : public boost::static_visitor return Res::Ok(); } + Res isPostEunosPayaFork() const { + if(static_cast(height) < consensus.EunosPayaHeight) { + return Res::Err("called before EunosPaya height"); + } + return Res::Ok(); + } + template Res serialize(T& obj) const { CDataStream ss(metadata, SER_NETWORK, PROTOCOL_VERSION); @@ -448,9 +455,12 @@ class CCustomTxVisitor : public boost::static_visitor } Res CheckICXTx() const { - if (tx.vout.size() != 2) { + if (static_cast(height) < consensus.EunosPayaHeight && tx.vout.size() != 2) { return Res::Err("malformed tx vouts ((wrong number of vouts)"); } + if (static_cast(height) >= consensus.EunosPayaHeight && tx.vout[0].nValue != 0) { + return Res::Err("malformed tx vouts, first vout must be OP_RETURN vout with value 0"); + } return Res::Ok(); } @@ -1259,15 +1269,27 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor srcAddr = CScript(order->creationTx.begin(), order->creationTx.end()); + CScript offerTxidAddr(offer->creationTx.begin(), offer->creationTx.end()); + CAmount calcAmount(static_cast((arith_uint256(submitdfchtlc.amount) * arith_uint256(order->orderPrice) / arith_uint256(COIN)).GetLow64())); if (calcAmount > offer->amount) return Res::Err("amount must be lower or equal the offer one"); - CScript offerTxidAddr(offer->creationTx.begin(), offer->creationTx.end()); - - //calculating adjusted takerFee - CAmount BTCAmount(static_cast((arith_uint256(submitdfchtlc.amount) * arith_uint256(order->orderPrice) / arith_uint256(COIN)).GetLow64())); - auto takerFee = CalculateTakerFee(BTCAmount); + CAmount takerFee = offer->takerFee; + //EunosPaya: calculating adjusted takerFee only if amount in htlc different than in offer + if (static_cast(height) >= consensus.EunosPayaHeight) + { + if (calcAmount < offer->amount) + { + CAmount BTCAmount(static_cast((arith_uint256(submitdfchtlc.amount) * arith_uint256(order->orderPrice) / arith_uint256(COIN)).GetLow64())); + takerFee = static_cast((arith_uint256(BTCAmount) * arith_uint256(offer->takerFee) / arith_uint256(offer->amount)).GetLow64()); + } + } + else + { + CAmount BTCAmount(static_cast((arith_uint256(submitdfchtlc.amount) * arith_uint256(order->orderPrice) / arith_uint256(COIN)).GetLow64())); + takerFee = CalculateTakerFee(BTCAmount); + } // refund the rest of locked takerFee if there is difference if (offer->takerFee - takerFee) { @@ -1362,7 +1384,7 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor CAmount calcAmount(static_cast((arith_uint256(dfchtlc->amount) * arith_uint256(order->orderPrice) / arith_uint256(COIN)).GetLow64())); if (submitexthtlc.amount != calcAmount) - return Res::Err("amount %d must be equal to calculated dfchtlc amount %d", submitexthtlc.amount, calcAmount); + return Res::Err("amount must be equal to calculated dfchtlc amount"); if (submitexthtlc.hash != dfchtlc->hash) return Res::Err("Invalid hash, external htlc hash is different than dfc htlc hash"); @@ -1383,14 +1405,26 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor if (submitexthtlc.timeout < CICXSubmitEXTHTLC::MINIMUM_TIMEOUT) return Res::Err("timeout must be greater than %d", CICXSubmitEXTHTLC::MINIMUM_TIMEOUT - 1); + CScript offerTxidAddr(offer->creationTx.begin(), offer->creationTx.end()); + CAmount calcAmount(static_cast((arith_uint256(submitexthtlc.amount) * arith_uint256(order->orderPrice) / arith_uint256(COIN)).GetLow64())); if (calcAmount > offer->amount) return Res::Err("amount must be lower or equal the offer one"); - CScript offerTxidAddr(offer->creationTx.begin(), offer->creationTx.end()); - - //calculating adjusted takerFee - auto takerFee = CalculateTakerFee(submitexthtlc.amount); + CAmount takerFee = offer->takerFee; + //EunosPaya: calculating adjusted takerFee only if amount in htlc different than in offer + if (static_cast(height) >= consensus.EunosPayaHeight) + { + if (calcAmount < offer->amount) + { + CAmount BTCAmount(static_cast((arith_uint256(offer->amount) * arith_uint256(COIN) / arith_uint256(order->orderPrice)).GetLow64())); + takerFee = static_cast((arith_uint256(submitexthtlc.amount) * arith_uint256(offer->takerFee) / arith_uint256(BTCAmount)).GetLow64()); + } + } + else + { + takerFee = CalculateTakerFee(submitexthtlc.amount); + } // refund the rest of locked takerFee if there is difference if (offer->takerFee - takerFee) { @@ -1454,7 +1488,7 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor return Res::Err("order with creation tx %s does not exists!", offer->orderTx.GetHex()); auto exthtlc = mnview.HasICXSubmitEXTHTLCOpen(dfchtlc->offerTx); - if (!exthtlc) + if (static_cast(height) < consensus.EunosPayaHeight && !exthtlc) return Res::Err("cannot claim, external htlc for this offer does not exists or expired!"); // claim DFC HTLC to receiveAddress @@ -1513,7 +1547,15 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor if (!res) return res; - return mnview.ICXCloseEXTHTLC(*exthtlc, CICXSubmitEXTHTLC::STATUS_CLOSED); + if (static_cast(height) >= consensus.EunosPayaHeight) + { + if (exthtlc) + return mnview.ICXCloseEXTHTLC(*exthtlc, CICXSubmitEXTHTLC::STATUS_CLOSED); + else + return (Res::Ok()); + } + else + return mnview.ICXCloseEXTHTLC(*exthtlc, CICXSubmitEXTHTLC::STATUS_CLOSED); } Res operator()(const CICXCloseOrderMessage& obj) const { @@ -1589,7 +1631,10 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor offer->closeTx = closeoffer.creationTx; offer->closeHeight = closeoffer.creationHeight; - if (order->orderType == CICXOrder::TYPE_INTERNAL && !mnview.HasICXSubmitDFCHTLCOpen(offer->creationTx)) { + bool isPreEunosPaya = static_cast(height) < consensus.EunosPayaHeight; + + if (order->orderType == CICXOrder::TYPE_INTERNAL && !mnview.ExistedICXSubmitDFCHTLC(offer->creationTx, isPreEunosPaya)) + { // subtract takerFee from txidAddr and return to owner CScript txidAddr(offer->creationTx.begin(), offer->creationTx.end()); CalculateOwnerRewards(offer->ownerAddress); @@ -1601,10 +1646,13 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor // subtract the balance from txidAddr and return to owner CScript txidAddr(offer->creationTx.begin(), offer->creationTx.end()); CalculateOwnerRewards(offer->ownerAddress); - res = ICXTransfer(order->idToken, offer->amount, txidAddr, offer->ownerAddress); - if (!res) - return res; - if (!mnview.HasICXSubmitEXTHTLCOpen(offer->creationTx)) + if (isPreEunosPaya) + { + res = ICXTransfer(order->idToken, offer->amount, txidAddr, offer->ownerAddress); + if (!res) + return res; + } + if (!mnview.ExistedICXSubmitEXTHTLC(offer->creationTx, isPreEunosPaya)) { res = ICXTransfer(DCT_ID{0}, offer->takerFee, txidAddr, offer->ownerAddress); if (!res) diff --git a/src/masternodes/rpc_icxorderbook.cpp b/src/masternodes/rpc_icxorderbook.cpp index 6ac8cca2b29..6a1bd392b32 100644 --- a/src/masternodes/rpc_icxorderbook.cpp +++ b/src/masternodes/rpc_icxorderbook.cpp @@ -1196,7 +1196,7 @@ UniValue icxlistorders(const JSONRPCRequest& request) { size_t limit = 50; std::string tokenSymbol, chain; uint256 orderTxid; - bool closed = false; + bool closed = false, offers = false; RPCTypeCheck(request.params, {UniValue::VOBJ}, false); if (request.params.size() > 0) @@ -1204,7 +1204,11 @@ UniValue icxlistorders(const JSONRPCRequest& request) { UniValue byObj = request.params[0].get_obj(); if (!byObj["token"].isNull()) tokenSymbol = trim_ws(byObj["token"].getValStr()); if (!byObj["chain"].isNull()) chain = trim_ws(byObj["chain"].getValStr()); - if (!byObj["orderTx"].isNull()) orderTxid = uint256S(byObj["orderTx"].getValStr()); + if (!byObj["orderTx"].isNull()) + { + orderTxid = uint256S(byObj["orderTx"].getValStr()); + offers = true; + } if (!byObj["limit"].isNull()) limit = (size_t) byObj["limit"].get_int64(); if (!byObj["closed"].isNull()) closed = byObj["closed"].get_bool(); } @@ -1226,7 +1230,7 @@ UniValue icxlistorders(const JSONRPCRequest& request) { prefix = idToken; auto orderkeylambda = [&](CICXOrderView::OrderKey const & key, uint8_t status) { - if (key.first != prefix) + if (key.first != prefix || !limit) return (false); auto order = pcustomcsview->GetICXOrderByCreationTx(key.second); if (order) @@ -1234,7 +1238,7 @@ UniValue icxlistorders(const JSONRPCRequest& request) { ret.pushKVs(icxOrderToJSON(*order, status)); limit--; } - return limit != 0; + return true; }; if (closed) @@ -1244,10 +1248,10 @@ UniValue icxlistorders(const JSONRPCRequest& request) { return ret; } - else if (!orderTxid.IsNull()) + else if (offers) { auto offerkeylambda = [&](CICXOrderView::TxidPairKey const & key, uint8_t status) { - if (key.first != orderTxid) + if (key.first != orderTxid || !limit) return (false); auto offer = pcustomcsview->GetICXMakeOfferByCreationTx(key.second); if (offer) @@ -1255,7 +1259,7 @@ UniValue icxlistorders(const JSONRPCRequest& request) { ret.pushKVs(icxMakeOfferToJSON(*offer, status)); limit--; } - return limit != 0; + return true; }; if (closed) pcustomcsview->ForEachICXMakeOfferClose(offerkeylambda, orderTxid); @@ -1266,13 +1270,15 @@ UniValue icxlistorders(const JSONRPCRequest& request) { } auto orderlambda = [&](CICXOrderView::OrderKey const & key, uint8_t status) { + if (!limit) + return false; auto order = pcustomcsview->GetICXOrderByCreationTx(key.second); if (order) { ret.pushKVs(icxOrderToJSON(*order, status)); limit--; } - return limit != 0; + return true; }; if (closed) @@ -1291,8 +1297,7 @@ UniValue icxlisthtlcs(const JSONRPCRequest& request) { { {"offerTx",RPCArg::Type::STR, RPCArg::Optional::NO, "Offer txid for which to list all HTLCS"}, {"limit", RPCArg::Type::NUM, RPCArg::Optional::OMITTED, "Maximum number of orders to return (default: 20)"}, - {"refunded", RPCArg::Type::BOOL, RPCArg::Optional::OMITTED, "Display refunded HTLC (default: false)"}, - {"claimed", RPCArg::Type::BOOL, RPCArg::Optional::OMITTED, "Display claimed HTLCs (default: false)"}, + {"closed", RPCArg::Type::BOOL, RPCArg::Optional::OMITTED, "Display also claimed, expired and refunded HTLCs (default: false)"}, }, }, @@ -1333,7 +1338,7 @@ UniValue icxlisthtlcs(const JSONRPCRequest& request) { ret.pushKV("WARNING", "ICX and Atomic Swap are experimental features. You might end up losing your funds. USE IT AT YOUR OWN RISK."); auto dfchtlclambda = [&](CICXOrderView::TxidPairKey const & key, uint8_t status) { - if (key.first != offerTxid) + if (key.first != offerTxid || !limit) return false; auto dfchtlc = pcustomcsview->GetICXSubmitDFCHTLCByCreationTx(key.second); if (dfchtlc) @@ -1341,10 +1346,10 @@ UniValue icxlisthtlcs(const JSONRPCRequest& request) { ret.pushKVs(icxSubmitDFCHTLCToJSON(*dfchtlc,status)); limit--; } - return limit != 0; + return true; }; auto exthtlclambda = [&](CICXOrderView::TxidPairKey const & key, uint8_t status) { - if (key.first != offerTxid) + if (key.first != offerTxid || !limit) return false; auto exthtlc = pcustomcsview->GetICXSubmitEXTHTLCByCreationTx(key.second); if (exthtlc) @@ -1352,11 +1357,11 @@ UniValue icxlisthtlcs(const JSONRPCRequest& request) { ret.pushKVs(icxSubmitEXTHTLCToJSON(*exthtlc, status)); limit--; } - return limit != 0; + return true; }; pcustomcsview->ForEachICXClaimDFCHTLC([&](CICXOrderView::TxidPairKey const & key, uint8_t status) { - if (key.first != offerTxid) + if (key.first != offerTxid || !limit) return false; auto claimdfchtlc = pcustomcsview->GetICXClaimDFCHTLCByCreationTx(key.second); if (claimdfchtlc) @@ -1364,7 +1369,7 @@ UniValue icxlisthtlcs(const JSONRPCRequest& request) { ret.pushKVs(icxClaimDFCHTLCToJSON(*claimdfchtlc)); limit--; } - return limit != 0; + return true; }, offerTxid); if (closed) diff --git a/src/validation.cpp b/src/validation.cpp index bac9baa929e..625ad011dd8 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -2627,6 +2627,8 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl // close expired orders, refund all expired DFC HTLCs at this block height if (pindex->nHeight >= chainparams.GetConsensus().EunosHeight) { + bool isPreEunosPaya = pindex->nHeight < chainparams.GetConsensus().EunosPayaHeight; + cache.ForEachICXOrderExpire([&](CICXOrderView::StatusKey const & key, uint8_t status) { if (static_cast(key.first) != pindex->nHeight) @@ -2671,8 +2673,8 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl CScript txidAddr(offer->creationTx.begin(),offer->creationTx.end()); CTokenAmount takerFee{DCT_ID{0}, offer->takerFee}; - if ((order->orderType == CICXOrder::TYPE_INTERNAL && !cache.HasICXSubmitDFCHTLCOpen(offer->creationTx)) || - (order->orderType == CICXOrder::TYPE_EXTERNAL && !cache.HasICXSubmitEXTHTLCOpen(offer->creationTx))) + if ((order->orderType == CICXOrder::TYPE_INTERNAL && !cache.ExistedICXSubmitDFCHTLC(offer->creationTx, isPreEunosPaya)) || + (order->orderType == CICXOrder::TYPE_EXTERNAL && !cache.ExistedICXSubmitEXTHTLC(offer->creationTx, isPreEunosPaya))) { auto res = cache.SubBalance(txidAddr,takerFee); if (!res) @@ -2710,7 +2712,7 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl if (status == CICXSubmitDFCHTLC::STATUS_EXPIRED && order->orderType == CICXOrder::TYPE_INTERNAL) { - if (!cache.HasICXSubmitEXTHTLCOpen(dfchtlc->offerTx)) + if (!cache.ExistedICXSubmitEXTHTLC(dfchtlc->offerTx, isPreEunosPaya)) { CTokenAmount makerDeposit{DCT_ID{0}, offer->takerFee}; cache.CalculateOwnerRewards(order->ownerAddress,pindex->nHeight); @@ -2765,7 +2767,7 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl if (status == CICXSubmitEXTHTLC::STATUS_EXPIRED && order->orderType == CICXOrder::TYPE_EXTERNAL) { - if (!cache.HasICXSubmitDFCHTLCOpen(exthtlc->offerTx)) + if (!cache.ExistedICXSubmitDFCHTLC(exthtlc->offerTx, isPreEunosPaya)) { CTokenAmount makerDeposit{DCT_ID{0}, offer->takerFee}; cache.CalculateOwnerRewards(order->ownerAddress,pindex->nHeight); diff --git a/test/functional/feature_icx_orderbook.py b/test/functional/feature_icx_orderbook.py index 45f8ad7ac82..d5af044a918 100755 --- a/test/functional/feature_icx_orderbook.py +++ b/test/functional/feature_icx_orderbook.py @@ -104,7 +104,7 @@ def run_test(self): assert_equal(result["ICX_TAKERFEE_PER_BTC"], Decimal('0.001')) - # Open and close an order + # DFI/BTC Open and close an order orderTx = self.nodes[0].icx_createorder({ 'tokenFrom': idDFI, 'chainTo': "BTC", @@ -199,6 +199,75 @@ def run_test(self): assert_equal(order[orderTx]["status"], "CLOSED") assert_equal(order[orderTx]["type"], "INTERNAL") + # BTC/DFI Open and close an order + orderTx = self.nodes[0].icx_createorder({ + 'chainFrom': "BTC", + 'tokenTo': idDFI, + 'ownerAddress': accountDFI, + 'amountFrom': 2, + 'orderPrice':100})["txid"] + + self.nodes[0].generate(1) + self.sync_blocks() + + beforeOffer = self.nodes[1].getaccount(accountBTC, {}, True)[idDFI] + + offerTx = self.nodes[1].icx_makeoffer({ + 'orderTx': orderTx, + 'amount': 10, + 'ownerAddress': accountBTC, + 'receivePubkey': '037f9563f30c609b19fd435a19b8bde7d6db703012ba1aba72e9f42a87366d1941'})["txid"] + + self.nodes[1].generate(1) + self.sync_blocks() + + assert_equal(self.nodes[1].getaccount(accountBTC, {}, True)[idDFI], beforeOffer - Decimal('0.01000000')) + + offer = self.nodes[0].icx_listorders({"orderTx": orderTx}) + + assert_equal(len(offer), 2) + + # Close offer + closeOrder = self.nodes[1].icx_closeoffer(offerTx)["txid"] + rawCloseOrder = self.nodes[1].getrawtransaction(closeOrder, 1) + authTx = self.nodes[1].getrawtransaction(rawCloseOrder['vin'][0]['txid'], 1) + found = False + for vout in authTx['vout']: + if 'addresses' in vout['scriptPubKey'] and vout['scriptPubKey']['addresses'][0] == accountBTC: + found = True + assert(found) + + self.nodes[1].generate(1) + self.sync_blocks() + + assert_equal(self.nodes[1].getaccount(accountBTC, {}, True)[idDFI], beforeOffer) + + offer = self.nodes[0].icx_listorders({"orderTx": orderTx}) + + assert_equal(len(offer), 1) + + # Check order exist + order = self.nodes[0].icx_listorders() + assert_equal(len(order), 2) + + # Close order + closeOrder = self.nodes[0].icx_closeorder(orderTx)["txid"] + rawCloseOrder = self.nodes[0].getrawtransaction(closeOrder, 1) + authTx = self.nodes[0].getrawtransaction(rawCloseOrder['vin'][0]['txid'], 1) + found = False + for vout in authTx['vout']: + if 'addresses' in vout['scriptPubKey'] and vout['scriptPubKey']['addresses'][0] == accountDFI: + found = True + assert(found) + + self.nodes[0].generate(1) + self.sync_blocks() + + order = self.nodes[0].icx_listorders() + + assert_equal(len(order), 1) + + # DFI/BTC scenario # Open an order @@ -522,6 +591,8 @@ def run_test(self): self.nodes[0].generate(1) self.sync_blocks() + assert_equal(self.nodes[1].getaccount(accountBTC, {}, True)[idDFI], beforeOffer - Decimal('0.20000000')) + # Check burn assert_equal(self.nodes[0].getburninfo()['tokens'][0], "0.43000000@DFI") result = self.nodes[0].listburnhistory() @@ -593,6 +664,86 @@ def run_test(self): order = self.nodes[0].icx_listorders({"closed": True}) assert_equal(order[orderTx]["status"], 'FILLED') + + # DFI/BTC partial offer acceptance + 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() + + beforeOffer = 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() + + assert_equal(self.nodes[1].getaccount(accountBTC, {}, True)[idDFI], beforeOffer - Decimal('0.10000000')) + + offer = self.nodes[0].icx_listorders({"orderTx": orderTx}) + + assert_equal(offer[offerTx]["orderTx"], orderTx) + assert_equal(offer[offerTx]["amount"], Decimal('1.00000000')) + assert_equal(offer[offerTx]["ownerAddress"], accountBTC) + assert_equal(offer[offerTx]["takerFee"], Decimal('0.10000000')) + + dfchtlcTx = self.nodes[0].icx_submitdfchtlc({ + 'offerTx': offerTx, + 'amount': 15, + 'hash': '957fc0fd643f605b2938e0631a61529fd70bd35b2162a21d978c41e5241a5220', + 'timeout': 500})["txid"] + + self.nodes[0].generate(1) + self.sync_blocks() + + assert_equal(self.nodes[1].getaccount(accountBTC, {}, True)[idDFI], beforeOffer - Decimal('0.01500000')) + + # Check burn + assert_equal(self.nodes[0].getburninfo()['tokens'][0], "0.46000000@DFI") + result = self.nodes[0].listburnhistory() + assert_equal(result[0]['owner'], burn_address) + assert_equal(result[0]['type'], 'ICXSubmitDFCHTLC') + assert_equal(result[0]['amounts'][0], '0.03000000@DFI') + + offer = self.nodes[0].icx_listorders({"orderTx": orderTx}) + assert_equal(offer[offerTx]["takerFee"], Decimal('0.01500000')) + + + exthtlcTx = self.nodes[1].icx_submitexthtlc({ + 'offerTx': offerTx, + 'amount': 0.15, + 'hash': '957fc0fd643f605b2938e0631a61529fd70bd35b2162a21d978c41e5241a5220', + 'htlcScriptAddress': '13sJQ9wBWh8ssihHUgAaCmNWJbBAG5Hr9N', + 'ownerPubkey': '036494e7c9467c8c7ff3bf29e841907fb0fa24241866569944ea422479ec0e6252', + 'timeout': 15})["txid"] + + self.nodes[1].generate(1) + self.sync_blocks() + + claimTx = self.nodes[1].icx_claimdfchtlc({ + 'dfchtlcTx': dfchtlcTx, + 'seed': 'f75a61ad8f7a6e0ab701d5be1f5d4523a9b534571e4e92e0c4610c6a6784ccef'})["txid"] + + self.nodes[1].generate(1) + self.sync_blocks() + + # Make sure offer and order are now closed + offer = self.nodes[0].icx_listorders({"orderTx": orderTx}) + assert_equal(len(offer), 1) + order = self.nodes[0].icx_listorders() + assert_equal(len(offer), 1) + order = self.nodes[0].icx_listorders({"closed": True}) + assert_equal(order[orderTx]["status"], 'FILLED') + # DFI/BTC scenario expiration test # Open an order diff --git a/test/functional/feature_icx_orderbook_errors.py b/test/functional/feature_icx_orderbook_errors.py index 4df0da64081..9cfd21cfc3f 100755 --- a/test/functional/feature_icx_orderbook_errors.py +++ b/test/functional/feature_icx_orderbook_errors.py @@ -299,7 +299,7 @@ def run_test(self): 'timeout': 15}) except JSONRPCException as e: errorString = e.error['message'] - assert("amount 100000 must be equal to calculated dfchtlc amount 1000000" in errorString) + assert("amount must be equal to calculated dfchtlc amount" in errorString) # more amount try: @@ -312,7 +312,7 @@ def run_test(self): 'timeout': 15}) except JSONRPCException as e: errorString = e.error['message'] - assert("amount 10000000 must be equal to calculated dfchtlc amount 1000000" in errorString) + assert("amount must be equal to calculated dfchtlc amount" in errorString) # different hash try: