diff --git a/src/rpcclient.cpp b/src/rpcclient.cpp index 07adf65fc..73ecabe33 100644 --- a/src/rpcclient.cpp +++ b/src/rpcclient.cpp @@ -34,6 +34,7 @@ static const CRPCConvertParam vRPCConvertParams[] = { "getnetworkhashps", 1 }, { "sendtoaddress", 1 }, { "sendtoaddress", 4 }, + { "z_embedstring", 1 }, { "settxfee", 0 }, { "getreceivedbyaddress", 1 }, { "getreceivedbyaccount", 1 }, @@ -73,6 +74,7 @@ static const CRPCConvertParam vRPCConvertParams[] = { "getblockheader", 1 }, { "gettransaction", 1 }, { "getrawtransaction", 1 }, + { "z_readstring", 0 }, { "createrawtransaction", 0 }, { "createrawtransaction", 1 }, { "signrawtransaction", 1 }, diff --git a/src/rpcrawtransaction.cpp b/src/rpcrawtransaction.cpp index 9ad1737fa..383c71d62 100644 --- a/src/rpcrawtransaction.cpp +++ b/src/rpcrawtransaction.cpp @@ -282,6 +282,73 @@ UniValue getrawtransaction(const UniValue& params, bool fHelp) return result; } +UniValue z_readstring(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "z_readstring \"txid\"\n" + "\nNOTE: By default this function only works sometimes. This is when the tx is in the mempool\n" + "or there is an unspent output in the utxo for this transaction. To make it always work,\n" + "you need to maintain a transaction index, using the -txindex command line option.\n" + "\nReturn the string embedded into the transaction with z_embedstring (as a json object).\n" + + "\nArguments:\n" + "1. \"txid\" (string, required) The transaction id\n" + + + "\nResult:\n" + "\"embedded-string\" (string) The embedded string.\n" + + "\nExamples:\n" + + HelpExampleCli("z_readstring", "\"mytxid\"") + ); + + LOCK(cs_main); + + uint256 hash = ParseHashV(params[0], "parameter 1"); + + CTransaction tx; + uint256 hashBlock; + if (!GetTransaction(hash, tx, hashBlock, true)) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available about transaction"); + + UniValue bad_txn = JSONRPCError(RPC_INVALID_PARAMETER, "It doesn't look like this transaction was created with z_embedstring."); + + if (tx.vout.size() < 1 || tx.vjoinsplit.size() > 0) { + throw bad_txn; + } + + // Find the first script beginning with OP_RETURN. + for (size_t i = 0; i < tx.vout.size(); i++) { + CScript script = tx.vout[i].scriptPubKey; + opcodetype opcode; + std::vector vch; + CScript::const_iterator pc = script.begin(); + + if (!script.GetOp(pc, opcode, vch)) { + throw bad_txn; + } + if (opcode == OP_RETURN) { + // Next should come the embedded string. + if (!script.GetOp(pc, opcode, vch)) { + throw bad_txn; + } + if (opcode < 0 || opcode > OP_PUSHDATA4) { + throw bad_txn; + } + + // Make sure there there's nothing else after the embedded string. + if (pc < script.end()) { + throw bad_txn; + } + return std::string(vch.begin(), vch.end()); + } + } + + // Didn't find an OP_RETURN. + throw bad_txn; +} + UniValue gettxoutproof(const UniValue& params, bool fHelp) { if (fHelp || (params.size() != 1 && params.size() != 2)) diff --git a/src/rpcserver.cpp b/src/rpcserver.cpp index 194ffdb1e..820a403f2 100644 --- a/src/rpcserver.cpp +++ b/src/rpcserver.cpp @@ -316,6 +316,7 @@ static const CRPCCommand vRPCCommands[] = { "rawtransactions", "decoderawtransaction", &decoderawtransaction, true }, { "rawtransactions", "decodescript", &decodescript, true }, { "rawtransactions", "getrawtransaction", &getrawtransaction, true }, + { "rawtransactions", "z_readstring", &z_readstring, true }, { "rawtransactions", "sendrawtransaction", &sendrawtransaction, false }, { "rawtransactions", "signrawtransaction", &signrawtransaction, false }, /* uses wallet if enabled */ #ifdef ENABLE_WALLET @@ -373,6 +374,7 @@ static const CRPCCommand vRPCCommands[] = { "wallet", "sendfrom", &sendfrom, false }, { "wallet", "sendmany", &sendmany, false }, { "wallet", "sendtoaddress", &sendtoaddress, false }, + { "wallet", "z_embedstring", &z_embedstring, false }, { "wallet", "setaccount", &setaccount, true }, { "wallet", "settxfee", &settxfee, true }, { "wallet", "signmessage", &signmessage, true }, diff --git a/src/rpcserver.h b/src/rpcserver.h index 1124801c2..dbd68417b 100644 --- a/src/rpcserver.h +++ b/src/rpcserver.h @@ -209,6 +209,7 @@ extern UniValue setaccount(const UniValue& params, bool fHelp); extern UniValue getaccount(const UniValue& params, bool fHelp); extern UniValue getaddressesbyaccount(const UniValue& params, bool fHelp); extern UniValue sendtoaddress(const UniValue& params, bool fHelp); +extern UniValue z_embedstring(const UniValue& params, bool fHelp); extern UniValue signmessage(const UniValue& params, bool fHelp); extern UniValue verifymessage(const UniValue& params, bool fHelp); extern UniValue getreceivedbyaddress(const UniValue& params, bool fHelp); @@ -247,6 +248,7 @@ extern UniValue zc_raw_receive(const UniValue& params, bool fHelp); extern UniValue zc_sample_joinsplit(const UniValue& params, bool fHelp); extern UniValue getrawtransaction(const UniValue& params, bool fHelp); // in rcprawtransaction.cpp +extern UniValue z_readstring(const UniValue& params, bool fHelp); // in rcprawtransaction.cpp extern UniValue listunspent(const UniValue& params, bool fHelp); extern UniValue lockunspent(const UniValue& params, bool fHelp); extern UniValue listlockunspent(const UniValue& params, bool fHelp); diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index ffa89c33d..bfc899b98 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -366,7 +366,7 @@ UniValue getaddressesbyaccount(const UniValue& params, bool fHelp) return ret; } -static void SendMoney(const CTxDestination &address, CAmount nValue, bool fSubtractFeeFromAmount, CWalletTx& wtxNew) +static void SendMoney(const CScript &scriptPubKey, CAmount nValue, bool fSubtractFeeFromAmount, CWalletTx& wtxNew) { CAmount curBalance = pwalletMain->GetBalance(); @@ -377,8 +377,9 @@ static void SendMoney(const CTxDestination &address, CAmount nValue, bool fSubtr if (nValue > curBalance) throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds"); + // TODO: no longer needed? // Parse Hush address - CScript scriptPubKey = GetScriptForDestination(address); + //CScript scriptPubKey = GetScriptForDestination(address); // Create and send the transaction CReserveKey reservekey(pwalletMain); @@ -450,7 +451,48 @@ UniValue sendtoaddress(const UniValue& params, bool fHelp) EnsureWalletIsUnlocked(); - SendMoney(address.Get(), nAmount, fSubtractFeeFromAmount, wtx); + SendMoney(GetScriptForDestination(address.Get()), nAmount, fSubtractFeeFromAmount, wtx); + + return wtx.GetHash().GetHex(); +} + +UniValue z_embedstring(const UniValue& params, bool fHelp) +{ + if (!EnsureWalletIsAvailable(fHelp)) + return NullUniValue; + + if (fHelp || params.size() != 2) + throw runtime_error( + "z_embedstring \"string-to-embed\" amount\n" + "\nEmbed a string into the blockchain (requires paying a fee).\n" + + HelpRequiringPassphrase() + + "\nArguments:\n" + "1. \"string-to-embed\" (string, required) The string to embed into the blockchain.\n" + "2. \"amount\" (numeric, required) The amount in zcash to burn (not including fee). eg 0.0001\n" + "\nResult:\n" + "\"transactionid\" (string) The transaction id.\n" + "\nExamples:\n" + + HelpExampleCli("z_embedstring", "\"Hello, world!") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + // String to embed + std::string string_to_embed = params[0].get_str(); + std::vector bytes_to_embed(string_to_embed.begin(), string_to_embed.end()); + + // TODO: Are zero-valued TXOUTs disallowed by consensus, or can we safely + // remove the 'amount' from this RPC and always have zero-value TXOUT? + CAmount nAmount = AmountFromValue(params[1]); + //if (nAmount <= 0) + // throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send"); + // Daira says they are supported, -- Duke + + EnsureWalletIsUnlocked(); + + CWalletTx wtx; + CScript opreturn; + SendMoney(CScript() << OP_RETURN << bytes_to_embed, nAmount, false, wtx); return wtx.GetHash().GetHex(); } @@ -928,7 +970,7 @@ UniValue sendfrom(const UniValue& params, bool fHelp) if (nAmount > nBalance) throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds"); - SendMoney(address.Get(), nAmount, false, wtx); + SendMoney(GetScriptForDestination(address.Get()), nAmount, false, wtx); return wtx.GetHash().GetHex(); }