|
|
@ -35,6 +35,7 @@ |
|
|
|
#include "primitives/transaction.h" |
|
|
|
#include "script/interpreter.h" |
|
|
|
#include "zcash/zip32.h" |
|
|
|
#include "librustzcash.h" |
|
|
|
#include "zcash/Note.hpp" |
|
|
|
#include "utiltime.h" |
|
|
|
#include "asyncrpcoperation.h" |
|
|
@ -52,6 +53,7 @@ |
|
|
|
#include "hush_defs.h" |
|
|
|
#include <string.h> |
|
|
|
#include "rpchushwallet.h" |
|
|
|
#define SATOSHIDEN ((uint64_t)100000000L) |
|
|
|
|
|
|
|
using namespace std; |
|
|
|
using namespace libzcash; |
|
|
@ -813,6 +815,154 @@ UniValue listaddressgroupings(const UniValue& params, bool fHelp, const CPubKey& |
|
|
|
return jsonGroupings; |
|
|
|
} |
|
|
|
|
|
|
|
UniValue z_signmessage(const UniValue& params, bool fHelp, const CPubKey& mypk) |
|
|
|
{ |
|
|
|
if (!EnsureWalletIsAvailable(fHelp)) |
|
|
|
return NullUniValue; |
|
|
|
|
|
|
|
if (fHelp || params.size() != 2) |
|
|
|
throw runtime_error( |
|
|
|
"z_signmessage \"zaddr\" \"message\"\n" |
|
|
|
"\nSign a message with the private key of a zaddr" |
|
|
|
+ HelpRequiringPassphrase() + "\n" |
|
|
|
"\nArguments:\n" |
|
|
|
"1. \"zaddr\" (string, required) The Sapling shielded address to use for the private key.\n" |
|
|
|
"2. \"message\" (string, required) The message to create a signature of.\n" |
|
|
|
"\nResult:\n" |
|
|
|
"\"signature\" (string) The signature of the message encoded in base 64\n" |
|
|
|
"\nExamples:\n" |
|
|
|
"\nCreate the signature\n" |
|
|
|
+ HelpExampleCli("z_signmessage", "\"zs1...\" \"my message\"") + |
|
|
|
"\nVerify the signature\n" |
|
|
|
+ HelpExampleCli("z_verifymessage", "\"zs1...\" \"signature\" \"my message\"") + |
|
|
|
"\nAs json rpc\n" |
|
|
|
+ HelpExampleRpc("z_signmessage", "\"zs1...\", \"my message\"") |
|
|
|
); |
|
|
|
|
|
|
|
LOCK2(cs_main, pwalletMain->cs_wallet); |
|
|
|
|
|
|
|
EnsureWalletIsUnlocked(); |
|
|
|
|
|
|
|
string strAddress = params[0].get_str(); |
|
|
|
string strMessage = params[1].get_str(); |
|
|
|
uint32_t branchId = CurrentEpochBranchId(chainActive.Height(), Params().GetConsensus()); |
|
|
|
|
|
|
|
// Is it a valid zaddr in this set of consensus rules?
|
|
|
|
auto res = DecodePaymentAddress(strAddress); |
|
|
|
if (!IsValidPaymentAddress(res, branchId)) { |
|
|
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid zaddr!"); |
|
|
|
} |
|
|
|
|
|
|
|
// Check that we have the spending key
|
|
|
|
if (!boost::apply_visitor(HaveSpendingKeyForPaymentAddress(pwalletMain), res)) { |
|
|
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "From address does not belong to this node, zaddr spending key not found."); |
|
|
|
} |
|
|
|
|
|
|
|
// Create data needed to make a SpendDescription
|
|
|
|
SaplingExpandedSpendingKey expsk; |
|
|
|
SpendingKey spendingkey_ = boost::apply_visitor(GetSpendingKeyForPaymentAddress(pwalletMain), res).get(); |
|
|
|
auto sk = boost::get<libzcash::SaplingExtendedSpendingKey>(spendingkey_); |
|
|
|
expsk = sk.expsk; |
|
|
|
uint256 ovk = expsk.full_viewing_key().ovk; |
|
|
|
|
|
|
|
SaplingNoteEntry noteEntry; |
|
|
|
|
|
|
|
auto ctx = librustzcash_sapling_proving_ctx_init(); |
|
|
|
fprintf(stderr,"%s: Created sapling proving context\n", __func__); |
|
|
|
// Empty output script.
|
|
|
|
uint256 dataToBeSigned; |
|
|
|
CScript scriptCode; |
|
|
|
CMutableTransaction mtx; |
|
|
|
UniValue obj(UniValue::VSTR); |
|
|
|
try { |
|
|
|
dataToBeSigned = SignatureHash(scriptCode, mtx, NOT_AN_INPUT, SIGHASH_ALL, 0, branchId); |
|
|
|
} catch (std::logic_error ex) { |
|
|
|
librustzcash_sapling_proving_ctx_free(ctx); |
|
|
|
throw JSONRPCError(RPC_MISC_ERROR, "SignatureHash exception"); |
|
|
|
} |
|
|
|
|
|
|
|
//TODO: Actually get sig data
|
|
|
|
|
|
|
|
SpendDescription shieldedSpend; |
|
|
|
//auto address = sk.default_address();
|
|
|
|
// TODO: can we use the given address directly to avoid edge cases?
|
|
|
|
auto address = sk.DefaultAddress(); |
|
|
|
|
|
|
|
// Create a zutxo with amount=1sat
|
|
|
|
SaplingNote fakenote(address, 1 * SATOSHIDEN); |
|
|
|
SaplingMerkleTree tree; |
|
|
|
auto maybe_cm = fakenote.cm(); |
|
|
|
tree.append(maybe_cm.get()); |
|
|
|
//uint256 anchor;
|
|
|
|
auto anchor = tree.root(); |
|
|
|
//SaplingWitness witness;
|
|
|
|
|
|
|
|
SpendDescriptionInfo spend = SpendDescriptionInfo(expsk, fakenote, anchor, tree.witness()); |
|
|
|
fprintf(stderr,"%s: Created SpendDescriptionInfo\n", __func__); |
|
|
|
|
|
|
|
// Generate spendAuthSig
|
|
|
|
bool spendResult = librustzcash_sapling_spend_sig( |
|
|
|
spend.expsk.ask.begin(), |
|
|
|
spend.alpha.begin(), |
|
|
|
dataToBeSigned.begin(), |
|
|
|
shieldedSpend.spendAuthSig.data()); |
|
|
|
|
|
|
|
if (!spendResult) { |
|
|
|
fprintf(stderr,"%s: librustzcash_sapling_spend_sig() returned false!\n", __func__); |
|
|
|
librustzcash_sapling_proving_ctx_free(ctx); |
|
|
|
throw JSONRPCError(RPC_MISC_ERROR, "Unable to make sapling spend authsig!"); |
|
|
|
} |
|
|
|
|
|
|
|
fprintf(stderr,"%s: spendAuthSig=%s\n", __func__, HexStr(shieldedSpend.spendAuthSig.begin(), shieldedSpend.spendAuthSig.end()).c_str() ); |
|
|
|
|
|
|
|
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); |
|
|
|
ss << tree.witness().path(); |
|
|
|
//ss << spend.witness().path();
|
|
|
|
std::vector<unsigned char> witness(ss.begin(), ss.end()); |
|
|
|
//fprintf(stderr,"%s: witness=%s\n", __func__, string((const char*)witness.data()).c_str());
|
|
|
|
//fprintf(stderr,"%s: Created witness data of size=%d and position=%d\n", __func__, witness.size(), witness.position());
|
|
|
|
fprintf(stderr,"%s: Created witness data of size=%d\n", __func__, (int)witness.size()); |
|
|
|
|
|
|
|
uint256 alpha; |
|
|
|
librustzcash_sapling_generate_r(alpha.begin()); |
|
|
|
fprintf(stderr,"%s: alpha=%s\n", __func__, alpha.GetHex().c_str()); |
|
|
|
|
|
|
|
auto nf = fakenote.nullifier(spend.expsk.full_viewing_key(), tree.witness().position()); |
|
|
|
|
|
|
|
if (!librustzcash_sapling_spend_proof( |
|
|
|
ctx, |
|
|
|
spend.expsk.full_viewing_key().ak.begin(), |
|
|
|
spend.expsk.nsk.begin(), |
|
|
|
fakenote.d.data(), |
|
|
|
fakenote.r.begin(), |
|
|
|
alpha.begin(), //spend.alpha.begin(),
|
|
|
|
spend.note.value(), |
|
|
|
spend.anchor.begin(), |
|
|
|
witness.data(), // const unsigned char *witness
|
|
|
|
shieldedSpend.cv.begin(), |
|
|
|
shieldedSpend.rk.begin(), |
|
|
|
shieldedSpend.zkproof.data())) { |
|
|
|
librustzcash_sapling_proving_ctx_free(ctx); |
|
|
|
fprintf(stderr,"%s: librustzcash_sapling_spend_proof() returned false!\n", __func__); |
|
|
|
throw JSONRPCError(RPC_MISC_ERROR, "Unable to make sapling spend proof!"); |
|
|
|
} |
|
|
|
|
|
|
|
char str[64]; |
|
|
|
|
|
|
|
fprintf(stderr,"%s: zkproof=%s\n", __FUNCTION__, HexStr(shieldedSpend.zkproof.begin(), shieldedSpend.zkproof.end()).c_str()); |
|
|
|
fprintf(stderr,"%s: nf=%s\n", __FUNCTION__, uint256_str(str,nf.get()) ); |
|
|
|
fprintf(stderr,"%s: rk=%s\n", __FUNCTION__, uint256_str(str,shieldedSpend.rk) ); |
|
|
|
|
|
|
|
// This defines our serialization format, should we include mainnet/testnet/regtest ?
|
|
|
|
stringstream zsig; |
|
|
|
zsig << std::string( std::begin(nf.get()), std::end(nf.get()) ); |
|
|
|
zsig << std::string( std::begin(shieldedSpend.rk), std::end(shieldedSpend.rk)); |
|
|
|
zsig << std::string( std::begin(shieldedSpend.zkproof), std::end(shieldedSpend.zkproof)); |
|
|
|
zsig << std::string( std::begin(shieldedSpend.spendAuthSig), std::end(shieldedSpend.spendAuthSig)); |
|
|
|
|
|
|
|
return EncodeBase64(zsig.str()); |
|
|
|
} |
|
|
|
|
|
|
|
UniValue signmessage(const UniValue& params, bool fHelp, const CPubKey& mypk) |
|
|
|
{ |
|
|
|
if (!EnsureWalletIsAvailable(fHelp)) |
|
|
@ -4117,7 +4267,7 @@ UniValue z_listnullifiers(const UniValue& params, bool fHelp, const CPubKey& myp |
|
|
|
"\nReturns the list of Sapling nullifiers.\n" |
|
|
|
"\nResult:\n" |
|
|
|
"[ (json array of string)\n" |
|
|
|
" \"nullifier\" (string) a Sapling nullifer\n" |
|
|
|
" \"nullifier\" (string) a Sapling nullifier\n" |
|
|
|
" ,...\n" |
|
|
|
"]\n" |
|
|
|
"\nExamples:\n" |
|
|
@ -8482,6 +8632,8 @@ static const CRPCCommand commands[] = |
|
|
|
{ "wallet", "setaccount", &setaccount, true }, |
|
|
|
{ "wallet", "settxfee", &settxfee, true }, |
|
|
|
{ "wallet", "signmessage", &signmessage, true }, |
|
|
|
{ "wallet", "z_signmessage", &z_signmessage, true }, |
|
|
|
{ "wallet", "z_verifymessage", &z_verifymessage, true }, |
|
|
|
{ "wallet", "walletlock", &walletlock, true }, |
|
|
|
{ "wallet", "walletpassphrasechange", &walletpassphrasechange, true }, |
|
|
|
{ "wallet", "walletpassphrase", &walletpassphrase, true }, |
|
|
|