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

Mint tokens rules adaptation #1568

Merged
merged 8 commits into from
Nov 15, 2022
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
1 change: 1 addition & 0 deletions src/init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -507,6 +507,7 @@ void SetupServerArgs()
gArgs.AddArg("-grandcentralheight", "Grand Central fork activation height (regtest only)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CHAINPARAMS);
gArgs.AddArg("-jellyfish_regtest", "Configure the regtest network for jellyfish testing", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::OPTIONS);
gArgs.AddArg("-regtest-skip-loan-collateral-validation", "Skip loan collateral check for jellyfish testing", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::OPTIONS);
gArgs.AddArg("-regtest-minttoken-simulate-mainnet", "Allow anybody to mint token on regtest", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::OPTIONS);
prasannavl marked this conversation as resolved.
Show resolved Hide resolved
gArgs.AddArg("-simulatemainnet", "Configure the regtest network to mainnet target timespan and spacing ", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::OPTIONS);
gArgs.AddArg("-dexstats", strprintf("Enable storing live dex data in DB (default: %u)", DEFAULT_DEXSTATS), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
#ifdef USE_UPNP
Expand Down
205 changes: 115 additions & 90 deletions src/masternodes/mn_checks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -586,7 +586,7 @@ class CCustomMetadataParseVisitor
auto res = isPostGrandCentralFork();
return !res ? res : serialize(obj);
}

Res operator()(CCreatePropMessage& obj) const {
auto res = isPostGrandCentralFork();
return !res ? res : serialize(obj);
Expand Down Expand Up @@ -750,7 +750,7 @@ CAmount CCustomTxVisitor::CalculateTakerFee(CAmount amount) const
return (arith_uint256(amount) * mnview.ICXGetTakerFeePerBTC() / COIN * GetDFIperBTC(pair->second) / COIN).GetLow64();
}

ResVal<CScript> CCustomTxVisitor::MintableToken(DCT_ID id, const CTokenImplementation& token) const {
ResVal<CScript> CCustomTxVisitor::MintableToken(DCT_ID id, const CTokenImplementation& token, bool anybodyCanMint) const {
if (token.destructionTx != uint256{}) {
return Res::Err("token %s already destroyed at height %i by tx %s", token.symbol,
token.destructionHeight, token.destructionTx.GetHex());
Expand All @@ -776,23 +776,29 @@ ResVal<CScript> CCustomTxVisitor::MintableToken(DCT_ID id, const CTokenImplement
if (token.IsPoolShare()) {
return Res::Err("can't mint LPS token %s!", id.ToString());
}

static const auto isMainNet = Params().NetworkIDString() == CBaseChainParams::MAIN;
// may be different logic with LPS, so, dedicated check:
if (!token.IsMintable() || (isMainNet && mnview.GetLoanTokenByID(id))) {
return Res::Err("token %s is not mintable!", id.ToString());
}

if (!HasAuth(auth.out.scriptPubKey)) { // in the case of DAT, it's ok to do not check foundation auth cause exact DAT owner is foundation member himself
if (!token.IsDAT()) {
return Res::Err("tx must have at least one input from token owner");
} else if (!HasFoundationAuth()) { // Is a DAT, check founders auth
if (height < static_cast<uint32_t>(consensus.GrandCentralHeight))
return Res::Err("token is DAT and tx not from foundation member");
ResVal<CScript> result = {auth.out.scriptPubKey, Res::Ok()};
if (anybodyCanMint || HasAuth(auth.out.scriptPubKey)) return result;

// Historic: in the case of DAT, it's ok to do not check foundation auth cause exact DAT owner is foundation member himself
// The above is no longer true.

if (token.IsDAT()) {
// Is a DAT, check founders auth
if (height < static_cast<uint32_t>(consensus.GrandCentralHeight) && !HasFoundationAuth()) {
return Res::Err("token is DAT and tx not from foundation member");
}
} else {
return Res::Err("tx must have at least one input from token owner");
}

return {auth.out.scriptPubKey, Res::Ok()};
return result;
}

Res CCustomTxVisitor::EraseEmptyBalances(TAmounts& balances) const
Expand Down Expand Up @@ -1293,114 +1299,133 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor
if (!token)
return Res::Err("token %s does not exist!", tokenId.ToString());

auto mintable = MintableToken(tokenId, *token);
// only on REGTEST and when flag is supplied
bool anybodyCanMint = (Params().NetworkIDString() != CBaseChainParams::REGTEST && !gArgs.GetArg("-regtest-minttoken-simulate-mainnet", false));

auto mintable = MintableToken(tokenId, *token, anybodyCanMint);

auto mintTokensInternal = [&](DCT_ID tokenId, CAmount amount) {
auto minted = mnview.AddMintedTokens(tokenId, amount);
if (!minted)
return minted;

CalculateOwnerRewards(*mintable.val);
auto res = mnview.AddBalance(*mintable.val, CTokenAmount{tokenId, amount});
if (!res)
return res;

return Res::Ok();
};

if (!mintable)
return std::move(mintable);


if (height >= static_cast<uint32_t>(consensus.GrandCentralHeight) && token->IsDAT() && !HasFoundationAuth())
if (anybodyCanMint || height < static_cast<uint32_t>(consensus.GrandCentralHeight) || !token->IsDAT() || HasFoundationAuth())
{
auto attributes = mnview.GetAttributes();
assert(attributes);
auto res = mintTokensInternal(tokenId, amount);
if (!res) return res;
continue;
}

CDataStructureV0 enableKey{AttributeTypes::Param, ParamIDs::Feature, DFIPKeys::ConsortiumEnabled};
if (attributes->GetValue(enableKey, false))
{
mintable.ok = false;
auto attributes = mnview.GetAttributes();
assert(attributes);

CDataStructureV0 membersKey{AttributeTypes::Consortium, tokenId.v, ConsortiumKeys::MemberValues};
const auto members = attributes->GetValue(membersKey, CConsortiumMembers{});
CDataStructureV0 enableKey{AttributeTypes::Param, ParamIDs::Feature, DFIPKeys::ConsortiumEnabled};
CDataStructureV0 membersKey{AttributeTypes::Consortium, tokenId.v, ConsortiumKeys::MemberValues};
const auto members = attributes->GetValue(membersKey, CConsortiumMembers{});

CDataStructureV0 membersMintedKey{AttributeTypes::Live, ParamIDs::Economy, EconomyKeys::ConsortiumMembersMinted};
auto membersBalances = attributes->GetValue(membersMintedKey, CConsortiumMembersMinted{});
if (!attributes->GetValue(enableKey, false) || members.empty()) {
const Coin& auth = coins.AccessCoin(COutPoint(token->creationTx, 1)); // always n=1 output
if (!HasAuth(auth.out.scriptPubKey))
return Res::Err("You are not a foundation member or token owner and cannot mint this token!");

const auto dailyInterval = height / consensus.blocksPerDay() * consensus.blocksPerDay();
auto res = mintTokensInternal(tokenId, amount);
if (!res) return res;
continue;
}

for (auto const& [key, member] : members)
{
if (HasAuth(member.ownerAddress))
{
if (member.status != CConsortiumMember::Status::Active)
return Res::Err("Cannot mint token, not an active member of consortium for %s!", token->symbol);

auto add = SafeAdd(membersBalances[tokenId][key].minted, amount);
if (!add)
return (std::move(add));
membersBalances[tokenId][key].minted = add;

if (dailyInterval == membersBalances[tokenId][key].dailyMinted.first) {
add = SafeAdd(membersBalances[tokenId][key].dailyMinted.second, amount);
if (!add)
return (std::move(add));
membersBalances[tokenId][key].dailyMinted.second = add;
} else {
membersBalances[tokenId][key].dailyMinted.first = dailyInterval;
membersBalances[tokenId][key].dailyMinted.second = amount;
}
mintable.ok = false;

if (membersBalances[tokenId][key].minted > member.mintLimit)
return Res::Err("You will exceed your maximum mint limit for %s token by minting this amount!", token->symbol);
CDataStructureV0 membersMintedKey{AttributeTypes::Live, ParamIDs::Economy, EconomyKeys::ConsortiumMembersMinted};
auto membersBalances = attributes->GetValue(membersMintedKey, CConsortiumMembersMinted{});

if (membersBalances[tokenId][key].dailyMinted.second > member.dailyMintLimit) {
return Res::Err("You will exceed your daily mint limit for %s token by minting this amount", token->symbol);
}
const auto dailyInterval = height / consensus.blocksPerDay() * consensus.blocksPerDay();

*mintable.val = member.ownerAddress;
mintable.ok = true;
break;
}
for (auto const& [key, member] : members)
{
if (HasAuth(member.ownerAddress))
{
if (member.status != CConsortiumMember::Status::Active)
return Res::Err("Cannot mint token, not an active member of consortium for %s!", token->symbol);

auto add = SafeAdd(membersBalances[tokenId][key].minted, amount);
if (!add)
return (std::move(add));
membersBalances[tokenId][key].minted = add;

if (dailyInterval == membersBalances[tokenId][key].dailyMinted.first) {
add = SafeAdd(membersBalances[tokenId][key].dailyMinted.second, amount);
if (!add)
return (std::move(add));
membersBalances[tokenId][key].dailyMinted.second = add;
} else {
membersBalances[tokenId][key].dailyMinted.first = dailyInterval;
membersBalances[tokenId][key].dailyMinted.second = amount;
}

if (!mintable)
return Res::Err("You are not a foundation or consortium member and cannot mint this token!");
if (membersBalances[tokenId][key].minted > member.mintLimit)
return Res::Err("You will exceed your maximum mint limit for %s token by minting this amount!", token->symbol);

CDataStructureV0 maxLimitKey{AttributeTypes::Consortium, tokenId.v, ConsortiumKeys::MintLimit};
const auto maxLimit = attributes->GetValue(maxLimitKey, CAmount{0});
if (membersBalances[tokenId][key].dailyMinted.second > member.dailyMintLimit) {
return Res::Err("You will exceed your daily mint limit for %s token by minting this amount", token->symbol);
}

CDataStructureV0 dailyLimitKey{AttributeTypes::Consortium, tokenId.v, ConsortiumKeys::DailyMintLimit};
const auto dailyLimit = attributes->GetValue(dailyLimitKey, CAmount{0});
*mintable.val = member.ownerAddress;
mintable.ok = true;
break;
}
}

CDataStructureV0 consortiumMintedKey{AttributeTypes::Live, ParamIDs::Economy, EconomyKeys::ConsortiumMinted};
auto globalBalances = attributes->GetValue(consortiumMintedKey, CConsortiumGlobalMinted{});
if (!mintable)
return Res::Err("You are not a foundation or consortium member and cannot mint this token!");

auto add = SafeAdd(globalBalances[tokenId].minted, amount);
if (!add)
return (std::move(add));
CDataStructureV0 maxLimitKey{AttributeTypes::Consortium, tokenId.v, ConsortiumKeys::MintLimit};
const auto maxLimit = attributes->GetValue(maxLimitKey, CAmount{0});

globalBalances[tokenId].minted = add;
CDataStructureV0 dailyLimitKey{AttributeTypes::Consortium, tokenId.v, ConsortiumKeys::DailyMintLimit};
const auto dailyLimit = attributes->GetValue(dailyLimitKey, CAmount{0});

if (maxLimit != -1 * COIN && globalBalances[tokenId].minted > maxLimit)
return Res::Err("You will exceed global maximum consortium mint limit for %s token by minting this amount!", token->symbol);
CDataStructureV0 consortiumMintedKey{AttributeTypes::Live, ParamIDs::Economy, EconomyKeys::ConsortiumMinted};
auto globalBalances = attributes->GetValue(consortiumMintedKey, CConsortiumGlobalMinted{});

CAmount totalDaily{};
for (const auto& [key, value] : membersBalances[tokenId]) {
if (value.dailyMinted.first == dailyInterval) {
totalDaily += value.dailyMinted.second;
}
}
auto add = SafeAdd(globalBalances[tokenId].minted, amount);
if (!add)
return (std::move(add));

if (dailyLimit != -1 * COIN && totalDaily > dailyLimit)
return Res::Err("You will exceed global daily maximum consortium mint limit for %s token by minting this amount.", token->symbol);
globalBalances[tokenId].minted = add;

attributes->SetValue(consortiumMintedKey, globalBalances);
attributes->SetValue(membersMintedKey, membersBalances);
if (maxLimit != -1 * COIN && globalBalances[tokenId].minted > maxLimit)
return Res::Err("You will exceed global maximum consortium mint limit for %s token by minting this amount!", token->symbol);

auto saved = mnview.SetVariable(*attributes);
if (!saved)
return saved;
CAmount totalDaily{};
for (const auto& [key, value] : membersBalances[tokenId]) {
if (value.dailyMinted.first == dailyInterval) {
totalDaily += value.dailyMinted.second;
}
else
return Res::Err("You are not a foundation member and cannot mint this token!");
}

auto minted = mnview.AddMintedTokens(tokenId, amount);
if (!minted)
return minted;
if (dailyLimit != -1 * COIN && totalDaily > dailyLimit)
return Res::Err("You will exceed global daily maximum consortium mint limit for %s token by minting this amount.", token->symbol);

CalculateOwnerRewards(*mintable.val);
auto res = mnview.AddBalance(*mintable.val, CTokenAmount{tokenId, amount});
if (!res)
return res;
attributes->SetValue(consortiumMintedKey, globalBalances);
attributes->SetValue(membersMintedKey, membersBalances);

auto saved = mnview.SetVariable(*attributes);
if (!saved)
return saved;

auto minted = mintTokensInternal(tokenId, amount);
if (!minted) return minted;
}

return Res::Ok();
Expand Down
2 changes: 1 addition & 1 deletion src/masternodes/mn_checks.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ class CCustomTxVisitor
DCT_ID FindTokenByPartialSymbolName(const std::string& symbol) const;
CPoolPair GetBTCDFIPoolPair() const;
CAmount CalculateTakerFee(CAmount amount) const;
ResVal<CScript> MintableToken(DCT_ID id, const CTokenImplementation& token) const;
ResVal<CScript> MintableToken(DCT_ID id, const CTokenImplementation& token, bool anybodyCanMint) const;
Res EraseEmptyBalances(TAmounts& balances) const;
Res SetShares(const CScript& owner, const TAmounts& balances) const;
Res DelShares(const CScript& owner, const TAmounts& balances) const;
Expand Down
13 changes: 4 additions & 9 deletions src/masternodes/mn_rpc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ static CTransactionRef send(CTransactionRef tx, CTransactionRef optAuthTx) {
return tx;
}

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

Expand Down Expand Up @@ -407,12 +407,7 @@ std::vector<CTxIn> GetAuthInputsSmart(CWalletCoinsUnlocker& pwallet, int32_t txV
// Look for founder's auth. minttoken may already have an auth in result.
if (needFounderAuth && result.empty()) {
auto anyFounder = AmIFounder(pwallet);
if (!anyFounder) {
// Called from minttokens if auth not empty here which can use collateralAddress
if (auths.empty()) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Need foundation member authorization");
}
} else {
if (anyFounder) {
auths.insert(anyFounder.value());
auto authInput = GetAnyFoundationAuthInput(pwallet);
if (authInput) {
Expand Down Expand Up @@ -838,7 +833,7 @@ UniValue listgovs(const JSONRPCRequest& request) {
} else if (prefix == "attrs") {
mode = GovVarsFilter::AttributesOnly;
} else if (prefix == "v/2.7") {
// Undocumented. Make be removed or deprecated without notice.
// Undocumented. Make be removed or deprecated without notice.
// Only here for unforeseen compatibility concern downstream
// for transitions.
mode = GovVarsFilter::Version2Dot7;
Expand Down Expand Up @@ -878,7 +873,7 @@ UniValue listgovs(const JSONRPCRequest& request) {
val = a->ExportFiltered(mode, prefix);
}
} else {
if (mode == GovVarsFilter::LiveAttributes ||
if (mode == GovVarsFilter::LiveAttributes ||
mode == GovVarsFilter::PrefixedAttributes ||
mode == GovVarsFilter::AttributesOnly){
continue;
Expand Down
4 changes: 0 additions & 4 deletions src/masternodes/rpc_tokens.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -728,10 +728,6 @@ UniValue minttokens(const JSONRPCRequest& request) {
}
}

if (needFoundersAuth && !AmIFounder(pwallet)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Need foundation or consortium member authorization!");
}

rawTx.vin = GetAuthInputsSmart(pwallet, rawTx.nVersion, auths, needFoundersAuth, optAuthTx, txInputs);

CDataStream metadata(DfTxMarker, SER_NETWORK, PROTOCOL_VERSION);
Expand Down
Loading