Range proofs in witness

This commit is contained in:
Pieter Wuille 2016-06-04 13:05:05 +02:00 committed by Gregory Sanders
parent 5093a5c61c
commit 670e7fb32e
3 changed files with 69 additions and 9 deletions

View file

@ -90,6 +90,9 @@ public:
void FromTx(const CTransaction &tx, int nHeightIn) {
fCoinBase = tx.IsCoinBase();
vout = tx.vout;
for (size_t i = 0; i < vout.size(); i++) {
CTxOutWitnessSerializer(vout[i]).SetNull();
}
nHeight = nHeightIn;
nVersion = tx.nVersion;
ClearUnspendable();

View file

@ -154,13 +154,9 @@ public:
if (ser_action.ForRead())
SetToBitcoinAmount(nAmount);
} else {
// Though the range proof + nonce commitment are essentially witness data,
// we dont care too much about the space-savings here. Also, since they're
// signed by everything (output data), we don't take a malleability hit for
// doing this.
// We only serialize the value commitment here.
// The ECDH key and range proof are serialized through CTxOutWitnessSerializer.
READWRITE(REF(CFlatData(&vchCommitment[0], &vchCommitment[nCommitmentSize])));
READWRITE(vchRangeproof);
READWRITE(vchNonceCommitment);
}
}
@ -271,6 +267,34 @@ public:
std::string ToString() const;
};
class CTxOutWitnessSerializer
{
CTxOut& ref;
public:
CTxOutWitnessSerializer(CTxOut& ref_) : ref(ref_) {}
ADD_SERIALIZE_METHODS;
bool IsNull() const {
return ref.nValue.vchRangeproof.empty() && ref.nValue.vchNonceCommitment.empty();
}
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
if (!(nVersion & SERIALIZE_BITCOIN_BLOCK_OR_TX)) {
READWRITE(ref.nValue.vchRangeproof);
READWRITE(ref.nValue.vchNonceCommitment);
}
}
void SetNull() {
std::vector<unsigned char>().swap(ref.nValue.vchRangeproof);
std::vector<unsigned char>().swap(ref.nValue.vchNonceCommitment);
}
};
class CTxInWitness
{
public:
@ -346,13 +370,17 @@ struct CMutableTransaction;
* - std::vector<CTxOut> vout
* - if (flags & 1):
* - CTxWitness wit;
* - if (flags & 2):
* - CTxOutWitness witout;
* - uint32_t nLockTime
*/
static const CAmount TX_FEE_BITCOIN_TX_FLAG = -42;
template<typename Stream, typename Operation, typename TxType>
inline void SerializeTransaction(TxType& tx, Stream& s, Operation ser_action, int nType, int nVersion) {
const bool fAllowWitness = !(nVersion & SERIALIZE_TRANSACTION_NO_WITNESS);
const bool fIsBitcoinTx = (nVersion & SERIALIZE_BITCOIN_BLOCK_OR_TX);
READWRITE(*const_cast<int32_t*>(&tx.nVersion));
if ((ser_action.ForRead() || (!ser_action.ForRead() && tx.nTxFee != TX_FEE_BITCOIN_TX_FLAG)) && !(nVersion & SERIALIZE_BITCOIN_BLOCK_OR_TX))
if ((ser_action.ForRead() || (!ser_action.ForRead() && tx.nTxFee != TX_FEE_BITCOIN_TX_FLAG)) && !fIsBitcoinTx)
READWRITE(*const_cast<CAmount*>(&tx.nTxFee));
else if (ser_action.ForRead())
const_cast<CAmount&>(tx.nTxFee) = TX_FEE_BITCOIN_TX_FLAG;
@ -380,6 +408,21 @@ inline void SerializeTransaction(TxType& tx, Stream& s, Operation ser_action, in
const_cast<CTxWitness*>(&tx.wit)->vtxinwit.resize(tx.vin.size());
READWRITE(tx.wit);
}
if ((flags & 2) && fAllowWitness && !fIsBitcoinTx) {
/* The witness output flag is present, and we support witnesses. */
flags ^= 2;
bool fHadOutputWitness = false;
for (size_t i = 0; i < tx.vout.size(); i++) {
CTxOutWitnessSerializer witser(REF(tx.vout[i]));
READWRITE(witser);
if (!witser.IsNull()) {
fHadOutputWitness = true;
}
}
if (!fHadOutputWitness) {
throw std::ios_base::failure("Superfluous output witness record");
}
}
if (flags) {
/* Unknown flag in the serialization */
throw std::ios_base::failure("Unknown transaction optional data");
@ -392,6 +435,14 @@ inline void SerializeTransaction(TxType& tx, Stream& s, Operation ser_action, in
if (!tx.wit.IsNull()) {
flags |= 1;
}
if (!fIsBitcoinTx) {
for (size_t i = 0; i < tx.vout.size(); i++) {
if (!CTxOutWitnessSerializer(*const_cast<CTxOut*>(&tx.vout[i])).IsNull()) {
flags |= 2;
break;
}
}
}
}
if (flags) {
/* Use extended format in case witnesses are to be serialized. */
@ -405,6 +456,12 @@ inline void SerializeTransaction(TxType& tx, Stream& s, Operation ser_action, in
const_cast<CTxWitness*>(&tx.wit)->vtxinwit.resize(tx.vin.size());
READWRITE(tx.wit);
}
if (flags & 2) {
for (size_t i = 0; i < tx.vout.size(); i++) {
CTxOutWitnessSerializer witser(*const_cast<CTxOut*>(&tx.vout[i]));
READWRITE(witser);
}
}
}
READWRITE(*const_cast<uint32_t*>(&tx.nLockTime));
}

View file

@ -71,9 +71,9 @@ BOOST_AUTO_TEST_CASE(rpc_rawparams)
BOOST_CHECK_THROW(CallRPC("decoderawtransaction"), runtime_error);
BOOST_CHECK_THROW(CallRPC("decoderawtransaction null"), runtime_error);
BOOST_CHECK_THROW(CallRPC("decoderawtransaction DEADBEEF"), runtime_error);
string rawtx = "01000000000000000000000000000002000000000000000000000000000000000000000000000000000000000005f5e10000210336cf214cb1633268d90ef794165de68911661210cb82e61276639b10911e71361976a914261324748d65eaf0fa3e22b1bd79f2f487c9d98188ac00000000000000000000000000000000000000000000000000000000000bebc200002102248846a76f455da4b3d6ca78a021aabcca7e22e9abd5c9813fb27155dd2739871976a91457a463ab039b40e9457abaff84a9bdd39c9caf0088ac69000000";
string rawtx = "0100000000000000000000000132313029282726252423222120191817161514131211100908070605040302010000000000ffffffff010000000000000000000000000000000000000000000000000000000000000000000069000000";
BOOST_CHECK_NO_THROW(r = CallRPC(string("decoderawtransaction ")+rawtx));
BOOST_CHECK_EQUAL(find_value(r.get_obj(), "size").get_int(), 208);
BOOST_CHECK_EQUAL(find_value(r.get_obj(), "size").get_int(), 93);
BOOST_CHECK_EQUAL(find_value(r.get_obj(), "version").get_int(), 1);
BOOST_CHECK_EQUAL(find_value(r.get_obj(), "locktime").get_int(), 105);
BOOST_CHECK_THROW(r = CallRPC(string("decoderawtransaction ")+rawtx+" extra"), runtime_error);