Browse Source

Export fix

pull/434/head
miketout 2 years ago
parent
commit
6fe56ebdef
  1. 89
      src/pbaas/crosschainrpc.h
  2. 53
      src/pbaas/identity.cpp
  3. 24
      src/pbaas/identity.h
  4. 50
      src/pbaas/pbaas.cpp
  5. 42
      src/rpc/pbaasrpc.cpp
  6. 12
      src/script/script.cpp

89
src/pbaas/crosschainrpc.h

@ -175,8 +175,9 @@ public:
DEST_QUANTUM = 7,
DEST_NESTEDTRANSFER = 8, // used to chain transfers, enabling them to be routed through multiple systems
DEST_ETH = 9,
DEST_RAW = 10,
LAST_VALID_TYPE_NO_FLAGS = 10,
DEST_ETHNFT = 10, // used when defining a mapped NFT to gateway that uses an ETH compatible model
DEST_RAW = 11,
LAST_VALID_TYPE_NO_FLAGS = DEST_RAW,
FLAG_DEST_AUX = 64,
FLAG_DEST_GATEWAY = 128,
FLAG_MASK = FLAG_DEST_AUX + FLAG_DEST_GATEWAY
@ -313,6 +314,36 @@ public:
return "0x" + HexBytes(ethDestID.begin(), ethDestID.size());
}
static std::pair<uint160, uint256> DecodeEthNFTDestination(const std::string &destStr)
{
uint160 retContract;
uint256 retTokenID;
UniValue nftJSON(UniValue::VOBJ);
nftJSON.read(destStr);
std::string contractAddrStr = uni_get_str(find_value(nftJSON, "contract"));
std::string TokenIDStr = uni_get_str(find_value(nftJSON, "tokenid"));
if (!(retContract = DecodeEthDestination(contractAddrStr)).IsNull() &&
TokenIDStr.length() == 66 &&
destStr.substr(0,2) == "0x" &&
IsHex(TokenIDStr.substr(2,64)))
{
retTokenID = uint256S(TokenIDStr.substr(2,64));
return std::make_pair(retContract, uint256S(TokenIDStr));
}
else
{
return std::make_pair(uint160(), uint256());
}
}
static std::string EncodeEthNFTDestination(const uint160 &ethContractID, const uint256 &tokenID)
{
// reverse bytes to match ETH encoding
return "{\"contract\":\"0x" + HexBytes(ethContractID.begin(), ethContractID.size()) + "\", \"tokenid\":\"0x" + HexBytes(tokenID.begin(), tokenID.size()) + "\"}";
}
static std::string CurrencyExportKeyName()
{
return "vrsc::system.currency.export";
@ -332,6 +363,40 @@ public:
UniValue ToUniValue() const;
};
class CNFTAddress
{
public:
uint32_t version;
CTransferDestination rootContractOrID;
std::vector<uint160> shortHashes;
std::vector<uint256> longHashes;
enum EVersions {
VERSION_INVALID = 0,
VERSION_VERUSID = 1,
VERSION_FIRST = 1,
VERSION_DEFAULT = 1,
VERSION_LAST = 1
};
CNFTAddress(const UniValue &uni);
CNFTAddress(uint32_t ver=VERSION_DEFAULT) : version(ver) {}
CNFTAddress(const CTransferDestination &rootDest, const std::vector<uint160> &shorts, const std::vector<uint256> &longs, uint32_t ver=VERSION_DEFAULT) :
version(ver), rootContractOrID(rootDest), shortHashes(shorts), longHashes(longs) {}
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action) {
READWRITE(VARINT(version));
READWRITE(rootContractOrID);
READWRITE(shortHashes);
READWRITE(longHashes);
}
UniValue ToUniValue() const;
};
extern int64_t AmountFromValueNoErr(const UniValue& value);
// convenience class for collections of currencies that supports comparisons, including ==, >, >=, <, <=, as well as addition, and subtraction
@ -444,6 +509,8 @@ public:
OPTION_PBAAS = 0x100, // this is a PBaaS chain definition
OPTION_GATEWAY_CONVERTER = 0x200, // this means that for a specific PBaaS gateway, this is the default converter and will publish prices
OPTION_GATEWAY_NAMECONTROLLER = 0x400, // when not set on a gateway, top level ID and currency registration happen on launch chain
OPTION_NFT_TOKEN = 0x800, // single satoshi NFT token, tokenizes control over the root ID
OPTIONS_FLAG_MASK = 0xfff
};
// these should be pluggable in function
@ -968,6 +1035,7 @@ public:
bool IsValid() const
{
return (nVersion != PBAAS_VERSION_INVALID) &&
!(options & ~OPTIONS_FLAG_MASK) &&
idReferralLevels <= MAX_ID_REFERRAL_LEVELS &&
name.size() > 0 &&
name.size() <= (KOMODO_ASSETCHAIN_MAXLEN - 1) &&
@ -993,6 +1061,11 @@ public:
return ChainOptions() & OPTION_TOKEN;
}
bool IsNFTToken() const
{
return ChainOptions() & OPTION_NFT_TOKEN;
}
bool IsGateway() const
{
return ChainOptions() & OPTION_GATEWAY;
@ -1031,6 +1104,18 @@ public:
}
}
void SetNFTToken(bool isToken)
{
if (isToken)
{
options |= OPTION_NFT_TOKEN;
}
else
{
options &= ~OPTION_NFT_TOKEN;
}
}
std::map<uint160, int32_t> GetCurrenciesMap() const
{
std::map<uint160, int32_t> retVal;

53
src/pbaas/identity.cpp

@ -74,9 +74,10 @@ bool CIdentity::IsInvalidMutation(const CIdentity &newIdentity, uint32_t height,
GetID() != newIdentity.GetID() ||
((newIdentity.flags & ~FLAG_REVOKED) && newIdentity.nVersion < VERSION_VAULT) ||
((newIdentity.flags & ~(FLAG_REVOKED + FLAG_LOCKED)) && newIdentity.nVersion < VERSION_PBAAS) ||
((newIdentity.flags & ~(FLAG_REVOKED + FLAG_ACTIVECURRENCY + FLAG_LOCKED)) && (newIdentity.nVersion >= VERSION_PBAAS)) ||
((newIdentity.flags & ~(FLAG_REVOKED + FLAG_ACTIVECURRENCY + FLAG_LOCKED + FLAG_TOKENIZED_CONTROL)) && (newIdentity.nVersion >= VERSION_PBAAS)) ||
(IsLocked(height) && (!newIdentity.IsRevoked() && !newIdentity.IsLocked(height))) ||
((flags & FLAG_ACTIVECURRENCY) && !(newIdentity.flags & FLAG_ACTIVECURRENCY)) ||
(HasActiveCurrency() && !HasActiveCurrency()) ||
(HasTokenizedControl() && !HasTokenizedControl()) ||
newIdentity.nVersion < VERSION_FIRSTVALID ||
newIdentity.nVersion > VERSION_LASTVALID)
{
@ -2186,16 +2187,38 @@ bool ValidateIdentityRevoke(struct CCcontract_info *cp, Eval* eval, const CTrans
spendingTx.vout[idIndex].scriptPubKey.IsPayToCryptoCondition(q);
if (q.evalCode != EVAL_IDENTITY_PRIMARY)
if (!q.IsValid() || q.evalCode != EVAL_IDENTITY_PRIMARY)
{
return eval->Error("Invalid identity output in spending transaction");
}
bool advanced = newIdentity.nVersion >= newIdentity.VERSION_VAULT;
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 (spendingTx.vout.size() <= (idIndex + 1) || spendingTx.vin.size() <= (nIn + 1))
{
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;
}
}
}
if (advanced)
{
// if not fulfilled, neither recovery data nor its spend condition may be modified
// if not fulfilled, neither revocation data nor its spend condition may be modified
if (!fulfilled && !oldIdentity.IsRevoked())
{
if (oldIdentity.IsRevocation(newIdentity) || oldIdentity.IsRevocationMutation(newIdentity, height))
@ -2310,6 +2333,28 @@ bool ValidateIdentityRecover(struct CCcontract_info *cp, Eval* eval, const CTran
bool advanced = newIdentity.nVersion >= newIdentity.VERSION_VAULT;
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 (spendingTx.vout.size() <= (idIndex + 1) || spendingTx.vin.size() <= (nIn + 1))
{
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;
}
}
}
if (advanced)
{
// if not fulfilled, neither recovery data nor its spend condition may be modified

24
src/pbaas/identity.h

@ -255,6 +255,7 @@ public:
static const uint8_t VERSION_VERUSID = 1;
static const uint8_t VERSION_VAULT = 2;
static const uint8_t VERSION_PBAAS = 3;
static const uint8_t VERSION_CURRENT = VERSION_VAULT;
static const uint8_t VERSION_FIRSTVALID = 1;
static const uint8_t VERSION_LASTVALID = 3;
@ -383,6 +384,7 @@ public:
FLAG_REVOKED = 0x8000, // set when this identity is revoked
FLAG_ACTIVECURRENCY = 0x1, // flag that is set when this ID is being used as an active currency name
FLAG_LOCKED = 0x2, // set when this identity is locked
FLAG_TOKENIZED_CONTROL = 0x4, // set when revocation/recovery over this identity can be performed by anyone who controls its token
MAX_UNLOCK_DELAY = 60 * 24 * 22 * 365 // 21+ year maximum unlock time for an ID
};
@ -630,7 +632,7 @@ public:
{
if (nVersion == VERSION_FIRSTVALID)
{
nVersion = VERSION_VAULT;
nVersion = VERSION_CURRENT;
}
flags |= FLAG_ACTIVECURRENCY;
}
@ -645,6 +647,25 @@ public:
return flags & FLAG_ACTIVECURRENCY;
}
void ActivateTokenizedControl()
{
if (nVersion >= VERSION_FIRSTVALID && nVersion < VERSION_PBAAS)
{
nVersion = VERSION_PBAAS;
}
flags |= FLAG_TOKENIZED_CONTROL;
}
void DeactivateTokenizedControl()
{
flags &= ~FLAG_TOKENIZED_CONTROL;
}
bool HasTokenizedControl() const
{
return flags & FLAG_TOKENIZED_CONTROL;
}
bool IsValid(bool strict=false) const
{
bool isOK = true;
@ -733,6 +754,7 @@ public:
privateAddresses != newIdentity.privateAddresses ||
(unlockAfter != newIdentity.unlockAfter && (!isRevokedExempt || newIdentity.unlockAfter != 0)) ||
(HasActiveCurrency() != newIdentity.HasActiveCurrency()) ||
(HasTokenizedControl() != newIdentity.HasTokenizedControl()) ||
(IsLocked() != newIdentity.IsLocked() && (!isRevokedExempt || newIdentity.IsLocked())))
{
return true;

50
src/pbaas/pbaas.cpp

@ -1856,11 +1856,40 @@ bool PrecheckCurrencyDefinition(const CTransaction &spendingTx, int32_t outNum,
{
return state.Error("Parent currency invalid to issue identities on this chain");
}
// any ID with a gateway as its system ID can issue an NFT mapped currency with 0
// satoshi supply as its currency for the cost of an ID import, not a currency import
CCurrencyDefinition systemDef = newSystem;
if (newCurrency.systemID != ASSETCHAINS_CHAINID &&
newCurrency.launchSystemID == ASSETCHAINS_CHAINID &&
newCurrency.nativeCurrencyID.TypeNoFlags() == newCurrency.nativeCurrencyID.DEST_ETHNFT &&
!systemDef.IsValid())
{
systemDef = ConnectedChains.GetCachedCurrency(newCurrency.systemID);
}
bool isNFTMappedCurrency = systemDef.IsValid() &&
systemDef.IsGateway() &&
systemDef.proofProtocol == systemDef.PROOF_ETHNOTARIZATION &&
newCurrency.maxPreconvert.size() == 1 &&
newCurrency.maxPreconvert[0] == 0 &&
newCurrency.nativeCurrencyID.TypeNoFlags() == newCurrency.nativeCurrencyID.DEST_ETHNFT &&
!(newCurrency.options &
newCurrency.OPTION_FRACTIONAL +
newCurrency.OPTION_ID_ISSUANCE +
newCurrency.OPTION_GATEWAY +
newCurrency.OPTION_PBAAS +
newCurrency.OPTION_GATEWAY_CONVERTER) &&
newCurrency.IsToken() &&
!newCurrency.GetTotalPreallocation();
if (newIdentity.parent != ASSETCHAINS_CHAINID &&
!isNFTMappedCurrency &&
!(parentCurrency.IsGateway() && parentCurrency.launchSystemID == ASSETCHAINS_CHAINID && !parentCurrency.IsNameController()))
{
return state.Error("Only gateway and PBaaS identities may create currencies");
return state.Error("Only gateway and PBaaS identities may create non-NFT currencies");
}
if (newIdentity.parent != ASSETCHAINS_CHAINID)
{
if (!(parentCurrency.proofProtocol == parentCurrency.PROOF_ETHNOTARIZATION &&
@ -1870,8 +1899,10 @@ bool PrecheckCurrencyDefinition(const CTransaction &spendingTx, int32_t outNum,
!newCurrency.IsGateway() &&
newCurrency.IsToken() &&
newCurrency.systemID == newIdentity.parent &&
!newCurrency.GetTotalPreallocation() &&
newCurrency.maxPreconvert.size() == 1 &&
newCurrency.maxPreconvert[0] == 0))
newCurrency.maxPreconvert[0] == 0) &&
!isNFTMappedCurrency)
{
return state.Error("Gateway currencies must me mapped currencies via the gateway");
}
@ -6476,6 +6507,21 @@ void CConnectedChains::AggregateChainTransfers(const CTransferDestination &feeRe
CInputDescriptor(notarizationTxes[cnd.lastConfirmed].first.vout[cnd.vtx[cnd.lastConfirmed].first.n].scriptPubKey,
notarizationTxes[cnd.lastConfirmed].first.vout[cnd.vtx[cnd.lastConfirmed].first.n].nValue,
CTxIn(cnd.vtx[cnd.lastConfirmed].first));
if (destDef.systemID != ASSETCHAINS_CHAINID &&
cnd.vtx[cnd.lastConfirmed].second.IsLaunchConfirmed())
{
CChainNotarizationData systemCND;
if (GetNotarizationData(destDef.systemID, systemCND) &&
systemCND.lastConfirmed != -1 &&
systemCND.vtx[systemCND.lastConfirmed].second.currencyStates.count(lastChain) &&
systemCND.vtx[systemCND.lastConfirmed].second.currencyStates[lastChain].IsLaunchCompleteMarker())
{
lastNotarization.currencyState = systemCND.vtx[systemCND.lastConfirmed].second.currencyStates[lastChain];
lastNotarization.flags = systemCND.vtx[systemCND.lastConfirmed].second.flags;
}
}
CPBaaSNotarization newNotarization;
int newNotarizationOutNum;

42
src/rpc/pbaasrpc.cpp

@ -8257,6 +8257,10 @@ CCurrencyDefinition ValidateNewUnivalueCurrencyDefinition(const UniValue &uniObj
}
bool currentChainDefinition = newCurrency.GetID() == ASSETCHAINS_CHAINID && _IsVerusActive();
if (currentChainDefinition)
{
newCurrency = checkDef;
}
if (newCurrency.parent.IsNull() && !currentChainDefinition)
{
@ -8265,11 +8269,7 @@ CCurrencyDefinition ValidateNewUnivalueCurrencyDefinition(const UniValue &uniObj
for (auto &oneID : newCurrency.preAllocation)
{
if (currentChainDefinition)
{
newCurrency = checkDef;
}
else if (!CIdentity::LookupIdentity(CIdentityID(oneID.first)).IsValid())
if (!CIdentity::LookupIdentity(CIdentityID(oneID.first)).IsValid())
{
throw JSONRPCError(RPC_INVALID_PARAMETER, "attempting to pre-allocate currency to a non-existent ID.");
}
@ -8288,18 +8288,20 @@ CCurrencyDefinition ValidateNewUnivalueCurrencyDefinition(const UniValue &uniObj
throw JSONRPCError(RPC_INVALID_PARAMETER, "currency cannot be both a token and also specify a mining and staking rewards schedule.");
}
if (newCurrency.nativeCurrencyID.TypeNoFlags() == newCurrency.nativeCurrencyID.DEST_ETH &&
!newCurrency.IsGateway())
if ((newCurrency.nativeCurrencyID.TypeNoFlags() == newCurrency.nativeCurrencyID.DEST_ETH || newCurrency.nativeCurrencyID.TypeNoFlags() == newCurrency.nativeCurrencyID.DEST_ETHNFT))
{
if (newCurrency.IsFractional())
if (newCurrency.IsGateway() ||
newCurrency.IsPBaaSChain() ||
!newCurrency.IsToken() ||
newCurrency.IsFractional())
{
throw JSONRPCError(RPC_INVALID_PARAMETER, "currency cannot be both a mapped currency and fractional");
throw JSONRPCError(RPC_INVALID_PARAMETER, "mapped currency must be a token with no initial supply and cannot be otherwise functional");
}
if (newCurrency.proofProtocol != newCurrency.PROOF_ETHNOTARIZATION)
{
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Ethereum mapped currency must have \"proofprotocol\":%d", (int)newCurrency.PROOF_ETHNOTARIZATION));
}
bool nonZeroSupply = newCurrency.conversions.size() && !newCurrency.maxPreconvert.size();
bool nonZeroSupply = (newCurrency.conversions.size() && !newCurrency.maxPreconvert.size()) || newCurrency.GetTotalPreallocation();
for (auto oneVal : newCurrency.maxPreconvert)
{
if (oneVal)
@ -8307,7 +8309,7 @@ CCurrencyDefinition ValidateNewUnivalueCurrencyDefinition(const UniValue &uniObj
nonZeroSupply = true;
}
}
if (nonZeroSupply || newCurrency.GetTotalPreallocation())
if (nonZeroSupply)
{
throw JSONRPCError(RPC_INVALID_PARAMETER, "Mapped currency definition requires zero initial supply and no possible conversions");
}
@ -8632,12 +8634,17 @@ UniValue definecurrency(const UniValue& params, bool fHelp)
parentCurrency = ConnectedChains.ThisChain();
}
bool invalidUnlessNFT = false;
if (newChain.parent != thisChainID &&
!(isVerusActive && newChain.GetID() == ASSETCHAINS_CHAINID && newChain.parent.IsNull()) &&
!(parentCurrency.IsGateway() && !parentCurrency.IsNameController() && parentCurrency.launchSystemID == ASSETCHAINS_CHAINID))
{
// parent chain must be current chain or be VRSC or VRSCTEST registered by the owner of the associated ID
throw JSONRPCError(RPC_INVALID_PARAMETER, "Attempting to define a currency relative to a parent that is not a valid gateway or the current chain.");
invalidUnlessNFT = true;
if (!newChain.IsNFTToken() )
{
// parent chain must be current chain or be VRSC or VRSCTEST registered by the owner of the associated ID
throw JSONRPCError(RPC_INVALID_PARAMETER, "Attempting to define a currency relative to a parent that is not a valid gateway or the current chain.");
}
}
uint160 newChainID = newChain.GetID();
@ -8665,6 +8672,11 @@ UniValue definecurrency(const UniValue& params, bool fHelp)
throw JSONRPCError(RPC_INVALID_PARAMETER, "ID " + newChain.name + " not found, is revoked, or already has an active currency defined");
}
if (launchIdentity.systemID != ASSETCHAINS_CHAINID)
{
throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot launch any currency or tokenized ID control unless the root identity is rooted on this system");
}
CTransaction idTx;
uint256 blockHash;
if (!GetTransaction(idTxIn.prevout.hash, idTx, blockHash, true))
@ -8924,6 +8936,10 @@ UniValue definecurrency(const UniValue& params, bool fHelp)
// first, we need the identity output with currency activated
launchIdentity.UpgradeVersion(height);
launchIdentity.ActivateCurrency();
if (newChain.IsNFTToken())
{
launchIdentity.ActivateTokenizedControl();
}
tb.AddTransparentOutput(launchIdentity.IdentityUpdateOutputScript(height + 1), 0);
// now, create the currency definition output

12
src/script/script.cpp

@ -284,7 +284,6 @@ CTxDestination TransferDestinationToDestination(const CTransferDestination &tran
case CTransferDestination::DEST_ID:
case CTransferDestination::DEST_PK:
case CTransferDestination::DEST_PKH:
case CTransferDestination::DEST_ETH:
case CTransferDestination::DEST_SH:
return TransferDestinationToDestination(auxDest);
}
@ -295,6 +294,17 @@ CTxDestination TransferDestinationToDestination(const CTransferDestination &tran
break;
}
case CTransferDestination::DEST_ETHNFT:
{
CCcontract_info CC;
CCcontract_info *cp;
// this type is only used in currency definitions and does not make sense
// to be converted to an address at this time
cp = CCinit(&CC, EVAL_CURRENCY_DEFINITION);
retDest = CTxDestination(CPubKey(ParseHex(CC.CChexstr)));
break;
}
case CTransferDestination::DEST_QUANTUM:
retDest = CQuantumID(uint160(transferDest.destination));
break;

Loading…
Cancel
Save