285 lines
13 KiB
C++
285 lines
13 KiB
C++
/**
|
|
* @file tutorial.cpp
|
|
*
|
|
* @brief Simple tutorial program to illustrate Zerocoin usage.
|
|
*
|
|
* @author Ian Miers, Christina Garman and Matthew Green
|
|
* @date June 2013
|
|
*
|
|
* @copyright Copyright 2013 Ian Miers, Christina Garman and Matthew Green
|
|
* @license This project is released under the MIT license.
|
|
**/
|
|
// Copyright (c) 2017-2018 The PIVX developers
|
|
|
|
#include <boost/test/unit_test.hpp>
|
|
#include <string>
|
|
#include <iostream>
|
|
#include <fstream>
|
|
// #include <curses.h>
|
|
#include <exception>
|
|
#include "streams.h"
|
|
#include "libzerocoin/bignum.h"
|
|
#include "libzerocoin/ParamGeneration.h"
|
|
#include "libzerocoin/Denominations.h"
|
|
#include "libzerocoin/Coin.h"
|
|
#include "libzerocoin/CoinSpend.h"
|
|
#include "libzerocoin/Accumulator.h"
|
|
#include "test_agrarian.h"
|
|
|
|
using namespace std;
|
|
|
|
#define COINS_TO_ACCUMULATE 5
|
|
#define DUMMY_TRANSACTION_HASH 0 // in real life these would be uint256 hashes
|
|
#define DUMMY_ACCUMULATOR_ID 0 // in real life these would be uint256 hashes
|
|
|
|
//
|
|
// We generated this for testing only. Don't use it in production!
|
|
//
|
|
#define TUTORIAL_TEST_MODULUS "a8852ebf7c49f01cd196e35394f3b74dd86283a07f57e0a262928e7493d4a3961d93d93c90ea3369719641d626d28b9cddc6d9307b9aabdbffc40b6d6da2e329d079b4187ff784b2893d9f53e9ab913a04ff02668114695b07d8ce877c4c8cac1b12b9beff3c51294ebe349eca41c24cd32a6d09dd1579d3947e5c4dcc30b2090b0454edb98c6336e7571db09e0fdafbd68d8f0470223836e90666a5b143b73b9cd71547c917bf24c0efc86af2eba046ed781d9acb05c80f007ef5a0a5dfca23236f37e698e8728def12554bc80f294f71c040a88eff144d130b24211016a97ce0f5fe520f477e555c9997683d762aff8bd1402ae6938dd5c994780b1bf6aa7239e9d8101630ecfeaa730d2bbc97d39beb057f016db2e28bf12fab4989c0170c2593383fd04660b5229adcd8486ba78f6cc1b558bcd92f344100dff239a8c00dbc4c2825277f24bdd04475bcc9a8c39fd895eff97c1967e434effcb9bd394e0577f4cf98c30d9e6b54cd47d6e447dcf34d67e48e4421691dbe4a7d9bd503abb9"
|
|
|
|
//
|
|
// The following routine exercises most of the core functions of
|
|
// the library. We've commented it as well as possible to give
|
|
// you the flavor of how libzerocoin works.
|
|
//
|
|
// For details on Zerocoin integration, see the libzerocoin wiki
|
|
// at: https://github.com/Zerocoin/libzerocoin/wiki
|
|
//
|
|
|
|
bool
|
|
ZerocoinTutorial()
|
|
{
|
|
// The following simple code illustrates the call flow for Zerocoin
|
|
// applications. In a real currency network these operations would
|
|
// be split between individual payers/payees, network nodes and miners.
|
|
//
|
|
// For each call we specify the participant who would use it.
|
|
|
|
// Zerocoin uses exceptions (based on the runtime_error class)
|
|
// to indicate all sorts of problems. Always remember to catch them!
|
|
|
|
try {
|
|
|
|
/********************************************************************/
|
|
// What is it: Parameter loading
|
|
// Who does it: ALL ZEROCOIN PARTICIPANTS
|
|
// What it does: Loads a trusted Zerocoin modulus "N" and
|
|
// generates all associated parameters from it.
|
|
// We use a hardcoded "N" that we generated using
|
|
// the included 'paramgen' utility.
|
|
/********************************************************************/
|
|
|
|
// Load a test modulus from our hardcoded string (above)
|
|
CBigNum testModulus;
|
|
testModulus.SetHex(std::string(TUTORIAL_TEST_MODULUS));
|
|
|
|
// Set up the Zerocoin Params object
|
|
libzerocoin::ZerocoinParams* params = new libzerocoin::ZerocoinParams(testModulus);
|
|
|
|
cout << "Successfully loaded parameters." << endl;
|
|
|
|
/********************************************************************/
|
|
// What is it: Coin generation
|
|
// Who does it: ZEROCOIN CLIENTS
|
|
// What it does: Generates a new 'zerocoin' coin using the
|
|
// public parameters. Once generated, the client
|
|
// will transmit the public portion of this coin
|
|
// in a ZEROCOIN_MINT transaction. The inputs
|
|
// to this transaction must add up to the zerocoin
|
|
// denomination plus any transaction fees.
|
|
/********************************************************************/
|
|
|
|
// The following constructor does all the work of minting a brand
|
|
// new zerocoin. It stores all the private values inside the
|
|
// PrivateCoin object. This includes the coin secrets, which must be
|
|
// stored in a secure location (wallet) at the client.
|
|
libzerocoin::PrivateCoin newCoin(params, libzerocoin::CoinDenomination::ZQ_ONE);
|
|
|
|
// Get a copy of the 'public' portion of the coin. You should
|
|
// embed this into a Zerocoin 'MINT' transaction along with a series
|
|
// of currency inputs totaling the assigned value of one zerocoin.
|
|
libzerocoin::PublicCoin pubCoin = newCoin.getPublicCoin();
|
|
|
|
cout << "Successfully minted a zerocoin." << endl;
|
|
|
|
// Serialize the public coin to a CDataStream object.
|
|
CDataStream serializedCoin(SER_NETWORK, PROTOCOL_VERSION);
|
|
serializedCoin << pubCoin;
|
|
|
|
/********************************************************************/
|
|
// What is it: Coin verification
|
|
// Who does it: TRANSACTION VERIFIERS
|
|
// What it does: Verifies the structure of a zerocoin obtained from
|
|
// a ZEROCOIN_MINT transaction. All coins must be
|
|
// verified before you operate on them.
|
|
// Note that this is only part of the transaction
|
|
// verification process! The client must also check
|
|
// that (1) the inputs to the transaction are valid
|
|
// and add up to the value of one zerocoin, (2) that
|
|
// this particular zerocoin has not been minted before.
|
|
/********************************************************************/
|
|
|
|
// Deserialize the public coin into a fresh object.
|
|
libzerocoin::PublicCoin pubCoinNew(params, serializedCoin);
|
|
|
|
// Now make sure the coin is valid.
|
|
if (!pubCoinNew.validate()) {
|
|
// If this returns false, don't accept the coin for any purpose!
|
|
// Any ZEROCOIN_MINT with an invalid coin should NOT be
|
|
// accepted as a valid transaction in the block chain.
|
|
cout << "Error: coin is not valid!";
|
|
}
|
|
|
|
cout << "Deserialized and verified the coin." << endl;
|
|
|
|
/********************************************************************/
|
|
// What is it: Accumulator computation
|
|
// Who does it: ZEROCOIN CLIENTS & TRANSACTION VERIFIERS
|
|
// What it does: Collects a number of PublicCoin values drawn from
|
|
// the block chain and calculates an accumulator.
|
|
// This accumulator is incrementally computable;
|
|
// you can stop and serialize it at any point
|
|
// then continue accumulating new transactions.
|
|
// The accumulator is also order-independent, so
|
|
// the same coins can be accumulated in any order
|
|
// to give the same result.
|
|
// WARNING: do not accumulate the same coin twice!
|
|
/********************************************************************/
|
|
|
|
// Create an empty accumulator object
|
|
libzerocoin::Accumulator accumulator(params,libzerocoin::CoinDenomination::ZQ_ONE);
|
|
|
|
// Add several coins to it (we'll generate them here on the fly).
|
|
for (uint32_t i = 0; i < COINS_TO_ACCUMULATE; i++) {
|
|
libzerocoin::PrivateCoin testCoin(params, libzerocoin::CoinDenomination::ZQ_ONE);
|
|
accumulator += testCoin.getPublicCoin();
|
|
}
|
|
|
|
// Serialize the accumulator object.
|
|
//
|
|
// If you're using Accumulator Checkpoints, each miner would
|
|
// start by deserializing the accumulator checkpoint from the
|
|
// previous block (or creating a new Accumulator if no previous
|
|
// block exists). It will then add all the coins in the new block,
|
|
// then serialize the resulting Accumulator object to obtain the
|
|
// new checkpoint. All block verifiers should do the same thing
|
|
// to check their work.
|
|
CDataStream serializedAccumulator(SER_NETWORK, PROTOCOL_VERSION);
|
|
serializedAccumulator << accumulator;
|
|
|
|
// Deserialize the accumulator object
|
|
libzerocoin::Accumulator newAccumulator(params, serializedAccumulator);
|
|
|
|
// We can now continue accumulating things into the accumulator
|
|
// we just deserialized. For example, let's put in the coin
|
|
// we generated up above.
|
|
newAccumulator += pubCoinNew;
|
|
|
|
cout << "Successfully accumulated coins." << endl;
|
|
|
|
/********************************************************************/
|
|
// What is it: Coin spend
|
|
// Who does it: ZEROCOIN CLIENTS
|
|
// What it does: Create a new transaction that spends a Zerocoin.
|
|
// The user first authors a transaction specifying
|
|
// a set of destination addresses (outputs). They
|
|
// next compute an Accumulator over all coins in the
|
|
// block chain (see above) and a Witness based on the
|
|
// coin to be spent. Finally they instantiate a CoinSpend
|
|
// object that 'signs' the transaction with a special
|
|
// zero knowledge signature of knowledge over the coin
|
|
// data, Witness and Accumulator.
|
|
/********************************************************************/
|
|
|
|
// We are going to spend the coin "newCoin" that we generated at the
|
|
// top of this function.
|
|
//
|
|
// We'll use the accumulator we constructed above. This contains
|
|
// a set of coins, but does NOT include the coin "newCoin".
|
|
//
|
|
// To generate the witness, we start with this accumulator and
|
|
// add the public half of the coin we want to spend.
|
|
libzerocoin::AccumulatorWitness witness(params, accumulator, newCoin.getPublicCoin());
|
|
|
|
// Add the public half of "newCoin" to the Accumulator itself.
|
|
accumulator += newCoin.getPublicCoin();
|
|
|
|
// At this point we should generate a ZEROCOIN_SPEND transaction to
|
|
// send to the network. This network should include a set of outputs
|
|
// totalling to the value of one zerocoin (minus transaction fees).
|
|
|
|
// Construct the CoinSpend object. This acts like a signature on the
|
|
// transaction.
|
|
libzerocoin::CoinSpend spend(params, params, newCoin, accumulator, 0, witness, 0, libzerocoin::SpendType::SPEND);//(0) - Presstab
|
|
|
|
// This is a sanity check. The CoinSpend object should always verify,
|
|
// but why not check before we put it onto the wire?
|
|
if (!spend.Verify(accumulator)) {
|
|
cout << "ERROR: Our new CoinSpend transaction did not verify!" << endl;
|
|
return false;
|
|
}
|
|
|
|
// Serialize the CoinSpend object into a buffer.
|
|
CDataStream serializedCoinSpend(SER_NETWORK, PROTOCOL_VERSION);
|
|
serializedCoinSpend << spend;
|
|
|
|
cout << "Successfully generated a coin spend transaction." << endl;
|
|
|
|
/********************************************************************/
|
|
// What is it: Coin spend verification
|
|
// Who does it: ALL PARTIES
|
|
// What it does: Verifies that a CoinSpend signature is correct
|
|
// with respect to a ZEROCOIN_SPEND transaction hash.
|
|
// The client must also extract the serial number from
|
|
// the CoinSpend and verify that this serial number has
|
|
// not previously appeared in another ZEROCOIN_SPEND
|
|
// transaction.
|
|
/********************************************************************/
|
|
|
|
// Deserialize the CoinSpend intro a fresh object
|
|
libzerocoin::CoinSpend newSpend(params, params, serializedCoinSpend);
|
|
|
|
// Create a new metadata object to contain the hash of the received
|
|
// ZEROCOIN_SPEND transaction. If we were a real client we'd actually
|
|
// compute the hash of the received transaction here.
|
|
|
|
// If we were a real client we would now re-compute the Accumulator
|
|
// from the information given in the ZEROCOIN_SPEND transaction.
|
|
// For our purposes we'll just use the one we calculated above.
|
|
//
|
|
// Verify that the spend is valid with respect to the Accumulator
|
|
// and the Metadata
|
|
if (!newSpend.Verify(accumulator)) {
|
|
cout << "ERROR: The CoinSpend transaction did not verify!" << endl;
|
|
return false;
|
|
}
|
|
|
|
// Pull the serial number out of the CoinSpend object. If we
|
|
// were a real Zerocoin client we would now check that the serial number
|
|
// has not been spent before (in another ZEROCOIN_SPEND) transaction.
|
|
// The serial number is stored as a CBigNum.
|
|
CBigNum serialNumber = newSpend.getCoinSerialNumber();
|
|
|
|
cout << "Successfully verified a coin spend transaction." << endl;
|
|
cout << endl << "Coin serial number is:" << endl << serialNumber << endl;
|
|
|
|
// We're done
|
|
return true;
|
|
|
|
} catch (runtime_error &e) {
|
|
cout << e.what() << endl;
|
|
return false;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
BOOST_FIXTURE_TEST_SUITE(tutorial_libzerocoin, TestingSetup)
|
|
BOOST_AUTO_TEST_CASE(tutorial_libzerocoin_tests)
|
|
{
|
|
cout << "libzerocoin v" << ZEROCOIN_VERSION_STRING << " tutorial." << endl << endl;
|
|
|
|
ZerocoinTutorial();
|
|
}
|
|
BOOST_AUTO_TEST_SUITE_END()
|