Sign JoinSplit transactions
This commit is contained in:
parent
fd7c139e37
commit
6aae9d1a55
|
@ -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
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];
|
||||
|
|
|
@ -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).
|
||||
// keeps the JoinSplit cryptographically bound
|
||||
// to the transaction.
|
||||
//
|
||||
// This must be rectified in the future.
|
||||
// See zcash/#529
|
||||
//
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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…
Reference in New Issue