From 7c3e940c7ba03ad61b77cee09661b13a7d6910b9 Mon Sep 17 00:00:00 2001 From: Bushstar Date: Fri, 11 Oct 2024 09:31:58 +0100 Subject: [PATCH 1/3] Allow Governance and Foundation to update loan tokens after the fork --- src/dfi/consensus/tokens.cpp | 31 +++--- src/dfi/rpc_tokens.cpp | 4 +- .../feature_community_governance.py | 100 ++++++++++++++---- 3 files changed, 99 insertions(+), 36 deletions(-) diff --git a/src/dfi/consensus/tokens.cpp b/src/dfi/consensus/tokens.cpp index 733043bddd..7a8145bfc7 100644 --- a/src/dfi/consensus/tokens.cpp +++ b/src/dfi/consensus/tokens.cpp @@ -201,20 +201,23 @@ Res CTokensConsensus::operator()(const CUpdateTokenMessage &obj) const { if (!ownerAuth) { // Governance or foundation can still mark/unmark token deprecation if (auto res = authCheck.HasGovOrFoundationAuth(); res) { - // Allow only deprecation. We disallow changes like name or symbol - // as a token holder shouldn't be misrepresented by governance. - // Governance can choose completely discard it by deprecating it - // or keep it in the form as intended by the owner. - - const auto toggledFlags = static_cast(updatedToken.flags ^ token.flags); - const auto hasDisallowedFlagToggle = - toggledFlags != static_cast(CToken::TokenFlags::Deprecated); - - const auto disallowedChanges = hasDisallowedFlagToggle || updatedToken.symbol != token.symbol || - updatedToken.name != token.name || obj.newCollateralAddress; - - if (disallowedChanges) { - return Res::Err("Only token deprecation toggle is allowed by governance"); + // Limit update token for governance and foundation for non-loan tokens + if (!mnview.GetLoanTokenByID(tokenID)) { + // Allow only deprecation. We disallow changes like name or symbol + // as a token holder shouldn't be misrepresented by governance. + // Governance can choose completely discard it by deprecating it + // or keep it in the form as intended by the owner. + + const auto toggledFlags = static_cast(updatedToken.flags ^ token.flags); + const auto hasDisallowedFlagToggle = + toggledFlags != static_cast(CToken::TokenFlags::Deprecated); + + const auto disallowedChanges = hasDisallowedFlagToggle || updatedToken.symbol != token.symbol || + updatedToken.name != token.name || obj.newCollateralAddress; + + if (disallowedChanges) { + return Res::Err("Only token deprecation toggle is allowed by governance"); + } } } else { return Res::Err("Authentication failed for token owner"); diff --git a/src/dfi/rpc_tokens.cpp b/src/dfi/rpc_tokens.cpp index d68bea5338..f17e10cb5e 100644 --- a/src/dfi/rpc_tokens.cpp +++ b/src/dfi/rpc_tokens.cpp @@ -267,8 +267,8 @@ UniValue updatetoken(const JSONRPCRequest &request) { auto [view, accountView, vaultView] = GetSnapshots(); auto targetHeight = view->GetLastHeight() + 1; + DCT_ID id{}; { - DCT_ID id; auto token = view->GetTokenGuessId(tokenStr, id); if (!token) { throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Token %s does not exist!", tokenStr)); @@ -350,6 +350,8 @@ UniValue updatetoken(const JSONRPCRequest &request) { const auto members = GetFoundationMembers(*view); isFoundersToken = !members.empty() ? members.count(owner) : Params().GetConsensus().foundationMembers.count(owner); + } else if (view->GetLoanTokenByID(id)) { + isFoundersToken = true; } if (isFoundersToken) { // need any founder's auth diff --git a/test/functional/feature_community_governance.py b/test/functional/feature_community_governance.py index 64f19e16d2..b580acd91a 100755 --- a/test/functional/feature_community_governance.py +++ b/test/functional/feature_community_governance.py @@ -66,6 +66,12 @@ def run_test(self): # Test updating Governance self.govvar_checks() + # Test loan token Foundation update after fork + self.update_loan_token(0) + + # Test loan token Governance update after fork + self.update_loan_token(1) + # Test member addition, removal and errors self.member_update_and_errors() @@ -235,8 +241,55 @@ def pre_fork_checks(self): }, ) - def test_setting_governance(self): + def update_loan_token(self, node): + # Rollback block + self.rollback_to_start() + + # Enable Governance + self.enable_governance(self.governance_member) + + # Create loan token + id = self.create_meta_loan_token(node) + + # Token split + self.nodes[node].setgovheight( + { + "ATTRIBUTES": { + f"v0/oracles/splits/{self.nodes[node].getblockcount() + 4}": f"{id}/2" + } + }, + self.nodes[node].getblockcount() + 2, + ) + self.nodes[node].generate(4) + + # Update the ID after split + id = list(self.nodes[node].gettoken("META").keys())[0] + + # Unlock token + self.nodes[node].setgovheight( + {"ATTRIBUTES": {f"v0/locks/token/{id}": "false"}}, + self.nodes[node].getblockcount() + 2, + ) + self.nodes[node].generate(2) + + # Check no collateral set + assert_equal( + self.nodes[node].gettoken("META")[f"{id}"]["collateralAddress"], "" + ) + + # Update DAT token. Will use owner auth. + self.nodes[node].updatetoken( + "META", + { + "name": "META", + }, + ) + self.nodes[node].generate(1) + + # Check loan token renamed + assert_equal(self.nodes[node].gettoken("META")[id]["name"], "META") + def test_setting_governance(self): # Rollback block self.rollback_to_start() @@ -940,14 +993,7 @@ def governance_oracles(self): self.nodes[1].setoracledata(oracle_tx, int(time.time()), oracle_prices) self.nodes[1].generate(1) - def governance_loans(self): - - # Rollback block - self.rollback_to_start() - - # Enable Governance - self.enable_governance(self.governance_member) - + def create_meta_loan_token(self, node): # Price feeds price_feeds = [ {"currency": "USD", "token": "META"}, @@ -955,9 +1001,9 @@ def governance_loans(self): ] # Appoint Oracle - oracle_address = self.nodes[1].getnewaddress() - oracle_tx = self.nodes[1].appointoracle(oracle_address, price_feeds, 10) - self.nodes[1].generate(1) + oracle_address = self.nodes[node].getnewaddress() + oracle_tx = self.nodes[node].appointoracle(oracle_address, price_feeds, 10) + self.nodes[node].generate(1) # Feed oracle oracle_prices = [ @@ -966,17 +1012,17 @@ def governance_loans(self): ] # Set Oracle data - self.nodes[1].setoracledata(oracle_tx, int(time.time()), oracle_prices) - self.nodes[1].generate(1) + self.nodes[node].setoracledata(oracle_tx, int(time.time()), oracle_prices) + self.nodes[node].generate(1) # Set collateral token - self.nodes[1].setcollateraltoken( + self.nodes[node].setcollateraltoken( {"token": 0, "factor": 1, "fixedIntervalPriceId": "DFI/USD"} ) - self.nodes[1].generate(1) + self.nodes[node].generate(1) # Set loan token - self.nodes[1].setloantoken( + self.nodes[node].setloantoken( { "symbol": "META", "name": "Facebook", @@ -985,10 +1031,22 @@ def governance_loans(self): "interest": 0, } ) - self.nodes[1].generate(1) + self.nodes[node].generate(1) + + return list(self.nodes[node].gettoken("META").keys())[0] + + def governance_loans(self): + # Rollback block + self.rollback_to_start() + + # Enable Governance + self.enable_governance(self.governance_member) + + # Create loan token + id = self.create_meta_loan_token(1) - # Check loan token created - assert_equal(self.nodes[1].gettoken("META")["2"]["name"], "Facebook") + # Check loan token name + assert_equal(self.nodes[1].gettoken("META")[f"{id}"]["name"], "Facebook") # Update loan token self.nodes[1].updateloantoken( @@ -1000,7 +1058,7 @@ def governance_loans(self): self.nodes[1].generate(1) # Check loan token updated - assert_equal(self.nodes[1].gettoken("META")["2"]["name"], "META") + assert_equal(self.nodes[1].gettoken("META")[f"{id}"]["name"], "META") # Create loan scheme self.nodes[1].createloanscheme(150, 1, "LOAN1") From 7e7edd5a4d75c013edb5bb04876429d9da526d7c Mon Sep 17 00:00:00 2001 From: Prasanna Loganathar Date: Fri, 11 Oct 2024 16:41:09 +0800 Subject: [PATCH 2/3] simplify branch --- src/dfi/consensus/tokens.cpp | 41 ++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/src/dfi/consensus/tokens.cpp b/src/dfi/consensus/tokens.cpp index 7a8145bfc7..65080e8857 100644 --- a/src/dfi/consensus/tokens.cpp +++ b/src/dfi/consensus/tokens.cpp @@ -200,28 +200,29 @@ Res CTokensConsensus::operator()(const CUpdateTokenMessage &obj) const { if (!ownerAuth) { // Governance or foundation can still mark/unmark token deprecation - if (auto res = authCheck.HasGovOrFoundationAuth(); res) { - // Limit update token for governance and foundation for non-loan tokens - if (!mnview.GetLoanTokenByID(tokenID)) { - // Allow only deprecation. We disallow changes like name or symbol - // as a token holder shouldn't be misrepresented by governance. - // Governance can choose completely discard it by deprecating it - // or keep it in the form as intended by the owner. - - const auto toggledFlags = static_cast(updatedToken.flags ^ token.flags); - const auto hasDisallowedFlagToggle = - toggledFlags != static_cast(CToken::TokenFlags::Deprecated); - - const auto disallowedChanges = hasDisallowedFlagToggle || updatedToken.symbol != token.symbol || - updatedToken.name != token.name || obj.newCollateralAddress; - - if (disallowedChanges) { - return Res::Err("Only token deprecation toggle is allowed by governance"); - } - } - } else { + if (auto res = authCheck.HasGovOrFoundationAuth(); !res) { return Res::Err("Authentication failed for token owner"); } + + // If it's loan token, that's owned by gov, so we don't need to disallow. + // Limit update token for governance and foundation for non-loan tokens + if (!mnview.GetLoanTokenByID(tokenID)) { + // Allow only deprecation. We disallow changes like name or symbol + // as a token holder shouldn't be misrepresented by governance. + // Governance can choose completely discard it by deprecating it + // or keep it in the form as intended by the owner. + + const auto toggledFlags = static_cast(updatedToken.flags ^ token.flags); + const auto hasDisallowedFlagToggle = + toggledFlags != static_cast(CToken::TokenFlags::Deprecated); + + const auto disallowedChanges = hasDisallowedFlagToggle || updatedToken.symbol != token.symbol || + updatedToken.name != token.name || obj.newCollateralAddress; + + if (disallowedChanges) { + return Res::Err("Only token deprecation toggle is allowed by governance"); + } + } } if (obj.newCollateralAddress) { From 4ff91ca7003d7edd7116ae6f1bf1a8daa181e689 Mon Sep 17 00:00:00 2001 From: Bushstar Date: Fri, 11 Oct 2024 10:01:50 +0100 Subject: [PATCH 3/3] lint: Format CPP --- src/dfi/consensus/tokens.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dfi/consensus/tokens.cpp b/src/dfi/consensus/tokens.cpp index 65080e8857..87cc8ab27e 100644 --- a/src/dfi/consensus/tokens.cpp +++ b/src/dfi/consensus/tokens.cpp @@ -203,7 +203,7 @@ Res CTokensConsensus::operator()(const CUpdateTokenMessage &obj) const { if (auto res = authCheck.HasGovOrFoundationAuth(); !res) { return Res::Err("Authentication failed for token owner"); } - + // If it's loan token, that's owned by gov, so we don't need to disallow. // Limit update token for governance and foundation for non-loan tokens if (!mnview.GetLoanTokenByID(tokenID)) { @@ -217,7 +217,7 @@ Res CTokensConsensus::operator()(const CUpdateTokenMessage &obj) const { toggledFlags != static_cast(CToken::TokenFlags::Deprecated); const auto disallowedChanges = hasDisallowedFlagToggle || updatedToken.symbol != token.symbol || - updatedToken.name != token.name || obj.newCollateralAddress; + updatedToken.name != token.name || obj.newCollateralAddress; if (disallowedChanges) { return Res::Err("Only token deprecation toggle is allowed by governance");