Commit 69731da8 authored by Artem B's avatar Artem B

Implement NFT tx signing strategy - self-sign, proto-sign, any-sign

parent 53fd591e
Pipeline #1081 passed with stage
in 37 minutes and 14 seconds
......@@ -11,6 +11,13 @@
namespace Platform
{
enum class NftRegSign : uint8_t
{
SignAny,
SelfSign,
SignByCreator
};
class NfTokenProtocol
{
public:
......@@ -20,8 +27,7 @@ namespace Platform
uint64_t tokenProtocolId;
/// Full name for this NF token type/protocol
/// Minimum length 3 symbols, maximum length 24(TODO:?) symbols
/// Characters TODO:
/// Minimum length 3 symbols, maximum length 24 symbols
std::string tokenProtocolName;
/// URI to schema (json/xml/binary) describing metadata format
......@@ -40,6 +46,9 @@ namespace Platform
/// It's recommended to use embedded metadata only if it's shorter than URI
bool isMetadataEmbedded;
/// Defines who must sign an NFT registration transaction
uint8_t nftRegSign;
/// Owner of the NF token protocol
CKeyID tokenProtocolOwnerId;
......@@ -55,6 +64,7 @@ namespace Platform
READWRITE(isTokenTransferable);
READWRITE(isTokenImmutable);
READWRITE(isMetadataEmbedded);
READWRITE(nftRegSign);
READWRITE(tokenProtocolOwnerId);
}
};
......@@ -67,6 +77,7 @@ namespace Platform
isTokenImmutable = false;
isTokenTransferable = false;
isMetadataEmbedded = false;
nftRegSign = NftRegSign::SignAny;
tokenProtocolId = cks;
tokenProtocolName = ercS21.kitts;
......@@ -75,6 +86,7 @@ namespace Platform
isTokenImmutable = false;
isTokenTransferable = false;
isMetadataEmbedded = false;
nftRegSign = NftRegSign::SignByCreator;
tokenProtocolId = crd;
tokenProtocolName = crown.id;
......@@ -83,6 +95,7 @@ namespace Platform
isTokenImmutable = false;
isTokenTransferable = false;
isMetadataEmbedded = false;
nftRegSign = NftRegSign::SelfSign;
tokenProtocolSymbol = doc;
tokenProtocolName = docproof;
......@@ -90,7 +103,8 @@ namespace Platform
tokenMetadataMimeType = text/plain;
isTokenImmutable = true;
isTokenTransferable = true;
isMetadataEmbedded = true; */
isMetadataEmbedded = true;
nftRegSign = NftRegSign::SignAny; */
}
#endif // CROWN_PLATFORM_NF_TOKEN_PROTOCOL_H
......@@ -28,10 +28,23 @@ namespace Platform
return *this;
}
NfTokenRegTxBuilder & SetTokenOwnerKey(const json_spirit::Value & tokenOwnerKeyOrAddress, CKey & ownerPrivKey)
NfTokenRegTxBuilder & SetTokenOwnerKey(const json_spirit::Value & tokenOwnerAddress, NftRegSign nftRegSign, CKey & ownerPrivKey)
{
ownerPrivKey = ParsePrivKeyOrAddress(tokenOwnerKeyOrAddress.get_str());
m_nfToken.tokenOwnerKeyId = ownerPrivKey.GetPubKey().GetID();
if (nftRegSign == NftRegSign::SelfSign)
{
ownerPrivKey = PullPrivKeyFromWallet(tokenOwnerAddress.get_str(), "nfTokenOwnerAddr");
m_nfToken.tokenOwnerKeyId = ownerPrivKey.GetPubKey().GetID();
}
else
{
m_nfToken.tokenOwnerKeyId = ParsePubKeyIDFromAddress(tokenOwnerAddress.get_str(), "nfTokenOwnerAddr");
}
return *this;
}
NfTokenRegTxBuilder & SetKeyToSign(const CKey & keyToSign)
{
m_keyToSign = keyToSign;
return *this;
}
......@@ -67,8 +80,7 @@ namespace Platform
private:
NfToken m_nfToken;
//CKey m_ownerKey;
//CPubKey m_ownerPubKey;
CKey m_keyToSign;
};
}
......
......@@ -30,7 +30,8 @@ namespace Platform
return m_nfToken;
}
bool Sign(CKey & privKey, CPubKey & pubKey);
// TODO: encapsulate signing
// bool Sign(CKey & privKey, CPubKey & pubKey);
std::string ToString() const;
void ToJson(json_spirit::Object & result) const;
......
......@@ -2,6 +2,7 @@
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <platform/nf-token/nf-token-protocol.h>
#include "primitives/transaction.h"
#include "platform/specialtx.h"
#include "platform/platform-utils.h"
......@@ -46,32 +47,57 @@ namespace Platform
"2. \"nfTokenId\" (string, required) The token id in hex.\n"
" The token id must be unique in the token type space.\n"
"3. \"nfTokenOwnerAddr\" (string, required) The token owner key, can be used in any operations with the token.\n"
" The private key belonging to this address may be or may be not known in your wallet.\n"
"3. \"nfTokenOwnerAddr\" (string, required) The token owner address, can be used in any operations with the token.\n"
" The private key belonging to this address should be or should be not known in your wallet. Depending on the NFT protocol nftRegSign field.\n"
" NftRegSign::SelfSign - the key should be known in your wallet.\n"
" NftRegSign::SignAny - an additional registrar private key should be provided.\n"
" NftRegSign::SignByCreator - NFT protocol key should be used to sing transactions.\n"
"4. \"nfTokenMetadataAdminAddr\" (string, optional, default = \"0\") The metadata token administration key, can be used to modify token metadata.\n"
"4. \"nfTokenMetadataAdminAddr\" (string, optional, default = \"0\") The metadata token administration address, can be used to modify token metadata.\n"
" The private key does not have to be known by your wallet. Can be set to 0.\n"
"5. \"nfTokenMetadata\" (string, optional) metadata describing the token.\n"
" It may contain text or binary in hex/base64 //TODO .\n";//TODO: add examples
" It may contain text or binary in hex/base64 //TODO .\n"
"6. \"nfTokenRegistrar\" (string, optional) Registrar address or private key.\n"
" It is used to sign the registration transaction if the specified protocol NFT can be signed by any key\n"
" The private key belonging to this address should be known in your wallet.\n"
"Examples: "
+ HelpExampleCli("nftoken", R"(register/issue doc a103d4bdfaa7d22591c4dacda81ba540e37f705bae41681c082b102e647aa8e8 CRWS78Yf5kbWAyfcES6RfiTVzP87csPNhZzc CRWS78YF1kbWAyfceS6RfiTVzP17csUnhUux "https://metadata-json.link")")
+ HelpExampleCli("nftoken", R"(register/issue doc 482115824e913a4a3c94ea06efdf25ff89c5b59b6f7e40d91b6ea76eebab5193 CRWS78Yf5kbWAyfcES6RfiTVzP87csPNhZzc CRWS78YF1kbWAyfceS6RfiTVzP17csUnhUux "https://metadata-json.link" CRWs89Yd1kbWAyfceS6RfiTVzP17csrntvxy)")
+ HelpExampleRpc("nftoken", R"(register/issue doc a103d4bdfaa7d22591c4dacda81ba540e37f705bae41681c082b102e647aa8e8 CRWS78Yf5kbWAyfcES6RfiTVzP87csPNhZzc CRWS78YF1kbWAyfceS6RfiTVzP17csUnhUux "https://metadata-json.link")");
throw std::runtime_error(helpMessage);
}
json_spirit::Value RegisterNfToken(const json_spirit::Array& params, bool fHelp)
{
if (fHelp || params.size() < 4 || params.size() > 6)
if (fHelp || params.size() < 4 || params.size() > 7)
RegisterNfTokenHelp();
CKey ownerPrivKey;
// TODO: get nftRegSign field from the protocol reg tx
NftRegSign nftRegSign;
CKey ownerKey;
NfTokenRegTxBuilder nfTokenRegTxBuilder;
nfTokenRegTxBuilder.SetTokenProtocol(params[1]).SetTokenId(params[2]).SetTokenOwnerKey(params[3], ownerPrivKey);
nfTokenRegTxBuilder.SetTokenProtocol(params[1]).SetTokenId(params[2]).SetTokenOwnerKey(params[3], nftRegSign, ownerKey);
if (params.size() > 4)
nfTokenRegTxBuilder.SetMetadataAdminKey(params[4]);
if (params.size() > 5)
nfTokenRegTxBuilder.SetMetadata(params[5]);
CKey keyToSign;
if (nftRegSign == NftRegSign::SelfSign && ownerKey.IsValid())
keyToSign = ownerKey;
else if (nftRegSign == NftRegSign::SignByCreator)
; //TODO: get NFT protocol key; privKeyToSign =
else if (nftRegSign == NftRegSign::SignAny && params.size() > 6)
keyToSign = ParsePrivKeyOrAddress(params[6].get_str(), "nfTokenRegistrar");
else
throw JSONRPCError(RPC_INVALID_PARAMETER, "Registrar address is missing or invalid");
nfTokenRegTxBuilder.SetKeyToSign(keyToSign);
NfTokenRegTx nfTokenRegTx = nfTokenRegTxBuilder.BuildTx();
CMutableTransaction tx;
......@@ -79,7 +105,7 @@ namespace Platform
tx.nType = TRANSACTION_NF_TOKEN_REGISTER;
FundSpecialTx(tx, nfTokenRegTx);
SignSpecialTxPayload(tx, nfTokenRegTx, ownerPrivKey);
SignSpecialTxPayload(tx, nfTokenRegTx, keyToSign);
SetTxPayload(tx, nfTokenRegTx);
std::string result = SignAndSendSpecialTx(tx);
......
......@@ -16,20 +16,13 @@ namespace Platform
{
// Allows to specify Crown address or priv key. In case of Crown address, the priv key is taken from the wallet
CKey ParsePrivKeyOrAddress(const std::string & strKeyOrAddress, bool allowAddresses /* = true */)
CKey ParsePrivKeyOrAddress(const std::string & strKeyOrAddress, const std::string & paramName, bool allowAddresses /* = true */)
{
CBitcoinAddress address;
if (allowAddresses && address.SetString(strKeyOrAddress) && address.IsValid())
if (allowAddresses)
{
#ifdef ENABLE_WALLET
CKeyID keyId;
CKey key;
if (!address.GetKeyID(keyId) || !pwalletMain->GetKey(keyId, key))
throw std::runtime_error(strprintf("non-wallet or invalid address %s", strKeyOrAddress));
return key;
#else//ENABLE_WALLET
throw std::runtime_error("addresses not supported in no-wallet builds");
#endif//ENABLE_WALLET
CKey key = PullPrivKeyFromWallet(strKeyOrAddress, paramName);
if (key.IsValid())
return key;
}
CBitcoinSecret secret;
......@@ -38,6 +31,22 @@ namespace Platform
return secret.GetKey();
}
CKey PullPrivKeyFromWallet(const std::string & strAddress, const std::string & paramName)
{
CBitcoinAddress address;
if (!address.SetString(strAddress) || !address.IsValid())
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("%s must be a valid P2PKH address, not %s", paramName, strAddress));
#ifdef ENABLE_WALLET
CKeyID keyId;
CKey key;
if (!address.GetKeyID(keyId) || !pwalletMain->GetKey(keyId, key))
throw std::runtime_error(strprintf("non-wallet or invalid address %s", strAddress));
return key;
#else//ENABLE_WALLET
throw std::runtime_error("addresses not supported in no-wallet builds");
#endif//ENABLE_WALLET
}
CKeyID ParsePubKeyIDFromAddress(const std::string & strAddress, const std::string & paramName)
{
......
......@@ -15,7 +15,8 @@ namespace Platform
std::string GetCommand(const json_spirit::Array & params, const std::string & errorMessage);
std::string SignAndSendSpecialTx(const CMutableTransaction & tx);
CKey ParsePrivKeyOrAddress(const std::string & strKeyOrAddress, bool allowAddresses = true);
CKey PullPrivKeyFromWallet(const std::string & strAddress, const std::string & paramName);
CKey ParsePrivKeyOrAddress(const std::string & strKeyOrAddress, const std::string & paramName, bool allowAddresses = true);
CKeyID ParsePubKeyIDFromAddress(const std::string & strAddress, const std::string & paramName);
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment