mirror of
https://github.com/ElementsProject/elements.git
synced 2026-08-19 13:27:35 +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" },
|
{ "combineblocksigs", 1, "signatures" },
|
||||||
{ "sendtomainchain", 1, "amount" },
|
{ "sendtomainchain", 1, "amount" },
|
||||||
{ "sendtomainchain", 2, "subtractfeefromamount" },
|
{ "sendtomainchain", 2, "subtractfeefromamount" },
|
||||||
|
{ "dumpissuanceblindingkey", 1, "vin" },
|
||||||
|
{ "importissuanceblindingkey", 1, "vin" },
|
||||||
{ "rawissueasset", 1, "issuances" },
|
{ "rawissueasset", 1, "issuances" },
|
||||||
{ "rawreissueasset", 1, "reissuances" },
|
{ "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", 1, "inputblinder" },
|
||||||
{ "rawblindrawtransaction", 2, "inputamount" },
|
{ "rawblindrawtransaction", 2, "inputamount" },
|
||||||
{ "rawblindrawtransaction", 3, "inputasset" },
|
{ "rawblindrawtransaction", 3, "inputasset" },
|
||||||
|
|
@ -177,6 +186,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
|
||||||
{ "blindrawtransaction", 1, "assetcommitments" },
|
{ "blindrawtransaction", 1, "assetcommitments" },
|
||||||
{ "blindrawtransaction", 2, "ignoreblindfail" },
|
{ "blindrawtransaction", 2, "ignoreblindfail" },
|
||||||
{ "blindrawtransaction", 3, "blind_issuances" },
|
{ "blindrawtransaction", 3, "blind_issuances" },
|
||||||
|
{ "destroyamount", 1, "amount" },
|
||||||
{ "sendmany", 7 , "output_assets" },
|
{ "sendmany", 7 , "output_assets" },
|
||||||
{ "sendmany", 8 , "ignoreblindfail" },
|
{ "sendmany", 8 , "ignoreblindfail" },
|
||||||
{ "sendtoaddress", 9 , "ignoreblindfail" },
|
{ "sendtoaddress", 9 , "ignoreblindfail" },
|
||||||
|
|
|
||||||
|
|
@ -1400,6 +1400,81 @@ UniValue importblindingkey(const JSONRPCRequest& request)
|
||||||
return NullUniValue;
|
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)
|
UniValue dumpblindingkey(const JSONRPCRequest& request)
|
||||||
{
|
{
|
||||||
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(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");
|
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;
|
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
|
// END ELEMENTS commands
|
||||||
//
|
//
|
||||||
|
|
@ -5655,7 +6035,9 @@ static UniValue unblindrawtransaction(const JSONRPCRequest& request)
|
||||||
UniValue abortrescan(const JSONRPCRequest& request); // in rpcdump.cpp
|
UniValue abortrescan(const JSONRPCRequest& request); // in rpcdump.cpp
|
||||||
UniValue dumpprivkey(const JSONRPCRequest& request); // in rpcdump.cpp
|
UniValue dumpprivkey(const JSONRPCRequest& request); // in rpcdump.cpp
|
||||||
UniValue importblindingkey(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 dumpblindingkey(const JSONRPCRequest& request); // in rpcdump.cpp
|
||||||
|
UniValue dumpissuanceblindingkey(const JSONRPCRequest& request); // in rpcdump.cpp
|
||||||
UniValue importprivkey(const JSONRPCRequest& request);
|
UniValue importprivkey(const JSONRPCRequest& request);
|
||||||
UniValue importaddress(const JSONRPCRequest& request);
|
UniValue importaddress(const JSONRPCRequest& request);
|
||||||
UniValue importpubkey(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", "initpegoutwallet", &initpegoutwallet, {"bitcoin_descriptor", "bip32_counter", "liquid_pak"} },
|
||||||
{ "wallet", "getwalletpakinfo", &getwalletpakinfo, {} },
|
{ "wallet", "getwalletpakinfo", &getwalletpakinfo, {} },
|
||||||
{ "wallet", "importblindingkey", &importblindingkey, {"address", "hexkey", "key_is_master"}},
|
{ "wallet", "importblindingkey", &importblindingkey, {"address", "hexkey", "key_is_master"}},
|
||||||
|
{ "wallet", "importissuanceblindingkey", &importissuanceblindingkey, {"txid", "vin", "blindingkey"}},
|
||||||
{ "wallet", "dumpblindingkey", &dumpblindingkey, {"address"}},
|
{ "wallet", "dumpblindingkey", &dumpblindingkey, {"address"}},
|
||||||
|
{ "wallet", "dumpissuanceblindingkey", &dumpissuanceblindingkey, {"txid", "vin"}},
|
||||||
{ "wallet", "signblock", &signblock, {"blockhex"}},
|
{ "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
|
// clang-format on
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue