Skip to content

Commit

Permalink
[zPIV] Remove v2 serials from serialized v4 PublicCoinSpend
Browse files Browse the repository at this point in the history
  • Loading branch information
random-zebra committed Oct 21, 2019
1 parent a9b8aa0 commit 7b81e54
Show file tree
Hide file tree
Showing 6 changed files with 111 additions and 43 deletions.
8 changes: 8 additions & 0 deletions src/libzerocoin/Coin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -299,4 +299,12 @@ bool IsValidCommitmentToCoinRange(const ZerocoinParams* params, const CBigNum& b
}


CBigNum ExtractSerialFromPubKey(const CPubKey pubkey)
{
uint256 hashedPubkey = Hash(pubkey.begin(), pubkey.end()) >> PrivateCoin::V2_BITSHIFT;
uint256 uintSerial = (uint256(0xF) << (256 - PrivateCoin::V2_BITSHIFT)) | hashedPubkey;
return CBigNum(uintSerial);
}


} /* namespace libzerocoin */
1 change: 1 addition & 0 deletions src/libzerocoin/Coin.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ namespace libzerocoin
bool IsValidSerial(const ZerocoinParams* params, const CBigNum& bnSerial);
bool IsValidCommitmentToCoinRange(const ZerocoinParams* params, const CBigNum& bnCommitment);
CBigNum GetAdjustedSerial(const CBigNum& bnSerial);
CBigNum ExtractSerialFromPubKey(const CPubKey pubkey);
bool GenerateKeyPair(const CBigNum& bnGroupOrder, const uint256& nPrivkey, CKey& key, CBigNum& bnSerial);

/** A Public coin is the part of a coin that
Expand Down
7 changes: 7 additions & 0 deletions src/libzerocoin/CoinSpend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -169,4 +169,11 @@ std::vector<unsigned char> CoinSpend::ParseSerial(CDataStream& s) {
return coinSerialNumber.getvch();
}

void CoinSpend::setPubKey(CPubKey pkey, bool fUpdateSerial) {
this->pubkey = pkey;
if (fUpdateSerial) {
this->coinSerialNumber = libzerocoin::ExtractSerialFromPubKey(this->pubkey);
}
}

} /* namespace libzerocoin */
1 change: 1 addition & 0 deletions src/libzerocoin/CoinSpend.h
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ class CoinSpend
bool HasValidSignature() const;
void setTxOutHash(uint256 txOutHash) { this->ptxHash = txOutHash; };
void setDenom(libzerocoin::CoinDenomination denom) { this->denomination = denom; }
void setPubKey(CPubKey pkey, bool fUpdateSerial = false);

CBigNum CalculateValidSerial(ZerocoinParams* params);
std::string ToString() const;
Expand Down
100 changes: 71 additions & 29 deletions src/zpiv/zpivmodule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,18 @@ PublicCoinSpend::PublicCoinSpend(libzerocoin::ZerocoinParams* params, const uint
this->version = version;
this->spendType = libzerocoin::SpendType::SPEND;
this->ptxHash = ptxHash;
this->coinVersion = libzerocoin::ExtractVersionFromSerial(coinSerialNumber);

const int coinVersion = getCoinVersion();

if (coinVersion < libzerocoin::PrivateCoin::PUBKEY_VERSION) {
if (!isAllowed()) {
// v1 coins need at least version 4 spends
if (version < PUBSPEND_SCHNORR) {
std::string errMsg = strprintf("Unable to create PublicCoinSpend version %d with coin version 1. "
"Minimum spend version required: %d", version, PUBSPEND_SCHNORR);
throw std::runtime_error(errMsg);
}
std::string errMsg = strprintf("Unable to create PublicCoinSpend version %d with coin version 1. "
"Minimum spend version required: %d", version, PUBSPEND_SCHNORR);
// this should be unreachable code (already checked in createInput)
// throw runtime error
throw std::runtime_error(errMsg);
}

} else {
if (pubkey && getCoinVersion() >= libzerocoin::PrivateCoin::PUBKEY_VERSION) {
// pubkey available only from v2 coins onwards
this->pubkey = *pubkey;
}
Expand All @@ -41,6 +41,25 @@ PublicCoinSpend::PublicCoinSpend(libzerocoin::ZerocoinParams* params, const uint

}

template <typename Stream>
PublicCoinSpend::PublicCoinSpend(libzerocoin::ZerocoinParams* params, Stream& strm): pubCoin(params) {
strm >> *this;
this->spendType = libzerocoin::SpendType::SPEND;

if (this->version < PUBSPEND_SCHNORR) {
// coinVersion is serialized only from v4 spends
this->coinVersion = libzerocoin::ExtractVersionFromSerial(this->coinSerialNumber);

} else {
// from v4 spends, serialNumber is not serialized for v2 coins anymore.
// in this case, we extract it from the coin public key
if (this->coinVersion >= libzerocoin::PrivateCoin::PUBKEY_VERSION)
this->coinSerialNumber = libzerocoin::ExtractSerialFromPubKey(this->pubkey);

}

}

bool PublicCoinSpend::Verify(const libzerocoin::Accumulator& a, bool verifyParams) const {
return validate();
}
Expand All @@ -62,21 +81,46 @@ bool PublicCoinSpend::validate() const {
if (comm.getCommitmentValue() != pubCoin.getValue()) {
return error("%s: commitments values are not equal", __func__);
}

} else {
// for v1 coins, double check that the serialized coin serial is indeed a v1 serial
if (coinVersion < libzerocoin::PrivateCoin::PUBKEY_VERSION &&
libzerocoin::ExtractVersionFromSerial(this->coinSerialNumber) != coinVersion) {
return error("%s: invalid coin version", __func__);
}

// spend contains a shnorr signature of ptxHash with the randomness of the coin
libzerocoin::ZerocoinParams* params = Params().Zerocoin_Params(fUseV1Params);
if (!schnorrSig.Verify(params, getCoinSerialNumber(), pubCoin.getValue(), getTxOutHash())) {
return error("%s: schnorr signature does not verify", __func__);
}

}

// Now check that the signature validates with the serial
if (!HasValidSignature()) {
return error("%s: signature invalid", __func__);;
}

return true;
}

bool PublicCoinSpend::HasValidSignature() const
{
if (coinVersion < libzerocoin::PrivateCoin::PUBKEY_VERSION)
return true;

// for spend version 3 we must check that the provided pubkey and serial number match
if (version < PUBSPEND_SCHNORR) {
CBigNum extractedSerial = libzerocoin::ExtractSerialFromPubKey(this->pubkey);
if (extractedSerial != this->coinSerialNumber)
return error("%s: hashedpubkey is not equal to the serial!", __func__);
}

return pubkey.Verify(signatureHash(), vchSig);
}


const uint256 PublicCoinSpend::signatureHash() const
{
CHashWriter h(0, 0);
Expand All @@ -87,47 +131,45 @@ const uint256 PublicCoinSpend::signatureHash() const
namespace ZPIVModule {

bool createInput(CTxIn &in, CZerocoinMint &mint, uint256 hashTxOut, const int spendVersion) {
bool fUseV1Params = mint.GetVersion() < libzerocoin::PrivateCoin::PUBKEY_VERSION;
libzerocoin::ZerocoinParams *params = Params().Zerocoin_Params(fUseV1Params);
if (fUseV1Params && spendVersion < PUBSPEND_SCHNORR) {
// check that this spend is allowed
const bool fUseV1Params = mint.GetVersion() < libzerocoin::PrivateCoin::PUBKEY_VERSION;
if (!PublicCoinSpend::isAllowed(fUseV1Params, spendVersion)) {
// v1 coins need at least version 4 spends
std::string errMsg = strprintf("Unable to create PublicCoinSpend version %d with coin version 1. "
"Minimum spend version required: %d", spendVersion, PUBSPEND_SCHNORR);
return error("%s: %s", __func__, errMsg);
}

CKey key;

CPubKey pk;
CPubKey* pk_ptr = nullptr;
if (!fUseV1Params) {
if (!mint.GetKeyPair(key))
return error("%s: failed to set zPIV privkey mint.", __func__);
pk = key.GetPubKey();
pk_ptr = &pk;
}

PublicCoinSpend spend(params, spendVersion, mint.GetSerialNumber(),
mint.GetRandomness(), hashTxOut, pk_ptr);
// create the PublicCoinSpend
libzerocoin::ZerocoinParams *params = Params().Zerocoin_Params(fUseV1Params);
PublicCoinSpend spend(params, spendVersion, mint.GetSerialNumber(), mint.GetRandomness(), hashTxOut, nullptr);

spend.outputIndex = mint.GetOutputIndex();
spend.txHash = mint.GetTxHash();
spend.setDenom(mint.GetDenomination());

std::vector<unsigned char> vchSig;
// add public key and signature
if (!fUseV1Params) {
if (!key.Sign(spend.signatureHash(), vchSig))
throw std::runtime_error("ZPIVModule failed to sign signatureHash\n");
CKey key;
if (!mint.GetKeyPair(key))
return error("%s: failed to set zPIV privkey mint.", __func__);
spend.setPubKey(key.GetPubKey(), true);

std::vector<unsigned char> vchSig;
if (!key.Sign(spend.signatureHash(), vchSig))
return error("%s: ZPIVModule failed to sign signatureHash.", __func__);
spend.setVchSig(vchSig);

}

// serialize the PublicCoinSpend and add it to the input scriptSig
CDataStream ser(SER_NETWORK, PROTOCOL_VERSION);
ser << spend;

std::vector<unsigned char> data(ser.begin(), ser.end());
CScript scriptSigIn = CScript() << OP_ZEROCOINPUBLICSPEND << data.size();
scriptSigIn.insert(scriptSigIn.end(), data.begin(), data.end());

// create the tx input
in = CTxIn(mint.GetTxHash(), mint.GetOutputIndex(), scriptSigIn, mint.GetDenomination());
in.nSequence = mint.GetDenomination();
return true;
Expand Down
37 changes: 23 additions & 14 deletions src/zpiv/zpivmodule.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,25 +26,25 @@ class PublicCoinSpend : public libzerocoin::CoinSpend {
public:

PublicCoinSpend(libzerocoin::ZerocoinParams* params): pubCoin(params) {};

PublicCoinSpend(libzerocoin::ZerocoinParams* params, const uint8_t version, const CBigNum& serial, const CBigNum& randomness, const uint256& ptxHash, CPubKey* pubkey);
template <typename Stream> PublicCoinSpend(libzerocoin::ZerocoinParams* params, Stream& strm);

~PublicCoinSpend(){};

template <typename Stream>
PublicCoinSpend(
libzerocoin::ZerocoinParams* params,
Stream& strm): pubCoin(params) {
strm >> *this;
this->spendType = libzerocoin::SpendType::SPEND;
}

const uint256 signatureHash() const override;
void setVchSig(std::vector<unsigned char> vchSig) { this->vchSig = vchSig; };
bool Verify(const libzerocoin::Accumulator& a, bool verifyParams = true) const override;
bool HasValidSignature() const;
bool validate() const;
static bool isAllowed(const bool fUseV1Params, const int spendVersion) { return !fUseV1Params || spendVersion >= PUBSPEND_SCHNORR; }
bool isAllowed() const {
const bool fUseV1Params = getCoinVersion() < libzerocoin::PrivateCoin::PUBKEY_VERSION;
return isAllowed(fUseV1Params, version);
}
int getCoinVersion() const { return this->coinVersion; }

// Members
int coinVersion;
CBigNum randomness;
libzerocoin::CoinRandomnessSchnorrSignature schnorrSig;
// prev out values
Expand All @@ -57,14 +57,23 @@ class PublicCoinSpend : public libzerocoin::CoinSpend {
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
READWRITE(version);
READWRITE(coinSerialNumber);
if (version < PUBSPEND_SCHNORR)

if (version < PUBSPEND_SCHNORR) {
READWRITE(coinSerialNumber);
READWRITE(randomness);
else
READWRITE(schnorrSig);
if (libzerocoin::ExtractVersionFromSerial(coinSerialNumber) >= libzerocoin::PrivateCoin::PUBKEY_VERSION) {
READWRITE(pubkey);
READWRITE(vchSig);

} else {
READWRITE(coinVersion);
if (coinVersion < libzerocoin::PrivateCoin::PUBKEY_VERSION) {
READWRITE(coinSerialNumber);
}
else {
READWRITE(pubkey);
READWRITE(vchSig);
}
READWRITE(schnorrSig);
}
}
};
Expand Down

0 comments on commit 7b81e54

Please sign in to comment.