Browse Source

Enable token-authorized revoke/recover + hardening

pull/451/head
miketout 2 years ago
parent
commit
ab900a2255
  1. 4
      src/httprpc.cpp
  2. 78
      src/pbaas/identity.cpp
  3. 151
      src/rpc/pbaasrpc.cpp
  4. 22
      src/wallet/rpcwallet.cpp

4
src/httprpc.cpp

@ -136,11 +136,11 @@ static bool HTTPReq_JSONRPC(HTTPRequest* req, const std::string &)
return false;
}
extern bool printoutAPI;
if (printoutAPI == true)
if (LogAcceptCategory("rpcapiconsole"))
{
printf("%s %s\n", jreq.strMethod.c_str(), jreq.params.write().c_str());
}
LogPrint("rpcapi", "%s %s\n", jreq.strMethod.c_str(), jreq.params.write().c_str());
UniValue result = tableRPC.execute(jreq.strMethod, jreq.params);

78
src/pbaas/identity.cpp

@ -19,6 +19,8 @@
#include "identity.h"
#include "txdb.h"
uint32_t TESTNET_FORK_HEIGHT = 1;
extern CTxMemPool mempool;
CCommitmentHash::CCommitmentHash(const CTransaction &tx)
@ -2374,6 +2376,12 @@ bool ValidateIdentityPrimary(struct CCcontract_info *cp, Eval* eval, const CTran
}
}
if (!fulfilled &&
!oldIdentity.HasActiveCurrency() &&
newIdentity.HasActiveCurrency())
{
return eval->Error("Unauthorized currency or token definition");
}
return true;
}
@ -2381,6 +2389,7 @@ bool ValidateIdentityRevoke(struct CCcontract_info *cp, Eval* eval, const CTrans
{
CTransaction sourceTx;
CIdentity oldIdentity = GetOldIdentity(spendingTx, nIn, &sourceTx);
if (!oldIdentity.IsValid())
{
return eval->Error("Invalid source identity");
@ -2392,6 +2401,8 @@ bool ValidateIdentityRevoke(struct CCcontract_info *cp, Eval* eval, const CTrans
}
uint32_t height = chainActive.LastTip()->GetHeight() + 1;
bool currencySigEnforcement = PBAAS_TESTMODE && height > TESTNET_FORK_HEIGHT;
bool advancedIdentity = CVerusSolutionVector::GetVersionByHeight(height) >= CActivationHeight::ACTIVATE_VERUSVAULT;
int idIndex;
@ -2425,22 +2436,29 @@ bool ValidateIdentityRevoke(struct CCcontract_info *cp, Eval* eval, const CTrans
uint160 identityID = oldIdentity.GetID();
// before we start conditioning decisions on fulfilled status,
// check to see if it has been fulfilled by using a control token/NFT
if (!fulfilled && oldIdentity.HasTokenizedControl())
if (!fulfilled)
{
if (spendingTx.vout.size() <= (idIndex + 1) || spendingTx.vin.size() <= (nIn + 1))
if (!oldIdentity.HasActiveCurrency() &&
newIdentity.HasActiveCurrency())
{
return eval->Error("Missing revocation signature. All authorities must sign for currency or token definition");
}
if (oldIdentity.HasTokenizedControl())
{
CAmount controlCurrencyVal = spendingTx.vout[idIndex + 1].ReserveOutValue().valueMap[identityID];
CTransaction tokenOutTx;
uint256 hashBlock;
COptCCParams tokenP;
if (controlCurrencyVal > 0 &&
myGetTransaction(spendingTx.vin[nIn + 1].prevout.hash, tokenOutTx, hashBlock) &&
tokenOutTx.vout[spendingTx.vin[nIn + 1].prevout.n].ReserveOutValue().valueMap[identityID] == controlCurrencyVal &&
tokenOutTx.vout[spendingTx.vin[nIn + 1].prevout.n].scriptPubKey == spendingTx.vout[idIndex + 1].scriptPubKey)
if (spendingTx.vout.size() <= (idIndex + 1) || spendingTx.vin.size() <= (nIn + 1))
{
fulfilled = true;
CAmount controlCurrencyVal = spendingTx.vout[idIndex + 1].ReserveOutValue().valueMap[identityID];
CTransaction tokenOutTx;
uint256 hashBlock;
COptCCParams tokenP;
if (controlCurrencyVal > 0 &&
myGetTransaction(spendingTx.vin[nIn + 1].prevout.hash, tokenOutTx, hashBlock) &&
tokenOutTx.vout[spendingTx.vin[nIn + 1].prevout.n].ReserveOutValue().valueMap[identityID] == controlCurrencyVal &&
tokenOutTx.vout[spendingTx.vin[nIn + 1].prevout.n].scriptPubKey == spendingTx.vout[idIndex + 1].scriptPubKey)
{
fulfilled = true;
}
}
}
}
@ -2493,7 +2511,7 @@ bool ValidateIdentityRevoke(struct CCcontract_info *cp, Eval* eval, const CTrans
{
sourceTx.vout[spendingTx.vin[nIn].prevout.n].scriptPubKey.IsPayToCryptoCondition(p);
if (oldIdentity.IsRevocation(newIdentity) || oldIdentity.IsRevocationMutation(newIdentity, height))
if (currencySigEnforcement && (oldIdentity.IsRevocation(newIdentity) || oldIdentity.IsRevocationMutation(newIdentity, height)))
{
return eval->Error("Unauthorized modification of revocation information");
}
@ -2536,6 +2554,8 @@ bool ValidateIdentityRecover(struct CCcontract_info *cp, Eval* eval, const CTran
}
uint32_t height = chainActive.LastTip()->GetHeight() + 1;
bool currencySigEnforcement = PBAAS_TESTMODE && height > TESTNET_FORK_HEIGHT;
bool advancedIdentity = CVerusSolutionVector::GetVersionByHeight(height) >= CActivationHeight::ACTIVATE_VERUSVAULT;
int idIndex;
@ -2566,20 +2586,28 @@ bool ValidateIdentityRecover(struct CCcontract_info *cp, Eval* eval, const CTran
// before we start conditioning decisions on fulfilled status,
// check to see if it has been fulfilled by using a control token/NFT
if (!fulfilled && oldIdentity.HasTokenizedControl())
if (!fulfilled)
{
if (spendingTx.vout.size() <= (idIndex + 1) || spendingTx.vin.size() <= (nIn + 1))
if (currencySigEnforcement && (!oldIdentity.HasActiveCurrency() && newIdentity.HasActiveCurrency()))
{
CAmount controlCurrencyVal = spendingTx.vout[idIndex + 1].ReserveOutValue().valueMap[identityID];
CTransaction tokenOutTx;
uint256 hashBlock;
COptCCParams tokenP;
if (controlCurrencyVal > 0 &&
myGetTransaction(spendingTx.vin[nIn + 1].prevout.hash, tokenOutTx, hashBlock) &&
tokenOutTx.vout[spendingTx.vin[nIn + 1].prevout.n].ReserveOutValue().valueMap[identityID] == controlCurrencyVal &&
tokenOutTx.vout[spendingTx.vin[nIn + 1].prevout.n].scriptPubKey == spendingTx.vout[idIndex + 1].scriptPubKey)
return eval->Error("Missing recovery signature. All authorities must sign for currency or token definition");
}
if (oldIdentity.HasTokenizedControl())
{
if (spendingTx.vout.size() <= (idIndex + 1) || spendingTx.vin.size() <= (nIn + 1))
{
fulfilled = true;
CAmount controlCurrencyVal = spendingTx.vout[idIndex + 1].ReserveOutValue().valueMap[identityID];
CTransaction tokenOutTx;
uint256 hashBlock;
COptCCParams tokenP;
if (controlCurrencyVal > 0 &&
myGetTransaction(spendingTx.vin[nIn + 1].prevout.hash, tokenOutTx, hashBlock) &&
tokenOutTx.vout[spendingTx.vin[nIn + 1].prevout.n].ReserveOutValue().valueMap[identityID] == controlCurrencyVal &&
tokenOutTx.vout[spendingTx.vin[nIn + 1].prevout.n].scriptPubKey == spendingTx.vout[idIndex + 1].scriptPubKey)
{
fulfilled = true;
}
}
}
}

151
src/rpc/pbaasrpc.cpp

@ -10673,14 +10673,17 @@ UniValue MapToUniObject(const std::map<std::string, UniValue> &uniMap)
UniValue updateidentity(const UniValue& params, bool fHelp)
{
if (fHelp || params.size() < 1 || params.size() > 2)
if (fHelp || params.size() < 1 || params.size() > 3)
{
throw runtime_error(
"updateidentity \"jsonidentity\" (returntx)\n"
"updateidentity \"jsonidentity\" (returntx) (tokenupdate)\n"
"\n\n"
"\nArguments\n"
" \"returntx\" (bool, optional) defaults to false and transaction is sent, if true, transaction is signed by this wallet and returned\n"
" \"tokenupdate\" (bool, optional) defaults to false, if true, the tokenized ID control token, if one exists, will be used to update\n"
" which enables changing the revocation or recovery IDs, even if the wallet holding the token does not\n"
" control either.\n"
"\nResult:\n"
" hex string of either the txid if returnhex is false or the hex serialized transaction if returntx is true\n"
@ -10694,11 +10697,18 @@ UniValue updateidentity(const UniValue& params, bool fHelp)
CheckIdentityAPIsValid();
bool returnTx = false;
bool tokenizedIDControl = false;
if (params.size() > 1)
{
returnTx = uni_get_bool(params[1], false);
}
if (params.size() > 2)
{
tokenizedIDControl = uni_get_bool(params[2], false);
}
uint160 parentID = uint160(GetDestinationID(DecodeDestination(uni_get_str(find_value(params[0], "parent")))));
if (parentID.IsNull())
{
@ -10757,14 +10767,17 @@ UniValue updateidentity(const UniValue& params, bool fHelp)
CIdentity revocationAuth = newID.revocationAuthority == newIDID ? newID : newID.LookupIdentity(newID.revocationAuthority);
CIdentity recoveryAuth = newID.recoveryAuthority == newIDID ? newID : newID.LookupIdentity(newID.recoveryAuthority);
if (!revocationAuth.IsValid() || !recoveryAuth.IsValid())
if (tokenizedIDControl)
{
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid revocation or recovery authority");
if (!oldID.HasTokenizedControl())
{
throw JSONRPCError(RPC_INVALID_PARAMETER, "Can only used ID control token for ID that has tokenized ID control on this chain");
}
}
if (!recoveryAuth.IsValidUnrevoked() || !revocationAuth.IsValidUnrevoked())
if (!revocationAuth.IsValid() || !recoveryAuth.IsValid())
{
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid or revoked recovery, or revocation identity.");
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid revocation or recovery authority");
}
CMutableTransaction txNew = CreateNewContextualCMutableTransaction(Params().GetConsensus(), nHeight + 1);
@ -10790,6 +10803,24 @@ UniValue updateidentity(const UniValue& params, bool fHelp)
}
}
// if we are supposed to get our authority from the token, make sure it is present and prepare to spend it
std::vector<COutput> controlTokenOuts;
CCurrencyValueMap tokenCurrencyControlMap(std::vector<uint160>({newIDID}), std::vector<int64_t>({1}));
if (tokenizedIDControl)
{
COptCCParams tcP;
std::map<uint160, int64_t> reserveMap;
pwalletMain->AvailableReserveCoins(controlTokenOuts, true, nullptr, false, false, nullptr, &tokenCurrencyControlMap, false);
if (!controlTokenOuts.size() != 1 ||
!controlTokenOuts[0].fSpendable ||
!(reserveMap = controlTokenOuts[0].tx->vout[controlTokenOuts[0].i].ReserveOutValue().valueMap).count(newIDID) ||
reserveMap[newIDID] != 1)
{
throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot locate spendable tokenized ID control currency in wallet - if present, may require rescan");
}
}
// create the identity definition transaction
std::vector<CRecipient> outputs = std::vector<CRecipient>({{newID.IdentityUpdateOutputScript(nHeight + 1), 0, false}});
CWalletTx wtx;
@ -10808,6 +10839,22 @@ UniValue updateidentity(const UniValue& params, bool fHelp)
// add the spend of the last ID transaction output
mtx.vin.push_back(idTxIn);
// if we are using tokenized ID control, add the input and output to the transaction before signing
if (tokenizedIDControl)
{
mtx.vin.push_back(CTxIn(controlTokenOuts[0].tx->GetHash(), controlTokenOuts[0].i));
// just in case a change output was inserted, we loop to find the first output with zero out
// and put the spend just after
for (auto it = mtx.vout.begin(); it != mtx.vout.end(); it++)
{
if (!it->nValue)
{
mtx.vout.insert(it + 1, CTxOut(controlTokenOuts[0].tx->vout[controlTokenOuts[0].i]));
}
}
}
*static_cast<CTransaction*>(&wtx) = CTransaction(mtx);
// now sign
@ -10937,14 +10984,15 @@ UniValue setidentitytimelock(const UniValue& params, bool fHelp)
UniValue revokeidentity(const UniValue& params, bool fHelp)
{
if (fHelp || params.size() < 1 || params.size() > 2)
if (fHelp || params.size() < 1 || params.size() > 3)
{
throw runtime_error(
"revokeidentity \"nameorID\" (returntx)\n"
"revokeidentity \"nameorID\" (returntx) (tokenrevoke)\n"
"\n\n"
"\nArguments\n"
" \"returntx\" (bool, optional) defaults to false and transaction is sent, if true, transaction is signed by this wallet and returned\n"
" \"tokenrevoke\" (bool, optional) defaults to false, if true, the tokenized ID control token, if one exists, will be used to revoke\n"
"\nResult:\n"
@ -10958,6 +11006,8 @@ UniValue revokeidentity(const UniValue& params, bool fHelp)
// get identity
bool returnTx = false;
bool tokenizedIDControl = false;
CTxDestination idDest = DecodeDestination(uni_get_str(params[0]));
if (idDest.which() != COptCCParams::ADDRTYPE_ID)
@ -10972,6 +11022,11 @@ UniValue revokeidentity(const UniValue& params, bool fHelp)
returnTx = uni_get_bool(params[1], false);
}
if (params.size() > 2)
{
tokenizedIDControl = uni_get_bool(params[2], false);
}
CTxIn idTxIn;
CIdentity oldID;
uint32_t idHeight;
@ -10983,6 +11038,24 @@ UniValue revokeidentity(const UniValue& params, bool fHelp)
throw JSONRPCError(RPC_INVALID_PARAMETER, "ID not found " + EncodeDestination(idID));
}
// if we are supposed to get our authority from the token, make sure it is present and prepare to spend it
std::vector<COutput> controlTokenOuts;
CCurrencyValueMap tokenCurrencyControlMap(std::vector<uint160>({idID}), std::vector<int64_t>({1}));
if (tokenizedIDControl)
{
COptCCParams tcP;
std::map<uint160, int64_t> reserveMap;
pwalletMain->AvailableReserveCoins(controlTokenOuts, true, nullptr, false, false, nullptr, &tokenCurrencyControlMap, false);
if (!controlTokenOuts.size() != 1 ||
!controlTokenOuts[0].fSpendable ||
!(reserveMap = controlTokenOuts[0].tx->vout[controlTokenOuts[0].i].ReserveOutValue().valueMap).count(idID) ||
reserveMap[idID] != 1)
{
throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot locate spendable tokenized ID control currency in wallet - if present, may require rescan");
}
}
CIdentity newID(oldID);
newID.UpgradeVersion(chainActive.Height() + 1);
newID.Revoke();
@ -11004,7 +11077,21 @@ UniValue revokeidentity(const UniValue& params, bool fHelp)
// add the spend of the last ID transaction output
mtx.vin.push_back(idTxIn);
// all of the reservation output is actually the fee offer, so zero the output
// if we are using tokenized ID control, add the input and output to the transaction before signing
if (tokenizedIDControl)
{
mtx.vin.push_back(CTxIn(controlTokenOuts[0].tx->GetHash(), controlTokenOuts[0].i));
// just in case a change output was inserted, we loop to find the first output with zero out
// and put the spend just after
for (auto it = mtx.vout.begin(); it != mtx.vout.end(); it++)
{
if (!it->nValue)
{
mtx.vout.insert(it + 1, CTxOut(controlTokenOuts[0].tx->vout[controlTokenOuts[0].i]));
}
}
}
*static_cast<CTransaction*>(&wtx) = CTransaction(mtx);
// now sign
@ -11051,11 +11138,12 @@ UniValue recoveridentity(const UniValue& params, bool fHelp)
if (fHelp || params.size() < 1 || params.size() > 2)
{
throw runtime_error(
"recoveridentity \"jsonidentity\" (returntx)\n"
"recoveridentity \"jsonidentity\" (returntx) (tokenrecover)\n"
"\n\n"
"\nArguments\n"
" \"returntx\" (bool, optional) defaults to false and transaction is sent, if true, transaction is signed by this wallet and returned\n"
" \"tokenrecover\" (bool, optional) defaults to false, if true, the tokenized ID control token, if one exists, will be used to recover\n"
"\nResult:\n"
@ -11068,6 +11156,7 @@ UniValue recoveridentity(const UniValue& params, bool fHelp)
// get identity
bool returnTx = false;
bool tokenizedIDControl = false;
LOCK2(cs_main, pwalletMain->cs_wallet);
@ -11088,6 +11177,7 @@ UniValue recoveridentity(const UniValue& params, bool fHelp)
}
CIdentity newID(newUniIdentity);
uint160 newIDID = newID.GetID();
if (!newID.IsValid(true))
{
@ -11099,11 +11189,16 @@ UniValue recoveridentity(const UniValue& params, bool fHelp)
returnTx = uni_get_bool(params[1], false);
}
if (params.size() > 2)
{
tokenizedIDControl = uni_get_bool(params[2], false);
}
CTxIn idTxIn;
CIdentity oldID;
uint32_t idHeight;
if (!(oldID = CIdentity::LookupIdentity(newID.GetID(), 0, &idHeight, &idTxIn)).IsValid())
if (!(oldID = CIdentity::LookupIdentity(newIDID, 0, &idHeight, &idTxIn)).IsValid())
{
throw JSONRPCError(RPC_INVALID_PARAMETER, "ID not found " + newID.ToUniValue().write());
}
@ -11113,6 +11208,24 @@ UniValue recoveridentity(const UniValue& params, bool fHelp)
throw JSONRPCError(RPC_INVALID_PARAMETER, "Identity must be revoked in order to recover : " + newID.name);
}
// if we are supposed to get our authority from the token, make sure it is present and prepare to spend it
std::vector<COutput> controlTokenOuts;
CCurrencyValueMap tokenCurrencyControlMap(std::vector<uint160>({newIDID}), std::vector<int64_t>({1}));
if (tokenizedIDControl)
{
COptCCParams tcP;
std::map<uint160, int64_t> reserveMap;
pwalletMain->AvailableReserveCoins(controlTokenOuts, true, nullptr, false, false, nullptr, &tokenCurrencyControlMap, false);
if (!controlTokenOuts.size() != 1 ||
!controlTokenOuts[0].fSpendable ||
!(reserveMap = controlTokenOuts[0].tx->vout[controlTokenOuts[0].i].ReserveOutValue().valueMap).count(newIDID) ||
reserveMap[newIDID] != 1)
{
throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot locate spendable tokenized ID control currency in wallet - if present, may require rescan");
}
}
newID.flags &= ~CIdentity::FLAG_REVOKED;
newID.systemID = oldID.systemID;
newID.UpgradeVersion(nHeight + 1);
@ -11135,7 +11248,21 @@ UniValue recoveridentity(const UniValue& params, bool fHelp)
// add the spend of the last ID transaction output
mtx.vin.push_back(idTxIn);
// all of the reservation output is actually the fee offer, so zero the output
// if we are using tokenized ID control, add the input and output to the transaction before signing
if (tokenizedIDControl)
{
mtx.vin.push_back(CTxIn(controlTokenOuts[0].tx->GetHash(), controlTokenOuts[0].i));
// just in case a change output was inserted, we loop to find the first output with zero out
// and put the spend just after
for (auto it = mtx.vout.begin(); it != mtx.vout.end(); it++)
{
if (!it->nValue)
{
mtx.vout.insert(it + 1, CTxOut(controlTokenOuts[0].tx->vout[controlTokenOuts[0].i]));
}
}
}
*static_cast<CTransaction*>(&wtx) = CTransaction(mtx);
// now sign

22
src/wallet/rpcwallet.cpp

@ -1248,27 +1248,6 @@ UniValue signmessage(const UniValue& params, bool fHelp)
uint256 HashFile(std::string filepath);
bool printoutAPI = false;
UniValue printapis(const UniValue& params, bool fHelp)
{
if (fHelp || params.size() != 1)
throw runtime_error(
"printapis trueorfalse\n"
"\nPrints the name of all APIs if parameter is true"
"\nResult:\n"
""
"\nExamples:\n"
+ HelpExampleCli("signfile", "\"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV\" \"filepath/filename\"") +
"\nVerify the signature\n"
+ HelpExampleCli("verifyfile", "\"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV\" \"signature\" \"filepath/filename\"") +
"\nAs json rpc\n"
+ HelpExampleRpc("signfile", "\"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV\", \"filepath/filename\"")
);
printoutAPI = uni_get_bool(params[0]);
return NullUniValue;
}
UniValue signfile(const UniValue& params, bool fHelp)
{
if (!EnsureWalletIsAvailable(fHelp))
@ -8140,7 +8119,6 @@ static const CRPCCommand commands[] =
{ "wallet", "settxfee", &settxfee, true },
{ "identity", "signmessage", &signmessage, true },
{ "identity", "signfile", &signfile, true },
{ "hidden", "printapis", &printapis, true },
// { "hidden", "signhash", &signhash, true }, // disable due to risk of signing something that doesn't contain the content
{ "wallet", "openwallet", &openwallet, true },
{ "wallet", "walletlock", &walletlock, true },

Loading…
Cancel
Save