mirror of
https://github.com/ElementsProject/elements.git
synced 2026-08-15 12:51:00 +02:00
[BROKEN] Add issuance wallet RPCs
This commit is contained in:
parent
a361dda7a3
commit
f7920d77ec
3 changed files with 536 additions and 0 deletions
|
|
@ -167,8 +167,17 @@ static const CRPCConvertParam vRPCConvertParams[] =
|
|||
{ "combineblocksigs", 1, "signatures" },
|
||||
{ "sendtomainchain", 1, "amount" },
|
||||
{ "sendtomainchain", 2, "subtractfeefromamount" },
|
||||
{ "dumpissuanceblindingkey", 1, "vin" },
|
||||
{ "importissuanceblindingkey", 1, "vin" },
|
||||
{ "rawissueasset", 1, "issuances" },
|
||||
{ "rawreissueasset", 1, "reissuances" },
|
||||
{ "getnewblockhex", 0, "required_age" },
|
||||
{ "testproposedblock", 1, "acceptnonstd" },
|
||||
{ "issueasset", 0, "assetamount" },
|
||||
{ "issueasset", 1, "tokenamount" },
|
||||
{ "issueasset", 2, "blind" },
|
||||
{ "reissueasset", 1, "assetamount" },
|
||||
{ "initpegoutwallet", 1, "bip32_counter"},
|
||||
{ "rawblindrawtransaction", 1, "inputblinder" },
|
||||
{ "rawblindrawtransaction", 2, "inputamount" },
|
||||
{ "rawblindrawtransaction", 3, "inputasset" },
|
||||
|
|
@ -177,6 +186,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
|
|||
{ "blindrawtransaction", 1, "assetcommitments" },
|
||||
{ "blindrawtransaction", 2, "ignoreblindfail" },
|
||||
{ "blindrawtransaction", 3, "blind_issuances" },
|
||||
{ "destroyamount", 1, "amount" },
|
||||
{ "sendmany", 7 , "output_assets" },
|
||||
{ "sendmany", 8 , "ignoreblindfail" },
|
||||
{ "sendtoaddress", 9 , "ignoreblindfail" },
|
||||
|
|
|
|||
|
|
@ -1400,6 +1400,81 @@ UniValue importblindingkey(const JSONRPCRequest& request)
|
|||
return NullUniValue;
|
||||
}
|
||||
|
||||
UniValue importissuanceblindingkey(const JSONRPCRequest& request)
|
||||
{
|
||||
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
|
||||
CWallet* const pwallet = wallet.get();
|
||||
|
||||
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
|
||||
return NullUniValue;
|
||||
}
|
||||
|
||||
if (request.fHelp || request.params.size() != 3)
|
||||
throw std::runtime_error(
|
||||
"importissuanceblindingkey \"txid\" vin \"blindingkey\"\n"
|
||||
"\nImports a private blinding key in hex for an asset issuance."
|
||||
"\nArguments:\n"
|
||||
|
||||
"1. \"txid\" (string, required) The transaction id of the issuance\n"
|
||||
"2. \"vin\" (numeric, required) The input number of the issuance in the transaction.\n"
|
||||
"3. \"blindingkey\" (string, required) The blinding key in hex\n"
|
||||
"\nExample:\n"
|
||||
+ HelpExampleCli("importblindingkey", "\"my blinded CT address\" <blindinghex>")
|
||||
);
|
||||
|
||||
LOCK2(cs_main, pwallet->cs_wallet);
|
||||
|
||||
if (!request.params[0].isStr() || !IsHex(request.params[0].get_str()) || request.params[0].get_str().size() != 64) {
|
||||
throw JSONRPCError(RPC_TYPE_ERROR, "First argument must be a txid string");
|
||||
}
|
||||
std::string txidstr = request.params[0].get_str();
|
||||
uint256 txid;
|
||||
txid.SetHex(txidstr);
|
||||
|
||||
uint32_t vindex;
|
||||
if (!request.params[1].isNum()) {
|
||||
throw JSONRPCError(RPC_TYPE_ERROR, "vin must be an integer");
|
||||
}
|
||||
vindex = request.params[1].get_int();
|
||||
|
||||
if (!request.params[2].isStr() || !IsHex(request.params[2].get_str()) || request.params[2].get_str().size() != 64) {
|
||||
throw JSONRPCError(RPC_TYPE_ERROR, "blinding key must be a hex string of length 64");
|
||||
}
|
||||
|
||||
std::vector<unsigned char> keydata = ParseHex(request.params[2].get_str());
|
||||
if (keydata.size() != 32) {
|
||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid hexadecimal key length");
|
||||
}
|
||||
CKey key;
|
||||
key.Set(keydata.begin(), keydata.end(), true);
|
||||
|
||||
// Process as issuance key dump
|
||||
for (std::map<uint256, CWalletTx>::const_iterator it = pwallet->mapWallet.begin(); it != pwallet->mapWallet.end(); ++it) {
|
||||
const CWalletTx* pcoin = &(*it).second;
|
||||
if (pcoin->GetHash() != txid) {
|
||||
continue;
|
||||
}
|
||||
if (pcoin->tx->vin.size() <= vindex) {
|
||||
throw JSONRPCError(RPC_WALLET_ERROR, "Transaction is in wallet but vin does not exist");
|
||||
}
|
||||
if (pcoin->tx->vin[vindex].assetIssuance.IsNull()) {
|
||||
throw JSONRPCError(RPC_WALLET_ERROR, "Transaction input has no issuance");
|
||||
}
|
||||
|
||||
// Import the key in that slot
|
||||
uint256 keyval;
|
||||
memcpy(keyval.begin(), &keydata[0], 32);
|
||||
CScript blindingScript(CScript() << OP_RETURN << std::vector<unsigned char>(pcoin->tx->vin[vindex].prevout.hash.begin(), pcoin->tx->vin[vindex].prevout.hash.end()) << pcoin->tx->vin[vindex].prevout.n);
|
||||
if (!pwallet->AddSpecificBlindingKey(CScriptID(blindingScript), keyval)) {
|
||||
throw JSONRPCError(RPC_WALLET_ERROR, "Failed to import blinding key");
|
||||
}
|
||||
pwallet->MarkDirty();
|
||||
return NullUniValue;
|
||||
}
|
||||
|
||||
throw JSONRPCError(RPC_WALLET_ERROR, "Transaction is unknown to wallet.");
|
||||
}
|
||||
|
||||
UniValue dumpblindingkey(const JSONRPCRequest& request)
|
||||
{
|
||||
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
|
||||
|
|
@ -1443,3 +1518,66 @@ UniValue dumpblindingkey(const JSONRPCRequest& request)
|
|||
|
||||
throw JSONRPCError(RPC_WALLET_ERROR, "Blinding key for address is unknown");
|
||||
}
|
||||
|
||||
UniValue dumpissuanceblindingkey(const JSONRPCRequest& request)
|
||||
{
|
||||
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
|
||||
CWallet* const pwallet = wallet.get();
|
||||
|
||||
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
|
||||
return NullUniValue;
|
||||
}
|
||||
|
||||
if (request.fHelp || request.params.size() != 2)
|
||||
throw std::runtime_error(
|
||||
"dumpissuanceblindingkey \"txid\" vin\n"
|
||||
"\nDumps the private blinding key for an asset issuance in wallet."
|
||||
"\nArguments:\n"
|
||||
"1. \"txid\" (string, required) The transaction id of the issuance\n"
|
||||
"2. \"vin\" (numeric, required) The input number of the issuance in the transaction.\n"
|
||||
"\nResult:\n"
|
||||
"\"blindingkey\" (string) The blinding key\n"
|
||||
"\nExample:\n"
|
||||
+ HelpExampleCli("dumpissuanceblindingkey", "\"<txid>\", 0")
|
||||
);
|
||||
|
||||
LOCK2(cs_main, pwallet->cs_wallet);
|
||||
|
||||
if (!request.params[0].isStr() || !IsHex(request.params[0].get_str()) || request.params[0].get_str().size() != 64) {
|
||||
throw JSONRPCError(RPC_TYPE_ERROR, "First argument must be a txid string");
|
||||
}
|
||||
std::string txidstr = request.params[0].get_str();
|
||||
uint256 txid;
|
||||
txid.SetHex(txidstr);
|
||||
|
||||
uint32_t vindex;
|
||||
if (!request.params[1].isNum()) {
|
||||
throw JSONRPCError(RPC_TYPE_ERROR, "vin must be an integer");
|
||||
}
|
||||
vindex = request.params[1].get_int();
|
||||
|
||||
// Process as issuance key dump
|
||||
for (std::map<uint256, CWalletTx>::const_iterator it = pwallet->mapWallet.begin(); it != pwallet->mapWallet.end(); ++it) {
|
||||
const CWalletTx* pcoin = &(*it).second;
|
||||
if (pcoin->tx->GetHash() != txid) {
|
||||
continue;
|
||||
}
|
||||
if (pcoin->tx->vin.size() <= vindex) {
|
||||
throw JSONRPCError(RPC_WALLET_ERROR, "Transaction is in wallet but vin does not exist");
|
||||
}
|
||||
if (pcoin->tx->vin[vindex].assetIssuance.IsNull()) {
|
||||
throw JSONRPCError(RPC_WALLET_ERROR, "Transaction input has no issuance");
|
||||
}
|
||||
// We can actually deblind the input
|
||||
if (pcoin->GetIssuanceAmount(vindex, false) != -1 ) {
|
||||
CScript blindingScript(CScript() << OP_RETURN << std::vector<unsigned char>(pcoin->tx->vin[vindex].prevout.hash.begin(), pcoin->tx->vin[vindex].prevout.hash.end()) << pcoin->tx->vin[vindex].prevout.n);
|
||||
CKey key;
|
||||
key = pwallet->GetBlindingKey(&blindingScript);
|
||||
return HexStr(key.begin(), key.end());
|
||||
} else {
|
||||
// We don't know how to deblind this using our wallet
|
||||
throw JSONRPCError(RPC_WALLET_ERROR, "Unable to unblind issuance with wallet blinding key.");
|
||||
}
|
||||
}
|
||||
throw JSONRPCError(RPC_WALLET_ERROR, "Transaction is unknown to wallet.");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5648,6 +5648,386 @@ static UniValue unblindrawtransaction(const JSONRPCRequest& request)
|
|||
return result;
|
||||
}
|
||||
|
||||
static CTransactionRef SendGenerationTransaction(const CScript& asset_script, const CPubKey &asset_pubkey, const CScript& token_script, const CPubKey &token_pubkey, CAmount asset_amount, CAmount token_amount, IssuanceDetails* issuance_details, CWallet* pwallet)
|
||||
{
|
||||
CAsset reissue_token = issuance_details->reissuance_token;
|
||||
CAmount curBalance = pwallet->GetBalance()[reissue_token];
|
||||
|
||||
if (!reissue_token.IsNull() && curBalance <= 0) {
|
||||
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "No available reissuance tokens in wallet.");
|
||||
}
|
||||
|
||||
// Might need up to 3 change keys: policyAsset, asset, and reissuance token
|
||||
std::vector<std::unique_ptr<CReserveKey>> change_keys;
|
||||
for (unsigned int i = 0; i < 3; i++) {
|
||||
change_keys.push_back(std::unique_ptr<CReserveKey>(new CReserveKey(pwallet)));
|
||||
}
|
||||
|
||||
std::vector<CRecipient> vecSend;
|
||||
// Signal outputs to skip "funding" with fixed asset numbers 1, 2, ...
|
||||
// We don't know the asset during initial issuance until inputs are chosen
|
||||
if (asset_script.size() > 0) {
|
||||
vecSend.push_back({asset_script, asset_amount, CAsset(uint256S("1")), asset_pubkey, false});
|
||||
}
|
||||
if (token_script.size() > 0) {
|
||||
CRecipient recipient = {token_script, token_amount, CAsset(uint256S("2")), token_pubkey, false};
|
||||
// We need to select the issuance token(s) to spend
|
||||
if (!reissue_token.IsNull()) {
|
||||
recipient.asset = reissue_token;
|
||||
recipient.nAmount = curBalance; // Or 1?
|
||||
// If the issuance token *is* the fee asset, subtract fee from this output
|
||||
if (reissue_token == ::policyAsset) {
|
||||
recipient.fSubtractFeeFromAmount = true;
|
||||
}
|
||||
}
|
||||
vecSend.push_back(recipient);
|
||||
}
|
||||
|
||||
CAmount nFeeRequired;
|
||||
int nChangePosRet = -1;
|
||||
std::string strError;
|
||||
CCoinControl dummy_control;
|
||||
BlindDetails blind_details;
|
||||
CTransactionRef tx_ref(MakeTransactionRef());
|
||||
if (!pwallet->CreateTransaction(vecSend, tx_ref, change_keys, nFeeRequired, nChangePosRet, strError, dummy_control, true, &blind_details, issuance_details)) {
|
||||
throw JSONRPCError(RPC_WALLET_ERROR, strError);
|
||||
}
|
||||
|
||||
CValidationState state;
|
||||
mapValue_t map_value;
|
||||
if (!pwallet->CommitTransaction(tx_ref, std::move(map_value), {} /* orderForm */, change_keys, g_connman.get(), state, &blind_details)) {
|
||||
throw JSONRPCError(RPC_WALLET_ERROR, "Error: The transaction was rejected! This might happen if some of the coins in your wallet were already spent, such as if you used a copy of the wallet and coins were spent in the copy but not marked as spent here.");
|
||||
}
|
||||
|
||||
return tx_ref;
|
||||
}
|
||||
|
||||
UniValue issueasset(const JSONRPCRequest& request)
|
||||
{
|
||||
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
|
||||
CWallet* const pwallet = wallet.get();
|
||||
|
||||
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
|
||||
return NullUniValue;
|
||||
}
|
||||
|
||||
if (request.fHelp || request.params.size() < 2 || request.params.size() > 3)
|
||||
throw std::runtime_error(
|
||||
"issueasset assetamount tokenamount ( blind )\n"
|
||||
"\nCreate an asset. Must have funds in wallet to do so. Returns asset hex id.\n"
|
||||
"\nArguments:\n"
|
||||
"1. \"assetamount\" (numeric or string, required) Amount of asset to generate.\n"
|
||||
"2. \"tokenamount\" (numeric or string, required) Amount of reissuance tokens to generate. These will allow you to reissue the asset if in wallet using `reissueasset`. These tokens are not consumed during reissuance.\n"
|
||||
"3. \"blind\" (bool, optional, default=true) Whether to blind the issuances.\n"
|
||||
"\nResult:\n"
|
||||
"{ (json object)\n"
|
||||
" \"txid\":\"<txid>\", (string) Transaction id for issuance.\n"
|
||||
" \"vin\":\"n\", (numeric) The input position of the issuance in the transaction.\n"
|
||||
" \"entropy\":\"<entropy>\", (string) Entropy of the asset type.\n"
|
||||
" \"asset\":\"<asset>\", (string) Asset type for issuance.\n"
|
||||
" \"token\":\"<token>\", (string) Token type for issuance.\n"
|
||||
"}\n"
|
||||
"\nExamples:\n"
|
||||
+ HelpExampleCli("issueasset", "10 0")
|
||||
+ HelpExampleRpc("issueasset", "10, 0")
|
||||
);
|
||||
|
||||
LOCK2(cs_main, pwallet->cs_wallet);
|
||||
|
||||
CAmount nAmount = AmountFromValue(request.params[0]);
|
||||
CAmount nTokens = AmountFromValue(request.params[1]);
|
||||
if (nAmount == 0 && nTokens == 0) {
|
||||
throw JSONRPCError(RPC_TYPE_ERROR, "Issuance must have one non-zero component");
|
||||
}
|
||||
|
||||
bool blind_issuances = request.params.size() < 3 || request.params[2].get_bool();
|
||||
|
||||
if (!pwallet->IsLocked())
|
||||
pwallet->TopUpKeyPool();
|
||||
|
||||
// Generate a new key that is added to wallet
|
||||
CPubKey newKey;
|
||||
CTxDestination asset_dest;
|
||||
CTxDestination token_dest;
|
||||
CPubKey asset_dest_blindpub;
|
||||
CPubKey token_dest_blindpub;
|
||||
|
||||
if (nAmount > 0) {
|
||||
if (!pwallet->GetKeyFromPool(newKey)) {
|
||||
throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first");
|
||||
}
|
||||
asset_dest = PKHash(newKey.GetID());
|
||||
pwallet->SetAddressBook(asset_dest, "", "receive");
|
||||
asset_dest_blindpub = pwallet->GetBlindingPubKey(GetScriptForDestination(asset_dest));
|
||||
}
|
||||
if (nTokens > 0) {
|
||||
if (!pwallet->GetKeyFromPool(newKey)) {
|
||||
throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first");
|
||||
}
|
||||
token_dest = PKHash(newKey.GetID());
|
||||
pwallet->SetAddressBook(token_dest, "", "receive");
|
||||
token_dest_blindpub = pwallet->GetBlindingPubKey(GetScriptForDestination(token_dest));
|
||||
}
|
||||
|
||||
uint256 dummyentropy;
|
||||
CAsset dummyasset;
|
||||
IssuanceDetails issuance_details;
|
||||
issuance_details.blind_issuance = blind_issuances;
|
||||
CTransactionRef tx_ref = SendGenerationTransaction(GetScriptForDestination(asset_dest), asset_dest_blindpub, GetScriptForDestination(token_dest), token_dest_blindpub, nAmount, nTokens, &issuance_details, pwallet);
|
||||
|
||||
// Calculate asset type, assumes first vin is used for issuance
|
||||
CAsset asset;
|
||||
CAsset token;
|
||||
assert(!tx_ref->vin.empty());
|
||||
GenerateAssetEntropy(issuance_details.entropy, tx_ref->vin[0].prevout, uint256());
|
||||
CalculateAsset(asset, issuance_details.entropy);
|
||||
CalculateReissuanceToken(token, issuance_details.entropy, blind_issuances);
|
||||
|
||||
UniValue ret(UniValue::VOBJ);
|
||||
ret.pushKV("txid", tx_ref->GetHash().GetHex());
|
||||
ret.pushKV("vin", 0);
|
||||
ret.pushKV("entropy", issuance_details.entropy.GetHex());
|
||||
ret.pushKV("asset", asset.GetHex());
|
||||
ret.pushKV("token", token.GetHex());
|
||||
return ret;
|
||||
}
|
||||
|
||||
UniValue reissueasset(const JSONRPCRequest& request)
|
||||
{
|
||||
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
|
||||
CWallet* const pwallet = wallet.get();
|
||||
|
||||
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
|
||||
return NullUniValue;
|
||||
}
|
||||
|
||||
if (request.fHelp || request.params.size() != 2)
|
||||
throw std::runtime_error(
|
||||
"reissueasset asset assetamount\n"
|
||||
"\nCreate more of an already issued asset. Must have reissuance token in wallet to do so. Reissuing does not affect your reissuance token balance, only asset.\n"
|
||||
"\nArguments:\n"
|
||||
"1. \"asset\" (string, required) The asset you want to re-issue. The corresponding token must be in your wallet.\n"
|
||||
"2. \"assetamount\" (numeric or string, required) Amount of additional asset to generate.\n"
|
||||
"\nResult:\n"
|
||||
"{ (json object)\n"
|
||||
" \"txid\":\"<txid>\", (string) Transaction id for issuance.\n"
|
||||
" \"vin\":\"n\", (numeric) The input position of the issuance in the transaction.\n"
|
||||
"}\n"
|
||||
"\nExamples:\n"
|
||||
+ HelpExampleCli("reissueasset", "<asset> 0")
|
||||
+ HelpExampleRpc("reissueasset", "<asset>, 0")
|
||||
);
|
||||
|
||||
LOCK2(cs_main, pwallet->cs_wallet);
|
||||
|
||||
std::string assetstr = request.params[0].get_str();
|
||||
CAsset asset = GetAssetFromString(assetstr);
|
||||
|
||||
CAmount nAmount = AmountFromValue(request.params[1]);
|
||||
if (nAmount <= 0) {
|
||||
throw JSONRPCError(RPC_TYPE_ERROR, "Reissuance must create a non-zero amount.");
|
||||
}
|
||||
|
||||
if (!pwallet->IsLocked()) {
|
||||
pwallet->TopUpKeyPool();
|
||||
}
|
||||
|
||||
// Find the entropy and reissuance token in wallet
|
||||
IssuanceDetails issuance_details;
|
||||
issuance_details.reissuance_asset = asset;
|
||||
std::map<uint256, std::pair<CAsset, CAsset> > tokenMap = pwallet->GetReissuanceTokenTypes();
|
||||
for (const auto& it : tokenMap) {
|
||||
if (it.second.second == asset) {
|
||||
issuance_details.entropy = it.first;
|
||||
issuance_details.reissuance_token = it.second.first;
|
||||
}
|
||||
if (it.second.first == asset) {
|
||||
throw JSONRPCError(RPC_WALLET_ERROR, "Asset given is a reissuance token type and can not be reissued.");
|
||||
}
|
||||
}
|
||||
if (issuance_details.reissuance_token.IsNull()) {
|
||||
throw JSONRPCError(RPC_WALLET_ERROR, "Asset reissuance token definition could not be found in wallet.");
|
||||
}
|
||||
|
||||
// Add destination for the to-be-created asset
|
||||
CPubKey newAssetKey;
|
||||
if (!pwallet->GetKeyFromPool(newAssetKey)) {
|
||||
throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first");
|
||||
}
|
||||
CTxDestination asset_dest = PKHash(newAssetKey.GetID());
|
||||
pwallet->SetAddressBook(asset_dest, "", "receive");
|
||||
CPubKey asset_dest_blindpub = pwallet->GetBlindingPubKey(GetScriptForDestination(asset_dest));
|
||||
|
||||
// Add destination for tokens we are moving
|
||||
CPubKey newTokenKey;
|
||||
if (!pwallet->GetKeyFromPool(newTokenKey)) {
|
||||
throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first");
|
||||
}
|
||||
CTxDestination token_dest = PKHash(newTokenKey.GetID());
|
||||
pwallet->SetAddressBook(token_dest, "", "receive");
|
||||
CPubKey token_dest_blindpub = pwallet->GetBlindingPubKey(GetScriptForDestination(token_dest));
|
||||
|
||||
// Attempt a send.
|
||||
CTransactionRef tx_ref = SendGenerationTransaction(GetScriptForDestination(asset_dest), asset_dest_blindpub, GetScriptForDestination(token_dest), token_dest_blindpub, nAmount, -1, &issuance_details, pwallet);
|
||||
assert(!tx_ref->vin.empty());
|
||||
|
||||
UniValue obj(UniValue::VOBJ);
|
||||
obj.pushKV("txid", tx_ref->GetHash().GetHex());
|
||||
for (uint64_t i = 0; i < tx_ref->vin.size(); i++) {
|
||||
if (!tx_ref->vin[i].assetIssuance.IsNull()) {
|
||||
obj.pushKV("vin", i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
UniValue listissuances(const JSONRPCRequest& request)
|
||||
{
|
||||
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
|
||||
CWallet* const pwallet = wallet.get();
|
||||
|
||||
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
|
||||
return NullUniValue;
|
||||
}
|
||||
|
||||
if (request.fHelp || request.params.size() > 1)
|
||||
throw std::runtime_error(
|
||||
"listissuances ( asset ) \n"
|
||||
"\nList all issuances known to the wallet for the given asset, or for all issued assets if none provided.\n"
|
||||
"\nArguments:\n"
|
||||
"1. \"asset\" (string, optional) The asset whose issaunces you wish to list. Accepts either the asset hex or the locally assigned asset label.\n"
|
||||
"\nResult:\n"
|
||||
"[ (json array of objects)\n"
|
||||
" {\n"
|
||||
" \"txid\":\"<txid>\", (string) Transaction id for issuance.\n"
|
||||
" \"entropy\":\"<entropy>\" (string) Entropy of the asset type.\n"
|
||||
" \"asset\":\"<asset>\", (string) Asset type for issuance if known.\n"
|
||||
" \"assetlabel\":\"<assetlabel>\", (string) Asset label for issuance if set.\n"
|
||||
" \"token\":\"<token>\", (string) Token type for issuance.\n"
|
||||
" \"vin\":\"n\", (numeric) The input position of the issuance in the transaction.\n"
|
||||
" \"assetamount\":\"X.XX\", (numeric) The amount of asset issued. Is -1 if blinded and unknown to wallet.\n"
|
||||
" \"tokenamount\":\"X.XX\", (numeric) The reissuance token amount issued. Is -1 if blinded and unknown to wallet.\n"
|
||||
" \"isreissuance\":\"<bool>\", (bool) True if this is a reissuance.\n"
|
||||
" \"assetblinds\":\"<blinder>\" (string) Hex blinding factor for asset amounts.\n"
|
||||
" \"tokenblinds\":\"<blinder>\" (string) Hex blinding factor for token amounts.\n"
|
||||
" }\n"
|
||||
" ,...\n"
|
||||
"]\n"
|
||||
"\"\" (array) List of transaction issuances and information in wallet\n"
|
||||
+ HelpExampleCli("listissuances", "<asset>")
|
||||
+ HelpExampleRpc("listissuances", "<asset>")
|
||||
);
|
||||
|
||||
LOCK2(cs_main, pwallet->cs_wallet);
|
||||
|
||||
std::string assetstr;
|
||||
CAsset asset_filter;
|
||||
if (request.params.size() > 0) {
|
||||
assetstr = request.params[0].get_str();
|
||||
asset_filter = GetAssetFromString(assetstr);
|
||||
}
|
||||
|
||||
UniValue issuancelist(UniValue::VARR);
|
||||
for (const auto& it : pwallet->mapWallet) {
|
||||
const CWalletTx* pcoin = &it.second;
|
||||
CAsset asset;
|
||||
CAsset token;
|
||||
uint256 entropy;
|
||||
for (uint64_t vinIndex = 0; vinIndex < pcoin->tx->vin.size(); vinIndex++) {
|
||||
UniValue item(UniValue::VOBJ);
|
||||
const CAssetIssuance& issuance = pcoin->tx->vin[vinIndex].assetIssuance;
|
||||
if (issuance.IsNull()) {
|
||||
continue;
|
||||
}
|
||||
if (issuance.assetBlindingNonce.IsNull()) {
|
||||
GenerateAssetEntropy(entropy, pcoin->tx->vin[vinIndex].prevout, issuance.assetEntropy);
|
||||
CalculateAsset(asset, entropy);
|
||||
// Null is considered explicit
|
||||
CalculateReissuanceToken(token, entropy, issuance.nAmount.IsCommitment());
|
||||
item.pushKV("isreissuance", false);
|
||||
item.pushKV("token", token.GetHex());
|
||||
CAmount itamount = pcoin->GetIssuanceAmount(vinIndex, true);
|
||||
item.pushKV("tokenamount", (itamount == -1 ) ? -1 : ValueFromAmount(itamount));
|
||||
item.pushKV("tokenblinds", pcoin->GetIssuanceBlindingFactor(vinIndex, true).GetHex());
|
||||
item.pushKV("entropy", entropy.GetHex());
|
||||
} else {
|
||||
CalculateAsset(asset, issuance.assetEntropy);
|
||||
item.pushKV("isreissuance", true);
|
||||
item.pushKV("entropy", issuance.assetEntropy.GetHex());
|
||||
}
|
||||
item.pushKV("txid", pcoin->tx->GetHash().GetHex());
|
||||
item.pushKV("vin", vinIndex);
|
||||
item.pushKV("asset", asset.GetHex());
|
||||
const std::string label = gAssetsDir.GetLabel(asset);
|
||||
if (label != "") {
|
||||
item.pushKV("assetlabel", label);
|
||||
}
|
||||
CAmount iaamount = pcoin->GetIssuanceAmount(vinIndex, false);
|
||||
item.pushKV("assetamount", (iaamount == -1 ) ? -1 : ValueFromAmount(iaamount));
|
||||
item.pushKV("assetblinds", pcoin->GetIssuanceBlindingFactor(vinIndex, false).GetHex());
|
||||
if (!asset_filter.IsNull() && asset_filter != asset) {
|
||||
continue;
|
||||
}
|
||||
issuancelist.push_back(item);
|
||||
}
|
||||
}
|
||||
return issuancelist;
|
||||
|
||||
}
|
||||
|
||||
UniValue destroyamount(const JSONRPCRequest& request)
|
||||
{
|
||||
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
|
||||
CWallet* const pwallet = wallet.get();
|
||||
|
||||
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
|
||||
return NullUniValue;
|
||||
}
|
||||
|
||||
if (request.fHelp || request.params.size() < 1 || request.params.size() > 3)
|
||||
throw std::runtime_error(
|
||||
"destroyamount asset amount ( \"comment\" )\n"
|
||||
"\nDestroy an amount of a given asset.\n\n"
|
||||
"\nArguments:\n"
|
||||
"1. \"asset\" (string, required) Hex asset id or asset label to destroy.\n"
|
||||
"2. \"amount\" (numeric or string, required) The amount to destroy (8 decimals above the minimal unit).\n"
|
||||
"3. \"comment\" (string, optional) A comment used to store what the transaction is for. \n"
|
||||
" This is not part of the transaction, just kept in your wallet.\n"
|
||||
"\nResult:\n"
|
||||
"\"transactionid\" (string) The transaction id.\n"
|
||||
"\nExamples:\n"
|
||||
+ HelpExampleCli("destroyamount", "\"bitcoin\" 100")
|
||||
+ HelpExampleCli("destroyamount", "\"bitcoin\" 100 \"destroy assets\"")
|
||||
+ HelpExampleRpc("destroyamount", "\"bitcoin\" 100 \"destroy assets\"")
|
||||
);
|
||||
|
||||
LOCK2(cs_main, pwallet->cs_wallet);
|
||||
|
||||
std::string strasset = request.params[0].get_str();
|
||||
CAsset asset = GetAssetFromString(strasset);
|
||||
|
||||
CAmount nAmount = AmountFromValue(request.params[1]);
|
||||
if (nAmount <= 0) {
|
||||
throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount to destroy");
|
||||
}
|
||||
|
||||
mapValue_t mapValue;
|
||||
if (request.params.size() > 2 && !request.params[2].isNull() && !request.params[2].get_str().empty()) {
|
||||
mapValue["comment"] = request.params[2].get_str();
|
||||
}
|
||||
|
||||
EnsureWalletIsUnlocked(pwallet);
|
||||
|
||||
NullData nulldata;
|
||||
CTxDestination address(nulldata);
|
||||
CCoinControl no_coin_control; // This is a deprecated API
|
||||
CTransactionRef tx = SendMoney(pwallet, address, nAmount, asset, false, no_coin_control, std::move(mapValue), true);
|
||||
|
||||
return tx->GetHash().GetHex();
|
||||
}
|
||||
|
||||
|
||||
// END ELEMENTS commands
|
||||
//
|
||||
|
|
@ -5655,7 +6035,9 @@ static UniValue unblindrawtransaction(const JSONRPCRequest& request)
|
|||
UniValue abortrescan(const JSONRPCRequest& request); // in rpcdump.cpp
|
||||
UniValue dumpprivkey(const JSONRPCRequest& request); // in rpcdump.cpp
|
||||
UniValue importblindingkey(const JSONRPCRequest& request); // in rpcdump.cpp
|
||||
UniValue importissuanceblindingkey(const JSONRPCRequest& request); // in rpcdump.cpp
|
||||
UniValue dumpblindingkey(const JSONRPCRequest& request); // in rpcdump.cpp
|
||||
UniValue dumpissuanceblindingkey(const JSONRPCRequest& request); // in rpcdump.cpp
|
||||
UniValue importprivkey(const JSONRPCRequest& request);
|
||||
UniValue importaddress(const JSONRPCRequest& request);
|
||||
UniValue importpubkey(const JSONRPCRequest& request);
|
||||
|
|
@ -5736,8 +6118,14 @@ static const CRPCCommand commands[] =
|
|||
{ "wallet", "initpegoutwallet", &initpegoutwallet, {"bitcoin_descriptor", "bip32_counter", "liquid_pak"} },
|
||||
{ "wallet", "getwalletpakinfo", &getwalletpakinfo, {} },
|
||||
{ "wallet", "importblindingkey", &importblindingkey, {"address", "hexkey", "key_is_master"}},
|
||||
{ "wallet", "importissuanceblindingkey", &importissuanceblindingkey, {"txid", "vin", "blindingkey"}},
|
||||
{ "wallet", "dumpblindingkey", &dumpblindingkey, {"address"}},
|
||||
{ "wallet", "dumpissuanceblindingkey", &dumpissuanceblindingkey, {"txid", "vin"}},
|
||||
{ "wallet", "signblock", &signblock, {"blockhex"}},
|
||||
{ "wallet", "listissuances", &listissuances, {"asset"}},
|
||||
{ "wallet", "issueasset", &issueasset, {"assetamount", "tokenamount", "blind"}},
|
||||
{ "wallet", "reissueasset", &reissueasset, {"asset, assetamount"}},
|
||||
{ "wallet", "destroyamount", &destroyamount, {"asset", "amount", "comment"} },
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue