Browse Source

Sign JoinSplit transactions

pull/145/head
Taylor Hornby 8 years ago
parent
commit
6aae9d1a55
  1. 17
      src/primitives/transaction.h
  2. 76
      src/pubkey.h
  3. 17
      src/script/interpreter.cpp
  4. 4
      src/script/interpreter.h
  5. 36
      src/wallet/rpcwallet.cpp
  6. 4
      src/zcash/JoinSplit.cpp

17
src/primitives/transaction.h

@ -10,6 +10,7 @@
#include "script/script.h"
#include "serialize.h"
#include "uint256.h"
#include "pubkey.h"
#include <boost/array.hpp>
@ -302,6 +303,10 @@ public:
const std::vector<CTxOut> vout;
const uint32_t nLockTime;
const std::vector<CPourTx> vpour;
// TODO: This should be an unsigned char[33] (or boost array)
const CCompressedPubKey joinSplitPubKey;
// TODO: This should be an unsigned char[64] (or boost array)
const std::vector<unsigned char> joinSplitSig;
/** Construct a CTransaction that qualifies as IsNull() */
CTransaction();
@ -322,6 +327,10 @@ public:
READWRITE(*const_cast<uint32_t*>(&nLockTime));
if (nVersion >= 2) {
READWRITE(*const_cast<std::vector<CPourTx>*>(&vpour));
if (vpour.size() > 0) {
READWRITE(*const_cast<CCompressedPubKey*>(&joinSplitPubKey));
READWRITE(*const_cast<std::vector<unsigned char>*>(&joinSplitSig));
}
}
if (ser_action.ForRead())
UpdateHash();
@ -375,6 +384,10 @@ struct CMutableTransaction
std::vector<CTxOut> vout;
uint32_t nLockTime;
std::vector<CPourTx> vpour;
// TODO: This should be an unsigned char[33] (or boost array)
CCompressedPubKey joinSplitPubKey;
// TODO: This should be an unsigned char[64] (or boost array)
std::vector<unsigned char> joinSplitSig;
CMutableTransaction();
CMutableTransaction(const CTransaction& tx);
@ -390,6 +403,10 @@ struct CMutableTransaction
READWRITE(nLockTime);
if (nVersion >= 2) {
READWRITE(vpour);
if (vpour.size() > 0) {
READWRITE(joinSplitPubKey);
READWRITE(joinSplitSig);
}
}
}

76
src/pubkey.h

@ -9,6 +9,7 @@
#include "hash.h"
#include "serialize.h"
#include "uint256.h"
#include "sodium.h"
#include <stdexcept>
#include <vector>
@ -187,6 +188,81 @@ public:
bool Derive(CPubKey& pubkeyChild, ChainCode &ccChild, unsigned int nChild, const ChainCode& cc) const;
};
class CCompressedPubKey {
private:
CPubKey pubKey;
public:
CCompressedPubKey()
{
// pubKey's 0-argument constructor invalidates it.
}
CCompressedPubKey(const CPubKey &pubKey)
{
this->pubKey = pubKey;
// TODO: check that it's compressed and valid and throw exception if
// not.
}
unsigned int GetSerializeSize(int nType, int nVersion) const
{
assert(pubKey.size() == 33);
return pubKey.size();
}
template <typename Stream>
void Serialize(Stream& s, int nType, int nVersion) const
{
unsigned int len = pubKey.size();
assert(len == 33);
s.write((char*)pubKey.begin(), len);
}
template <typename Stream>
void Unserialize(Stream& s, int nType, int nVersion)
{
unsigned int len = 33;
s.read((char*)pubKey.begin(), len);
// TODO: check that it's compressed and valid.
}
//! Get the 256-bit hash of this public key for the Zcash protocol.
uint256 GetZcashHash() const
{
// TODO: is the thing in vch actually the right thing to hash/encode?
const unsigned char personalization[crypto_generichash_blake2b_PERSONALBYTES]
= {'Z','c','a','s','h','E','C','D','S','A','P','u','b','K','e','y'};
uint256 hash;
assert(pubKey[0] == 2 || pubKey[0] == 3);
assert(pubKey.size() == 33);
if (crypto_generichash_blake2b_salt_personal(hash.begin(), 32,
pubKey.begin(), pubKey.size(),
NULL, 0, // No key.
NULL, // No salt.
personalization
) != 0)
{
throw std::logic_error("hash function failure");
}
return hash;
}
// TODO: implement this to verify the shorter kind of signature
// TODO: make sure to check the s value thing etc.
// TODO: this used to have "const" at the end, what does that mean??
bool Verify(const uint256& hash, const std::vector<unsigned char>& vchSig)
{
// TODO implement signature verification.
return false;
}
};
struct CExtPubKey {
unsigned char nDepth;
unsigned char vchFingerprint[4];

17
src/script/interpreter.cpp

@ -14,6 +14,8 @@
#include "script/script.h"
#include "uint256.h"
#include "sodium.h"
using namespace std;
typedef vector<unsigned char> valtype;
@ -1030,6 +1032,7 @@ public:
// Serialize the prevout
::Serialize(s, txTo.vin[nInput].prevout, nType, nVersion);
// Serialize the script
assert(nInput != NOT_AN_INPUT);
if (nInput != nIn)
// Blank out other inputs' signatures
::Serialize(s, CScript(), nType, nVersion);
@ -1073,22 +1076,14 @@ public:
// Serialize vpour
if (txTo.nVersion >= 2) {
// TODO:
//
// SIGHASH_* functions will hash portions of
// the transaction for use in signatures. This
// keeps the pour cryptographically bound to
// the transaction from the perspective of the
// inputs (but not from the perspective of the
// pour).
//
// This must be rectified in the future.
// See zcash/#529
// keeps the JoinSplit cryptographically bound
// to the transaction.
//
// It will be necessary to change this API to
// be abstract over whether an input script is
// being skipped or a pour is being skipped.
::Serialize(s, txTo.vpour, nType, nVersion);
::Serialize(s, txTo.joinSplitPubKey, nType, nVersion);
}
}
};

4
src/script/interpreter.h

@ -12,12 +12,16 @@
#include <vector>
#include <stdint.h>
#include <string>
#include <climits>
class CPubKey;
class CScript;
class CTransaction;
class uint256;
/** Special case nIn for signing JoinSplits. */
const unsigned int NOT_AN_INPUT = UINT_MAX;
/** Signature hash types/flags */
enum
{

36
src/wallet/rpcwallet.cpp

@ -18,6 +18,8 @@
#include "walletdb.h"
#include "primitives/transaction.h"
#include "zcbenchmarks.h"
#include "key.h"
#include "script/interpreter.h"
#include <stdint.h>
@ -2650,22 +2652,38 @@ Value zc_raw_pour(const json_spirit::Array& params, bool fHelp)
throw runtime_error("unsupported pour input/output counts");
}
// TODO: #808
uint256 pubKeyHash;
CKey joinSplitPrivKey;
joinSplitPrivKey.MakeNewKey(true);
CCompressedPubKey joinSplitPubKey(joinSplitPrivKey.GetPubKey());
CMutableTransaction mtx(tx);
mtx.nVersion = 2;
mtx.joinSplitPubKey = joinSplitPubKey;
CPourTx pourtx(*pzcashParams,
pubKeyHash,
joinSplitPubKey.GetZcashHash(),
anchor,
{vpourin[0], vpourin[1]},
{vpourout[0], vpourout[1]},
vpub_old,
vpub_new);
assert(pourtx.Verify(*pzcashParams, joinSplitPubKey.GetZcashHash()));
assert(pourtx.Verify(*pzcashParams, pubKeyHash));
CMutableTransaction mtx(tx);
mtx.nVersion = 2;
mtx.vpour.push_back(pourtx);
// TODO: #966.
static const uint256 one(uint256S("0000000000000000000000000000000000000000000000000000000000000001"));
// Empty output script.
CScript scriptCode;
CTransaction signTx(mtx);
uint256 dataToBeSigned = SignatureHash(scriptCode, signTx, NOT_AN_INPUT, SIGHASH_ALL);
if (dataToBeSigned == one) {
throw runtime_error("SignatureHash failed");
}
// Add the signature
joinSplitPrivKey.SignCompact(dataToBeSigned, mtx.joinSplitSig);
CTransaction rawTx(mtx);
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
@ -2678,7 +2696,7 @@ Value zc_raw_pour(const json_spirit::Array& params, bool fHelp)
ss2 << ((unsigned char) 0x00);
ss2 << pourtx.ephemeralKey;
ss2 << pourtx.ciphertexts[0];
ss2 << pourtx.h_sig(*pzcashParams, pubKeyHash);
ss2 << pourtx.h_sig(*pzcashParams, joinSplitPubKey.GetZcashHash());
encryptedBucket1 = HexStr(ss2.begin(), ss2.end());
}
@ -2687,7 +2705,7 @@ Value zc_raw_pour(const json_spirit::Array& params, bool fHelp)
ss2 << ((unsigned char) 0x01);
ss2 << pourtx.ephemeralKey;
ss2 << pourtx.ciphertexts[1];
ss2 << pourtx.h_sig(*pzcashParams, pubKeyHash);
ss2 << pourtx.h_sig(*pzcashParams, joinSplitPubKey.GetZcashHash());
encryptedBucket2 = HexStr(ss2.begin(), ss2.end());
}

4
src/zcash/JoinSplit.cpp

@ -301,7 +301,7 @@ uint256 JoinSplit<NumInputs, NumOutputs>::h_sig(
const boost::array<uint256, NumInputs>& nullifiers,
const uint256& pubKeyHash
) {
unsigned char personalization[crypto_generichash_blake2b_PERSONALBYTES]
const unsigned char personalization[crypto_generichash_blake2b_PERSONALBYTES]
= {'Z','c','a','s','h','C','o','m','p','u','t','e','h','S','i','g'};
std::vector<unsigned char> block(randomSeed.begin(), randomSeed.end());
@ -349,4 +349,4 @@ JSInput::JSInput() : witness(ZCIncrementalMerkleTree().witness()),
template class JoinSplit<ZC_NUM_JS_INPUTS,
ZC_NUM_JS_OUTPUTS>;
}
}

Loading…
Cancel
Save