Compare commits

...

21 Commits

Author SHA1 Message Date
Duke 00ea085746 Merge branch 'dev' into z_signmessage 8 months ago
Duke Leto 10fc949252 WIP attempting to deserialize nullifier 4 years ago
Duke Leto ba221fe2f1 Start to implement zsig verification 4 years ago
Duke Leto b21fd893df Only check for a valid zaddr in z_verifymessage 4 years ago
Duke Leto b82e49b914 Turns out we are not using ecdsa, but it was fun 4 years ago
Duke Leto dd9b89bcbd Serialize the zksnark sapling spend data 4 years ago
Duke Leto 1391315b4a Avoid core dumps and actually get back base64 data 4 years ago
Duke Leto 0bcd779fda Setup CHashWriter stuff 4 years ago
Duke Leto 6f0799a5b0 Here come the coredumps 4 years ago
Duke Leto db41b5b05f Create nullifier from viewkey and witness from fakenote 4 years ago
Duke Leto 5bf5650284 librustzcash_sapling_spend_proof now succeeds, giving it the actual fakenote we created instead of an empty note in another data structure 4 years ago
Duke Leto ee0a0d39c7 Log more debug data 4 years ago
Duke Leto ee83126f08 Throw exceptions instead of returning empty JSON for error conditions; check return result of librustzcash_sapling_spend_sig; change return result type to a string 4 years ago
Duke Leto 74aea430e0 Get things compiling 4 years ago
Duke Leto 1b13ac5b52 Generate spend proof via librustzcash 4 years ago
Duke Leto 385b4b14d7 Create a fake SaplingNote for given zaddr of 1 puposhi, and MerkleTree with note commitment as only element 4 years ago
Duke Leto a45c81e134 Create more data that is needed for making our fake Sapling SpendDescription 4 years ago
Duke Leto 8155559832 Delete some sprout code inside AsyncRPCOperation_sendmany::find_unspent_notes 4 years ago
Duke Leto 8644555d5e Get spending key, expsk and ovk for a zaddr 4 years ago
Duke Leto f12a4e83fb Start to create librustzcash data needed to make a spendAuthSig 4 years ago
Duke Leto dad4b8a706 Initial work towards z_signmessage+z_verifymessage 4 years ago
  1. 77
      src/rpc/misc.cpp
  2. 3
      src/rpc/server.h
  3. 3
      src/wallet/asyncrpcoperation_sendmany.cpp
  4. 154
      src/wallet/rpcwallet.cpp

77
src/rpc/misc.cpp

@ -701,6 +701,81 @@ UniValue createmultisig(const UniValue& params, bool fHelp, const CPubKey& mypk)
return result;
}
UniValue z_verifymessage(const UniValue& params, bool fHelp, const CPubKey& mypk)
{
if (fHelp || params.size() != 3)
throw runtime_error(
"z_verifymessage \"zaddr\" \"signature\" \"message\"\n"
"\nVerify a signed message\n"
"\nArguments:\n"
"1. \"zaddr\" (string, required) The Sapling zaddr to use for the signature.\n"
"2. \"signature\" (string, required) The signature provided by the signer in base 64 encoding (see signmessage).\n"
"3. \"message\" (string, required) The message that was signed.\n"
"\nResult:\n"
"true|false (boolean) If the signature is verified or not.\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_verifymessage", "\"zs1...\", \"signature\", \"my message\"")
);
LOCK(cs_main);
string strAddress = params[0].get_str();
string strSign = params[1].get_str();
string strMessage = params[2].get_str();
fprintf(stderr,"%s: base64 data of size %d\n", __func__, (int)strSign.size());
fprintf(stderr,"%s: message data of size %d\n", __func__, (int)strMessage.size());
// Is it a valid zaddr in this set of consensus rules?
auto res = DecodePaymentAddress(strAddress);
uint32_t branchId = CurrentEpochBranchId(chainActive.Height(), Params().GetConsensus());
if (!IsValidPaymentAddress(res, branchId)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid zaddr!");
}
bool fInvalid = false;
vector<unsigned char> vchSig = DecodeBase64(strSign.c_str(), &fInvalid);
if (fInvalid)
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Malformed base64 encoding");
fprintf(stderr,"%s: Valid base64, decoded size %d\n", __func__, (int)vchSig.size());
bool validSig = false;
int zSigSize = 320;
if (vchSig.size() == zSigSize) {
//TODO: parse out nf, rk, zkproof, spendAuthSig from vchSig
stringstream ss;
char str[64];
uint256 nf, rk;
//spend_auth_sig_t spendAuthSig;
//librustzcash::GrothProof zkproof;
string nullif;
ss >> nullif;
char *buffer = new char[64];
string s1;
std::streamsize ssize = 64;
ss.read(buffer, ssize);
fprintf(stderr,"%s: buffer=%s\n", __func__, HexStr(string(buffer)).c_str());
//string nullifier(buffer);
//fprintf(stderr,"%s: buffer=%s\n", __func__, HexStr(nullifier.begin(), nullifier.end()).c_str());
//nf.SetHex(buffer);
//fprintf(stderr,"%s: nf=%s\n", __func__, uint256_str(str,nf) );
nf.SetHex(s1);
fprintf(stderr,"%s: nf=%s\n", __func__, uint256_str(str,nf) );
// ss >> rk;
} else {
fprintf(stderr,"%s: invalid signature size! %d\n", __func__, (int)vchSig.size());
}
return validSig;
}
UniValue verifymessage(const UniValue& params, bool fHelp, const CPubKey& mypk)
{
if (fHelp || params.size() != 3)
@ -1584,8 +1659,8 @@ static const CRPCCommand commands[] =
{ "util", "z_validateaddress", &z_validateaddress, true }, /* uses wallet if enabled */
{ "util", "createmultisig", &createmultisig, true },
{ "util", "verifymessage", &verifymessage, true },
{ "util", "z_verifymessage", &z_verifymessage, true },
{ "util", "rpcinfo", &rpcinfo, true },
/* Not shown in help */
{ "hidden", "setmocktime", &setmocktime, true },
};

3
src/rpc/server.h

@ -481,7 +481,8 @@ extern UniValue z_listoperationids(const UniValue& params, bool fHelp, const CPu
extern UniValue opreturn_burn(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcwallet.cpp
extern UniValue rescan(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcwallet.cpp
extern UniValue z_validateaddress(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcmisc.cpp
extern UniValue z_signmessage(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcwallet.cpp
extern UniValue z_verifymessage(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcwallet.cpp
extern UniValue MoMoMdata(const UniValue& params, bool fHelp, const CPubKey& mypk);
extern UniValue calc_MoM(const UniValue& params, bool fHelp, const CPubKey& mypk);
extern UniValue height_MoM(const UniValue& params, bool fHelp, const CPubKey& mypk);

3
src/wallet/asyncrpcoperation_sendmany.cpp

@ -45,6 +45,7 @@
#include <thread>
#include <string>
#include <boost/optional/optional_io.hpp>
#define SATOSHIDEN ((uint64_t)100000000L)
using namespace libzcash;
@ -665,6 +666,7 @@ bool AsyncRPCOperation_sendmany::find_unspent_notes() {
std::vector<SaplingNoteEntry> saplingEntries;
{
LOCK2(cs_main, pwalletMain->cs_wallet);
pwalletMain->GetFilteredNotes(saplingEntries, fromaddress_, mindepth_);
}
@ -680,6 +682,7 @@ bool AsyncRPCOperation_sendmany::find_unspent_notes() {
}
// sort in descending order, so big notes appear first
//TODO: what are the performance and metadata leakage implications of this?
std::sort(z_sapling_inputs_.begin(), z_sapling_inputs_.end(),
[](SaplingNoteEntry i, SaplingNoteEntry j) -> bool {
return i.note.value() > j.note.value();

154
src/wallet/rpcwallet.cpp

@ -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 },

Loading…
Cancel
Save