From f37db5e608ceb52daa2ea9d6a1f25bed9931b6c8 Mon Sep 17 00:00:00 2001 From: Gregory Sanders Date: Fri, 24 Mar 2017 11:19:51 -0400 Subject: [PATCH] add issueasset rpc call --- src/rpc/client.cpp | 3 + src/wallet/rpcwallet.cpp | 144 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 147 insertions(+) diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index ac37ed6a59..943a168df1 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -117,6 +117,9 @@ static const CRPCConvertParam vRPCConvertParams[] = { "prioritisetransaction", 1, "priority_delta" }, { "prioritisetransaction", 2, "fee_delta" }, { "setban", 2, "bantime" }, + { "issueasset", 0, "assetamount" }, + { "issueasset", 1, "tokenamount" }, + { "issueasset", 2, "blind" }, { "setban", 3, "absolute" }, { "setnetworkactive", 0, "state" }, { "getmempoolancestors", 1, "verbose" }, diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 063ae332a5..fe9a6ad6f3 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -14,6 +14,7 @@ #include "global/common.h" #include "init.h" #include "validation.h" +#include "issuance.h" #include "net.h" #include "policy/policy.h" #include "policy/policy.h" @@ -446,6 +447,57 @@ static void SendMoney(const CTxDestination &address, CAmount nValue, CAsset asse SendMoney(GetScriptForDestination(address), nValue, asset, fSubtractFeeFromAmount, confidentiality_key, wtxNew); } +static void SendGenerationTransaction(const CScript& assetScriptPubKey, const CPubKey &assetKey, const CScript& tokenScriptPubKey, const CPubKey &tokenKey, CAmount nAmountAsset, CAmount nTokens, bool fBlindIssuances, uint256& entropy, CAsset& reissuanceAsset, CAsset& reissuanceToken, CWalletTx& wtxNew) +{ + + CAmount curBalance = pwalletMain->GetBalance()[reissuanceToken]; + + if (!reissuanceToken.IsNull() && curBalance <= 0) { + throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "No available reissuance tokens in wallet."); + } + + // Create and send the transaction + std::vector vpChangeKey; + std::vector vChangeKey; + // Need to be careful to not copy CReserveKeys or bad things happen + // We need 2 change outputs possibly for reissuance case: + // one for policyAsset, the other for reissuance token + vChangeKey.reserve(2); + vChangeKey.emplace_back(pwalletMain); + vpChangeKey.push_back(&vChangeKey[0]); + if (!reissuanceAsset.IsNull()) { + vChangeKey.emplace_back(pwalletMain); + vpChangeKey.push_back(&vChangeKey[1]); + } + + CAmount nFeeRequired; + std::string strError; + vector vecSend; + int nChangePosRet = -1; + // Signal outputs to skip "funding" with fixed asset numbers 1, 2, ... + // We don't know the asset during initial issuance until inputs are chosen + CRecipient recipient = {assetScriptPubKey, nAmountAsset, CAsset(uint256S("1")), assetKey, false}; + if (assetScriptPubKey.size() > 0) { + vecSend.push_back(recipient); + } + if (tokenScriptPubKey.size() > 0) { + recipient = {tokenScriptPubKey, nTokens, CAsset(uint256S("2")), tokenKey, false}; + // We need to select the issuance token(s) to spend + if (!reissuanceToken.IsNull()) { + recipient.asset = reissuanceToken; + recipient.nAmount = curBalance; // Or 1? + } + vecSend.push_back(recipient); + } + if (!pwalletMain->CreateTransaction(vecSend, wtxNew, vpChangeKey, nFeeRequired, nChangePosRet, strError, NULL, true, NULL, fBlindIssuances, &entropy, &reissuanceAsset, &reissuanceToken)) { + throw JSONRPCError(RPC_WALLET_ERROR, strError); + } + CValidationState state; + if (!pwalletMain->CommitTransaction(wtxNew, vpChangeKey, g_connman.get(), state)) + 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."); +} + + UniValue sendtoaddress(const JSONRPCRequest& request) { if (!EnsureWalletIsAvailable(request.fHelp)) @@ -3660,6 +3712,97 @@ UniValue claimpegin(const JSONRPCRequest& request) return finalTxn.GetHash().GetHex(); } +UniValue issueasset(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() < 2 || request.params.size() > 3) + throw 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\":\"\", (string) Transaction id for issuance.\n" + " \"entropy\":\"\" (string) Entropy of the asset type.\n" + " \"asset\":\"\", (string) Asset type for issuance if known.\n" + " \"token\":\"\", (string) Token type for issuance.\n" + "}\n" + "\nExamples:\n" + + HelpExampleCli("issueasset", "10 0") + + HelpExampleRpc("issueasset", "10, 0") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + CAmount nAmount = AmountFromValue(request.params[0]); + if (nAmount < 0) + throw JSONRPCError(RPC_TYPE_ERROR, "Asset creation amount must be non-negative"); + + CAmount nTokens = AmountFromValue(request.params[1]); + if (nTokens < 0) + throw JSONRPCError(RPC_TYPE_ERROR, "Reissuance token creation amount must be non-negative"); + + if (nAmount == 0 && nTokens == 0) { + throw JSONRPCError(RPC_TYPE_ERROR, "Issuance must have one non-zero component"); + } + + bool fBlindIssuances = request.params.size() < 3 || request.params[2].get_bool(); + + if (!pwalletMain->IsLocked()) + pwalletMain->TopUpKeyPool(); + + // Generate a new key that is added to wallet + CPubKey newKey; + CKeyID keyID; + CBitcoinAddress assetAddr; + CBitcoinAddress tokenAddr; + CPubKey assetKey; + CPubKey tokenKey; + + if (nAmount > 0) { + if (!pwalletMain->GetKeyFromPool(newKey)) + throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first"); + keyID = newKey.GetID(); + pwalletMain->SetAddressBook(keyID, "", "receive"); + assetAddr = CBitcoinAddress(keyID); + assetKey = pwalletMain->GetBlindingPubKey(GetScriptForDestination(assetAddr.Get())); + } + if (nTokens > 0) { + if (!pwalletMain->GetKeyFromPool(newKey)) + throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first"); + keyID = newKey.GetID(); + pwalletMain->SetAddressBook(keyID, "", "receive"); + tokenAddr = CBitcoinAddress(keyID); + tokenKey = pwalletMain->GetBlindingPubKey(GetScriptForDestination(CTxDestination(keyID))); + } + + CWalletTx wtx; + uint256 dummyentropy; + CAsset dummyasset; + SendGenerationTransaction(GetScriptForDestination(assetAddr.Get()), assetKey, GetScriptForDestination(tokenAddr.Get()), tokenKey, nAmount, nTokens, fBlindIssuances, dummyentropy, dummyasset, dummyasset, wtx); + + // Calculate asset type, assumes first vin is used for issuance + uint256 entropy; + CAsset asset; + CAsset token; + GenerateAssetEntropy(entropy, wtx.tx->vin[0].prevout, uint256()); + CalculateAsset(asset, entropy); + CalculateReissuanceToken(token, entropy, fBlindIssuances); + + UniValue ret(UniValue::VOBJ); + ret.push_back(Pair("txid", wtx.tx->GetHash().GetHex())); + ret.push_back(Pair("vin", "0")); + ret.push_back(Pair("entropy", entropy.GetHex())); + ret.push_back(Pair("asset", asset.GetHex())); + ret.push_back(Pair("token", token.GetHex())); + return ret; +} + UniValue dumpassetlabels(const JSONRPCRequest& request) { if (!EnsureWalletIsAvailable(request.fHelp)) @@ -3726,6 +3869,7 @@ static const CRPCCommand commands[] = { "wallet", "importprunedfunds", &importprunedfunds, true, {"rawtransaction","txoutproof"} }, { "wallet", "importpubkey", &importpubkey, true, {"pubkey","label","rescan"} }, { "wallet", "keypoolrefill", &keypoolrefill, true, {"newsize"} }, + { "wallet", "issueasset", &issueasset, true, {"assetamount", "tokenamount", "blind"} }, { "wallet", "listaccounts", &listaccounts, false, {"minconf","include_watchonly"} }, { "wallet", "listaddressgroupings", &listaddressgroupings, false, {} }, { "wallet", "listlockunspent", &listlockunspent, false, {} },