Sign JoinSplit transactions
This commit is contained in:
parent
fd7c139e37
commit
6aae9d1a55
|
@ -10,6 +10,7 @@
|
||||||
#include "script/script.h"
|
#include "script/script.h"
|
||||||
#include "serialize.h"
|
#include "serialize.h"
|
||||||
#include "uint256.h"
|
#include "uint256.h"
|
||||||
|
#include "pubkey.h"
|
||||||
|
|
||||||
#include <boost/array.hpp>
|
#include <boost/array.hpp>
|
||||||
|
|
||||||
|
@ -302,6 +303,10 @@ public:
|
||||||
const std::vector<CTxOut> vout;
|
const std::vector<CTxOut> vout;
|
||||||
const uint32_t nLockTime;
|
const uint32_t nLockTime;
|
||||||
const std::vector<CPourTx> vpour;
|
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() */
|
/** Construct a CTransaction that qualifies as IsNull() */
|
||||||
CTransaction();
|
CTransaction();
|
||||||
|
@ -322,6 +327,10 @@ public:
|
||||||
READWRITE(*const_cast<uint32_t*>(&nLockTime));
|
READWRITE(*const_cast<uint32_t*>(&nLockTime));
|
||||||
if (nVersion >= 2) {
|
if (nVersion >= 2) {
|
||||||
READWRITE(*const_cast<std::vector<CPourTx>*>(&vpour));
|
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())
|
if (ser_action.ForRead())
|
||||||
UpdateHash();
|
UpdateHash();
|
||||||
|
@ -375,6 +384,10 @@ struct CMutableTransaction
|
||||||
std::vector<CTxOut> vout;
|
std::vector<CTxOut> vout;
|
||||||
uint32_t nLockTime;
|
uint32_t nLockTime;
|
||||||
std::vector<CPourTx> vpour;
|
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();
|
||||||
CMutableTransaction(const CTransaction& tx);
|
CMutableTransaction(const CTransaction& tx);
|
||||||
|
@ -390,6 +403,10 @@ struct CMutableTransaction
|
||||||
READWRITE(nLockTime);
|
READWRITE(nLockTime);
|
||||||
if (nVersion >= 2) {
|
if (nVersion >= 2) {
|
||||||
READWRITE(vpour);
|
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 "hash.h"
|
||||||
#include "serialize.h"
|
#include "serialize.h"
|
||||||
#include "uint256.h"
|
#include "uint256.h"
|
||||||
|
#include "sodium.h"
|
||||||
|
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
@ -187,6 +188,81 @@ public:
|
||||||
bool Derive(CPubKey& pubkeyChild, ChainCode &ccChild, unsigned int nChild, const ChainCode& cc) const;
|
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 {
|
struct CExtPubKey {
|
||||||
unsigned char nDepth;
|
unsigned char nDepth;
|
||||||
unsigned char vchFingerprint[4];
|
unsigned char vchFingerprint[4];
|
||||||
|
|
|
@ -14,6 +14,8 @@
|
||||||
#include "script/script.h"
|
#include "script/script.h"
|
||||||
#include "uint256.h"
|
#include "uint256.h"
|
||||||
|
|
||||||
|
#include "sodium.h"
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
typedef vector<unsigned char> valtype;
|
typedef vector<unsigned char> valtype;
|
||||||
|
@ -1030,6 +1032,7 @@ public:
|
||||||
// Serialize the prevout
|
// Serialize the prevout
|
||||||
::Serialize(s, txTo.vin[nInput].prevout, nType, nVersion);
|
::Serialize(s, txTo.vin[nInput].prevout, nType, nVersion);
|
||||||
// Serialize the script
|
// Serialize the script
|
||||||
|
assert(nInput != NOT_AN_INPUT);
|
||||||
if (nInput != nIn)
|
if (nInput != nIn)
|
||||||
// Blank out other inputs' signatures
|
// Blank out other inputs' signatures
|
||||||
::Serialize(s, CScript(), nType, nVersion);
|
::Serialize(s, CScript(), nType, nVersion);
|
||||||
|
@ -1073,22 +1076,14 @@ public:
|
||||||
|
|
||||||
// Serialize vpour
|
// Serialize vpour
|
||||||
if (txTo.nVersion >= 2) {
|
if (txTo.nVersion >= 2) {
|
||||||
// TODO:
|
|
||||||
//
|
//
|
||||||
// SIGHASH_* functions will hash portions of
|
// SIGHASH_* functions will hash portions of
|
||||||
// the transaction for use in signatures. This
|
// the transaction for use in signatures. This
|
||||||
// keeps the pour cryptographically bound to
|
// keeps the JoinSplit cryptographically bound
|
||||||
// the transaction from the perspective of the
|
// to the transaction.
|
||||||
// inputs (but not from the perspective of the
|
|
||||||
// pour).
|
|
||||||
//
|
//
|
||||||
// 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.vpour, nType, nVersion);
|
||||||
|
::Serialize(s, txTo.joinSplitPubKey, nType, nVersion);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -12,12 +12,16 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <climits>
|
||||||
|
|
||||||
class CPubKey;
|
class CPubKey;
|
||||||
class CScript;
|
class CScript;
|
||||||
class CTransaction;
|
class CTransaction;
|
||||||
class uint256;
|
class uint256;
|
||||||
|
|
||||||
|
/** Special case nIn for signing JoinSplits. */
|
||||||
|
const unsigned int NOT_AN_INPUT = UINT_MAX;
|
||||||
|
|
||||||
/** Signature hash types/flags */
|
/** Signature hash types/flags */
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
|
|
|
@ -18,6 +18,8 @@
|
||||||
#include "walletdb.h"
|
#include "walletdb.h"
|
||||||
#include "primitives/transaction.h"
|
#include "primitives/transaction.h"
|
||||||
#include "zcbenchmarks.h"
|
#include "zcbenchmarks.h"
|
||||||
|
#include "key.h"
|
||||||
|
#include "script/interpreter.h"
|
||||||
|
|
||||||
#include <stdint.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");
|
throw runtime_error("unsupported pour input/output counts");
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: #808
|
CKey joinSplitPrivKey;
|
||||||
uint256 pubKeyHash;
|
joinSplitPrivKey.MakeNewKey(true);
|
||||||
|
CCompressedPubKey joinSplitPubKey(joinSplitPrivKey.GetPubKey());
|
||||||
|
|
||||||
|
CMutableTransaction mtx(tx);
|
||||||
|
mtx.nVersion = 2;
|
||||||
|
mtx.joinSplitPubKey = joinSplitPubKey;
|
||||||
|
|
||||||
CPourTx pourtx(*pzcashParams,
|
CPourTx pourtx(*pzcashParams,
|
||||||
pubKeyHash,
|
joinSplitPubKey.GetZcashHash(),
|
||||||
anchor,
|
anchor,
|
||||||
{vpourin[0], vpourin[1]},
|
{vpourin[0], vpourin[1]},
|
||||||
{vpourout[0], vpourout[1]},
|
{vpourout[0], vpourout[1]},
|
||||||
vpub_old,
|
vpub_old,
|
||||||
vpub_new);
|
vpub_new);
|
||||||
|
assert(pourtx.Verify(*pzcashParams, joinSplitPubKey.GetZcashHash()));
|
||||||
|
|
||||||
assert(pourtx.Verify(*pzcashParams, pubKeyHash));
|
|
||||||
|
|
||||||
CMutableTransaction mtx(tx);
|
|
||||||
mtx.nVersion = 2;
|
|
||||||
mtx.vpour.push_back(pourtx);
|
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);
|
CTransaction rawTx(mtx);
|
||||||
|
|
||||||
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
|
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 << ((unsigned char) 0x00);
|
||||||
ss2 << pourtx.ephemeralKey;
|
ss2 << pourtx.ephemeralKey;
|
||||||
ss2 << pourtx.ciphertexts[0];
|
ss2 << pourtx.ciphertexts[0];
|
||||||
ss2 << pourtx.h_sig(*pzcashParams, pubKeyHash);
|
ss2 << pourtx.h_sig(*pzcashParams, joinSplitPubKey.GetZcashHash());
|
||||||
|
|
||||||
encryptedBucket1 = HexStr(ss2.begin(), ss2.end());
|
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 << ((unsigned char) 0x01);
|
||||||
ss2 << pourtx.ephemeralKey;
|
ss2 << pourtx.ephemeralKey;
|
||||||
ss2 << pourtx.ciphertexts[1];
|
ss2 << pourtx.ciphertexts[1];
|
||||||
ss2 << pourtx.h_sig(*pzcashParams, pubKeyHash);
|
ss2 << pourtx.h_sig(*pzcashParams, joinSplitPubKey.GetZcashHash());
|
||||||
|
|
||||||
encryptedBucket2 = HexStr(ss2.begin(), ss2.end());
|
encryptedBucket2 = HexStr(ss2.begin(), ss2.end());
|
||||||
}
|
}
|
||||||
|
|
|
@ -301,7 +301,7 @@ uint256 JoinSplit<NumInputs, NumOutputs>::h_sig(
|
||||||
const boost::array<uint256, NumInputs>& nullifiers,
|
const boost::array<uint256, NumInputs>& nullifiers,
|
||||||
const uint256& pubKeyHash
|
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'};
|
= {'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());
|
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,
|
template class JoinSplit<ZC_NUM_JS_INPUTS,
|
||||||
ZC_NUM_JS_OUTPUTS>;
|
ZC_NUM_JS_OUTPUTS>;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue