/**
* @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()
