Skip to content

Commit

Permalink
Refactor smart contracts (#1057)
Browse files Browse the repository at this point in the history
* Refactor smart contracts

* Make tests executable

* Refactor smart contract execution RPC

* Refine CWalletCoinsUnlocker ctors
  • Loading branch information
prasannavl authored Jan 25, 2022
1 parent 7f5cf1c commit a9bb158
Show file tree
Hide file tree
Showing 13 changed files with 175 additions and 157 deletions.
9 changes: 4 additions & 5 deletions src/chainparams.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
// file LICENSE or http://www.opensource.org/licenses/mit-license.php.

#include <chainparams.h>

#include <chainparamsseeds.h>
#include <consensus/merkle.h>
#include <masternodes/mn_checks.h>
Expand Down Expand Up @@ -244,7 +243,7 @@ class CMainParams : public CChainParams {
consensus.accountDestruction.insert(GetScriptForDestination(DecodeDestination("8UAhRuUFCyFUHEPD7qvtj8Zy2HxF5HH5nb", *this)));

consensus.smartContracts.clear();
consensus.smartContracts["DFIP2201"] = GetScriptForDestination(CTxDestination(WitnessV0KeyHash(std::vector<unsigned char>{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0})));
consensus.smartContracts[SMART_CONTRACT_DFIP_2201] = GetScriptForDestination(CTxDestination(WitnessV0KeyHash(std::vector<unsigned char>{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0})));

// owner base58, operator base58
vMasternodes.push_back({"8PuErAcazqccCVzRcc8vJ3wFaZGm4vFbLe", "8J846CKFF83Jcj5m4EReJmxiaJ6Jy1Y6Ea"});
Expand Down Expand Up @@ -460,7 +459,7 @@ class CTestNetParams : public CChainParams {
consensus.accountDestruction.insert(GetScriptForDestination(DecodeDestination("75jrurn8tkDLhZ3YPyzhk6D9kc1a4hBrmM", *this))); // cSmsVpoR6dSW5hPNKeGwC561gXHXcksdQb2yAFQdjbSp5MUyzZqr

consensus.smartContracts.clear();
consensus.smartContracts["DFIP2201"] = GetScriptForDestination(CTxDestination(WitnessV0KeyHash(std::vector<unsigned char>{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0})));
consensus.smartContracts[SMART_CONTRACT_DFIP_2201] = GetScriptForDestination(CTxDestination(WitnessV0KeyHash(std::vector<unsigned char>{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0})));

// owner base58, operator base58
vMasternodes.push_back({"7LMorkhKTDjbES6DfRxX2RiNMbeemUkxmp", "7KEu9JMKCx6aJ9wyg138W3p42rjg19DR5D"});
Expand Down Expand Up @@ -641,7 +640,7 @@ class CDevNetParams : public CChainParams {
consensus.foundationMembers.emplace(GetScriptForDestination(DecodeDestination("7L29itepC13pgho1X2y7mcuf4WjkBi7x2w", *this)));

consensus.smartContracts.clear();
consensus.smartContracts["DFIP2201"] = GetScriptForDestination(CTxDestination(WitnessV0KeyHash(std::vector<unsigned char>{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0})));
consensus.smartContracts[SMART_CONTRACT_DFIP_2201] = GetScriptForDestination(CTxDestination(WitnessV0KeyHash(std::vector<unsigned char>{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0})));

// owner base58, operator base58
vMasternodes.push_back({"7M3g9CSERjLdXisE5pv2qryDbURUj9Vpi1", "7Grgx69MZJ4wDKRx1bBxLqTnU9T3quKW7n"});
Expand Down Expand Up @@ -828,7 +827,7 @@ class CRegTestParams : public CChainParams {
consensus.accountDestruction.insert(GetScriptForDestination(DecodeDestination("mxiaFfAnCoXEUy4RW8NgsQM7yU5YRCiFSh", *this)));

consensus.smartContracts.clear();
consensus.smartContracts["DFIP2201"] = GetScriptForDestination(CTxDestination(WitnessV0KeyHash(std::vector<unsigned char>{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0})));
consensus.smartContracts[SMART_CONTRACT_DFIP_2201] = GetScriptForDestination(CTxDestination(WitnessV0KeyHash(std::vector<unsigned char>{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0})));

// owner base58, operator base58
vMasternodes.push_back({"mwsZw8nF7pKxWH8eoKL9tPxTpaFkz7QeLU", "mswsMVsyGMj1FzDMbbxw2QW3KvQAv2FKiy"});
Expand Down
2 changes: 2 additions & 0 deletions src/chainparams.h
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,8 @@ class CChainParams
std::set<CKeyID> genesisTeam;
};

const auto SMART_CONTRACT_DFIP_2201 = "DFIP2201";

/**
* Creates and returns a std::unique_ptr<CChainParams> of the chosen chain.
* @returns a CChainParams* of the chosen chain.
Expand Down
137 changes: 70 additions & 67 deletions src/masternodes/mn_checks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1343,96 +1343,96 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor
return !res ? res : addBalancesSetShares(obj.to);
}

Res operator()(const CSmartContractMessage& obj) const {

Res HandleDFIP2201Contract(const CSmartContractMessage& obj) const {
const auto attributes = mnview.GetAttributes();
if (!attributes) {
return Res::Err("DFIP2201 smart contract is not enabled");
}
if (!attributes)
return Res::Err("Attributes unavailable");

CDataStructureV0 activeKey{AttributeTypes::Param, ParamIDs::DFIP2201, DFIP2201Keys::Active};
if (!attributes->GetValue(activeKey, false)) {

if (!attributes->GetValue(activeKey, false))
return Res::Err("DFIP2201 smart contract is not enabled");
}

if (obj.accounts.empty()) {
return Res::Err("No address and amount entries found");
}
if (obj.name != SMART_CONTRACT_DFIP_2201)
return Res::Err("DFIP2201 contract mismatch - got: " + obj.name);

if (obj.name == Params().GetConsensus().smartContracts.begin()->first) {
if (obj.accounts.size() != 1) {
return Res::Err("Only one address entry expected for " + obj.name);
}
if (obj.accounts.size() != 1)
return Res::Err("Only one address entry expected for " + obj.name);

if (obj.accounts.begin()->second.balances.size() != 1) {
return Res::Err("Only one amount entry expected for " + obj.name);
}
if (obj.accounts.begin()->second.balances.size() != 1)
return Res::Err("Only one amount entry expected for " + obj.name);

const auto& script = obj.accounts.begin()->first;
if (!HasAuth(script)) {
return Res::Err("Must have at least one input from supplied address");
}
const auto& script = obj.accounts.begin()->first;
if (!HasAuth(script))
return Res::Err("Must have at least one input from supplied address");

const auto& id = obj.accounts.begin()->second.balances.begin()->first;
const auto& amount = obj.accounts.begin()->second.balances.begin()->second;
const auto& id = obj.accounts.begin()->second.balances.begin()->first;
const auto& amount = obj.accounts.begin()->second.balances.begin()->second;

if (amount <= 0) {
return Res::Err("Amount out of range");
}
if (amount <= 0)
return Res::Err("Amount out of range");

CDataStructureV0 minSwapKey{AttributeTypes::Param, ParamIDs::DFIP2201, DFIP2201Keys::MinSwap};
auto minSwap = attributes->GetValue(minSwapKey, CAmount{0});
CDataStructureV0 minSwapKey{AttributeTypes::Param, ParamIDs::DFIP2201, DFIP2201Keys::MinSwap};
auto minSwap = attributes->GetValue(minSwapKey, CAmount{0});

if (minSwap && amount < minSwap) {
return Res::Err("Below minimum swapable amount, must be at least " + ValueFromAmount(minSwap).getValStr() + " BTC");
}
if (minSwap && amount < minSwap)
return Res::Err("Below minimum swapable amount, must be at least " + ValueFromAmount(minSwap).getValStr() + " BTC");

const auto token = mnview.GetToken(id);
if (!token) {
return Res::Err("Specified token not found");
}
const auto token = mnview.GetToken(id);
if (!token)
return Res::Err("Specified token not found");

if (token->symbol != "BTC" || token->name != "Bitcoin" || !token->IsDAT()) {
return Res::Err("Only Bitcoin can be swapped in " + obj.name);
}
if (token->symbol != "BTC" || token->name != "Bitcoin" || !token->IsDAT())
return Res::Err("Only Bitcoin can be swapped in " + obj.name);

auto res = mnview.SubBalance(script, {id, amount});
if (!res) {
return res;
}
auto res = mnview.SubBalance(script, {id, amount});
if (!res)
return res;

const std::pair<std::string, std::string> btcUsd{"BTC","USD"};
const std::pair<std::string, std::string> dfiUsd{"DFI","USD"};
const std::pair<std::string, std::string> btcUsd{"BTC","USD"};
const std::pair<std::string, std::string> dfiUsd{"DFI","USD"};

bool useNextPrice{false}, requireLivePrice{true};
auto resVal = mnview.GetValidatedIntervalPrice(btcUsd, useNextPrice, requireLivePrice);
if (!resVal) {
return std::move(resVal);
}
bool useNextPrice{false}, requireLivePrice{true};
auto resVal = mnview.GetValidatedIntervalPrice(btcUsd, useNextPrice, requireLivePrice);
if (!resVal)
return std::move(resVal);

CDataStructureV0 premiumKey{AttributeTypes::Param, ParamIDs::DFIP2201, DFIP2201Keys::Premium};
auto premium = attributes->GetValue(premiumKey, CAmount{2500000});
CDataStructureV0 premiumKey{AttributeTypes::Param, ParamIDs::DFIP2201, DFIP2201Keys::Premium};
auto premium = attributes->GetValue(premiumKey, CAmount{2500000});

const auto& btcPrice = MultiplyAmounts(*resVal.val, premium + COIN);
const auto& btcPrice = MultiplyAmounts(*resVal.val, premium + COIN);

resVal = mnview.GetValidatedIntervalPrice(dfiUsd, useNextPrice, requireLivePrice);
if (!resVal) {
return std::move(resVal);
}
resVal = mnview.GetValidatedIntervalPrice(dfiUsd, useNextPrice, requireLivePrice);
if (!resVal)
return std::move(resVal);

const auto totalDFI = MultiplyAmounts(DivideAmounts(btcPrice, *resVal.val), amount);
const auto totalDFI = MultiplyAmounts(DivideAmounts(btcPrice, *resVal.val), amount);

res = mnview.SubBalance(Params().GetConsensus().smartContracts.begin()->second, {{0}, totalDFI});
if (!res) {
return res;
}
res = mnview.SubBalance(Params().GetConsensus().smartContracts.begin()->second, {{0}, totalDFI});
if (!res)
return res;

res = mnview.AddBalance(script, {{0}, totalDFI});
if (!res) {
return res;
}
res = mnview.AddBalance(script, {{0}, totalDFI});
if (!res)
return res;

return Res::Ok();
}

return Res::Ok();
Res operator()(const CSmartContractMessage& obj) const {
if (obj.accounts.empty()) {
return Res::Err("Contract account parameters missing");
}
auto contracts = Params().GetConsensus().smartContracts;

auto contract = contracts.find(obj.name);
if (contract == contracts.end())
return Res::Err("Specified smart contract not found");

// Convert to switch when it's long enough.
if (obj.name == SMART_CONTRACT_DFIP_2201)
return HandleDFIP2201Contract(obj);

return Res::Err("Specified smart contract not found");
}
Expand Down Expand Up @@ -3263,8 +3263,11 @@ Res CustomTxVisit(CCustomCSView& mnview, const CCoinsViewCache& coins, const CTr
TBytes dummy;
switch(GuessCustomTxType(tx, dummy))
{
case CustomTxType::TakeLoan: case CustomTxType::PaybackLoan: case CustomTxType::DepositToVault:
case CustomTxType::WithdrawFromVault: case CustomTxType::UpdateVault:
case CustomTxType::TakeLoan:
case CustomTxType::PaybackLoan:
case CustomTxType::DepositToVault:
case CustomTxType::WithdrawFromVault:
case CustomTxType::UpdateVault:
return Res::Err("This type of transaction is not possible around hard fork height");
break;
default:
Expand Down
3 changes: 2 additions & 1 deletion src/masternodes/mn_rpc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,8 @@ static CTransactionRef send(CTransactionRef tx, CTransactionRef optAuthTx) {
return tx;
}

CWalletCoinsUnlocker::CWalletCoinsUnlocker(std::shared_ptr<CWallet> pwallet) : pwallet(std::move(pwallet)) {
CWalletCoinsUnlocker::CWalletCoinsUnlocker(std::shared_ptr<CWallet> pwallet) :
pwallet(std::move(pwallet)) {
}

CWalletCoinsUnlocker::~CWalletCoinsUnlocker() {
Expand Down
2 changes: 2 additions & 0 deletions src/masternodes/mn_rpc.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ class CWalletCoinsUnlocker {
std::vector<COutPoint> coins;
public:
explicit CWalletCoinsUnlocker(std::shared_ptr<CWallet> pwallet);
CWalletCoinsUnlocker(const CWalletCoinsUnlocker&) = delete;
CWalletCoinsUnlocker(CWalletCoinsUnlocker&&) = default;
~CWalletCoinsUnlocker();
CWallet* operator->();
CWallet& operator*();
Expand Down
Loading

0 comments on commit a9bb158

Please sign in to comment.