示例#1
0
int main (int argc, char * const argv[])
{
	if (argc < 4)
	{
		printf("\n\nUsage:  createmint  server_id  server_user_id  asset_type_id \n\n"
			"For now, expiration dates are automatically set:\n"
			"-- FROM: Today, Now.\n"
			"-- TO: 6 months from now.\n\n"
			"It is recommended that you issue a new series (create a new mint) every 3 months for each\n"
			"asset type, so that there is a 3-month overlap. In the future, this will be configured inside\n"
			"the contracts themselves.\n\n"
			"Server ID needs to match the Server ID from notaryServer.xml\n"
			"Server user ID needs to match the Server User ID from notaryServer.xml\n"
			"Asset Type ID needs to match the Asset ID (aka Hashed Contract ID) of the currency contract.\n\n"
			//			   "The path to the data folder is necessary for initialization of the OT library.\n\n"
			//             "(You probably want to use ~/.ot/server_data as your data folder.)\n\n"
			);
		return 0;
	}
	// **************************************************************************

	class __OTcreatemint_RAII
	{
	public:
		__OTcreatemint_RAII()
		{
			if(!OTLog::Init(SERVER_CONFIG_KEY,0)) { assert(false); };  // setup the logger.

			OTLog::vOutput(0, "\n\nWelcome to Open Transactions -- 'createmint', version %s\n", 
				OTLog::Version());
			// ------------------------------------
#ifdef _WIN32
			WSADATA wsaData;
			WORD wVersionRequested = MAKEWORD( 2, 2 );
			int err = WSAStartup( wVersionRequested, &wsaData );

			/* Tell the user that we could not find a usable		*/
			/* Winsock DLL.											*/		

			OT_ASSERT_MSG((err == 0), "WSAStartup failed!\n");


			/*	Confirm that the WinSock DLL supports 2.2.			*/
			/*	Note that if the DLL supports versions greater		*/
			/*	than 2.2 in addition to 2.2, it will still return	*/
			/*	2.2 in wVersion since that is the version we		*/
			/*	requested.											*/

			bool bWinsock = (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2);

			/* Tell the user that we could not find a usable */
			/* WinSock DLL.                                  */

			if (!bWinsock) WSACleanup();  // do cleanup.
			OT_ASSERT_MSG((!bWinsock), "Could not find a usable version of Winsock.dll\n");

			/* The Winsock DLL is acceptable. Proceed to use it. */
			/* Add network programming using Winsock here */
			/* then call WSACleanup when done using the Winsock dll */
			OTLog::vOutput(0,"The Winsock 2.2 dll was found okay\n");
#endif
			// ------------------------------------
			// SIGNALS
			//
#if defined(OT_SIGNAL_HANDLING)
			//
			OTLog::SetupSignalHandler();  // <===== SIGNALS
			//
			// This is optional! You can always remove it using the OT_NO_SIGNAL_HANDLING
			//  option, and plus, the internals only execute once anyway. (It keeps count.)
#endif
			// ------------------------------------

			//
			// OT Server Path:
			//
			{
				bool bSetupPathsSuccess = false;
				if(!OTDataFolder::Init(SERVER_CONFIG_KEY)) { OT_ASSERT(false); }
				else
					bSetupPathsSuccess = true;

				OT_ASSERT_MSG(bSetupPathsSuccess, "main(): Assert failed: Failed to set OT Path");
			}

			// -----------------------------------------------------------------------    

			OTCrypto::It()->Init(); // (OpenSSL gets initialized here.)

			// ------------------------------------
		}
		~__OTcreatemint_RAII()
		{
			// We clean these up in reverse order from the Init function, which just seems
			// like the best default, in absence of any brighter ideas.
			//
			OTCrypto::It()->Cleanup();  // (OpenSSL gets cleaned up here.)

			// ------------------------------------
#ifdef _WIN32
			WSACleanup(); // Corresponds to WSAStartup() in InitOTAPI().
#endif
			// ------------------------------------
		}
	};
	// --------------------------------------------

	__OTcreatemint_RAII   the_createmint_cleanup;

	// **************************************************************************

	OTServer theServer;

	// -----------------------------------------

	int nReturnVal = 0;

	OTString		strServerID(argv[1]),
		strServerNymID(argv[2]),
		strAssetTypeID(argv[3]);

	OTIdentifier	ASSET_TYPE_ID(strAssetTypeID),
		SERVER_ID(strServerID);

	//	std::string strDataFolderPath(argv[4]), strNotaryFile("notaryServer.xml");

	OTLog::vOutput(0, 
		"\nNow loading the server nym, which will also ask you for a password, to unlock\n"
		"its private key. (Default password is \"%s\".)\n", KEY_PASSWORD);

	// Keys, etc are loaded here. ===> Assumes main path is set! <===
	//
	theServer.Init(true); //bool bReadOnly=false by default (We don't want to overwrite anything if the ACTUAL server is running...)


	//  NOTE: I copied some code in here from xmlrpcxx_server.cpp, including the above
	//  call to theServer.Init(), which has the InitDefaultStorage inside of it, and
	//  which uses OTLog::Path(), which the above new code should be setting properly
	//  before theServer.Init() actually gets called. (So this should work....)
	//  
	//	bool bSuccessInitDefault = OTDB::InitDefaultStorage(OTDB_DEFAULT_STORAGE, 
	//														OTDB_DEFAULT_PACKER, strDataFolderPath, strNotaryFile);
	//    if (!bSuccessInitDefault)
	//    {
	//        OTLog::vError("\n\n%s: Failed invoking OTDB::InitDefaultStorage with path: %s and main filename: %s\n\n",
	//                      __FUNCTION__, strDataFolderPath.c_str(), strNotaryFile.c_str());
	//        return 0;
	//    }
	// -----------------------------------------

	OTString strMintPath;
	bool bFileIsPresent = false;		
	int nSeries = 0;

	for (nSeries = 0; nSeries < 10000; ++nSeries)
	{
//		struct stat st;

		OTString strFilename;
		strFilename.Format("%s%s%d", strAssetTypeID.Get(), ".", nSeries);

		bFileIsPresent = OTDB::Exists(OTFolders::Mint().Get(),
                                      strServerID.Get(),
                                      strFilename.Get());

		// Old Code
		//strMintPath.Format("%s%s%s%s%s%s%s%s%d", 
		//	OTLog::Path(), 
		//	OTLog::PathSeparator(),
		//	OTFolders::Mint().Get(),
		//	OTLog::PathSeparator(),
		//	strServerID.Get(),
		//	OTLog::PathSeparator(),
		//	strAssetTypeID.Get(), ".", nSeries);
		//bFileIsPresent = (stat(strMintPath.Get(), &st) == 0);

		if (!bFileIsPresent)
			break;
	}

	// if bFileIsPresent is STILL true, that means we got all the way up to 1000 and the 
	// file was present every time.
	// Geez, there must be 10000 mints on this computer.  At one new Mint per 3 months,
	// that's 4 per year, that's 2500 years already!!
	//
	if (bFileIsPresent)
	{
		OTLog::Output(0, "\n\nThis program automatically finds the next series, up to 10000. You\n"
			"have reached 10000. You will have to change the source code of this\n"
			"program in order to continue. Sorry.\n\n");
		return 0;
	}
	// ---------------------------------------------------------------------

	// nSeries now contains the number we need to use for the next series.
	// and strMintPath now contains the correct file path.

	OTMint * pMint = new OTMint(strServerID, strServerNymID, strAssetTypeID);
	OT_ASSERT(NULL != pMint);

	OTString strSeries; strSeries.Format("%s%d", ".", nSeries);

	if (pMint->LoadMint(strSeries.Get()))
	{
		OTLog::Output(0, "\n\nSorry, that mint already exists. Delete it first if you wish to re-create it.\n\n");
	}
	else 
	{
		OTLog::vOutput(0, "\n\nMint file does not (yet) exist for series %d and asset type:\n%s\n Creating......\n\n", 
			nSeries, strAssetTypeID.Get());

		// TODO: read the denominations out of the asset contract itself, instead of hardcoding them here.


		// Calculate FROM as Today, Now, 
		// then calculate TO as 6 months from now, 
		// and EXPIRATION as 3 months from now.
		//
		// TODO: Let these numbers be configured either in server operator contract, or issuer contract.
		// In the meantime, 3 and 6 months are good enough.

		OTPseudonym theNym;
		theNym.SetIdentifier(strServerNymID);

		// 1 hour	==     3600 Seconds
		// 1 day	==    86400 Seconds
		// 30 days	==  2592000 Seconds
		// 3 months ==  7776000 Seconds
		// 6 months == 15552000 Seconds


		// This part requires the server operator to enter his passphrase.
		// Which is why the server can't just fire it off automatically and
		// make a mint available to the client.  The client has to wait a day or
		// until the operator is able to run this script and type the passphrase.
		if (theNym.Loadx509CertAndPrivateKey())
		{								
			const time_t	CURRENT_TIME	= time(NULL), 
				VALID_TO		= CURRENT_TIME + 15552000,	// Tokens generated by this mint are valid from today until 6 months from today
				MINT_EXPIRATION	= CURRENT_TIME + 7776000;	// The mint itself will expire in 3 months from today, and be replaced with a new one.

			pMint->GenerateNewMint(nSeries, CURRENT_TIME, VALID_TO, MINT_EXPIRATION, ASSET_TYPE_ID, SERVER_ID,
				// penny, nickel, dime, quarter, $1, $5, $10, $20, $100, $1000
				theNym, 1, 5, 10, 25, 100, 500, 1000, 2000, 10000, 100000);
			// should be: 1, 2, 4,   8,  16,  32,   64,  128,   256,    512, 1024, 2048, 4096, 8192, 16384, 32768, 65536

			// ---------------------------------------

			//			OTString strFilename;// strPUBLICFilename;		
			//			strFilename.		Format("%s%s%s",		strServerID.Get(), OTLog::PathSeparator(), strAssetTypeID.Get());
			//			strPUBLICFilename.	Format("%s%s%s%sPUBLIC",strServerID.Get(), OTLog::PathSeparator(), strAssetTypeID.Get(), ".");

			if (!OTDataFolder::IsInitialized()) { OT_ASSERT(false); };

			OTString strServerFolder(""), strMintFolder("");

			if (!OTPaths::AppendFolder(strMintFolder,	OTDataFolder::Get(),OTFolders::Mint())) { OT_ASSERT(false); }; // mint/
			if (!OTPaths::AppendFolder(strServerFolder,	strMintFolder,		strServerID.Get())) { OT_ASSERT(false); }; // mint/serverID

			bool bFolderCreated;
			if (OTPaths::BuildFolderPath(strServerFolder,bFolderCreated))
			{
				// -------------------------------------------------------------------
				// This causes the next serialization to save the private, not just public, keys.
				pMint->SetSavePrivateKeys(); // <== PRIVATE MINT VERSION.

				pMint->SignContract(theNym);
				pMint->SaveContract();

				pMint->SaveMint();		// save the private mint file as: path/mints/server_id/Asset_TypeID (overwriting the last "current mint"...)
				pMint->SaveMint(strSeries.Get()); // save the private mint file as: path/mints/server_id/Asset_TypeID.nSeries (These accumulate.)

				// When the server needs to reference the "current" private keys, then it just loads the
				// mint without specifying the series number. Since this file is overwritten with each new 
				// mint, it is thus always the latest one.  (Similarly, the latest public is always asset_id.PUBLIC)
				// On the other hand, if I need to load the keys for a specific series, (since more than one may be
				// redeemable, even if only the latest one is being issued) then they are all also saved by series
				// number, which is not overwritten with each new mint, but accumulates.
				// That is why above, you see me save the mint twice in two different files, and below you see
				// it being saved with the .PUBLIC appending to the filename.

				//				pMint->SaveContract(OTFolders::Mint().Get(), strFilename.Get());  // save the mint file.

				// -------------------------------------------------------------------
				// Now I sign it again, to get the private keys out of there.
				pMint->ReleaseSignatures();
				pMint->SignContract(theNym);
				pMint->SaveContract();

				pMint->SaveMint(".PUBLIC");  // save the public mint file.
				//				pMint->SaveContract(OTFolders::Mint().Get(), strPUBLICFilename.Get());  // save the public mint file.

				nReturnVal = 1;

				OTLog::Output(0, "\nDone.\n\n");
			}
			else
				OTLog::Output(0, "\n\nError calling OTLog::ConfirmOrCreateFolder() for path/mints/server_id\n\n");
		}
		else
			OTLog::Output(0, "\n\nError calling theNym.Loadx509CertAndPrivateKey()\n\n");
	} // Mint file doesn't exist yet (therefore the above block creates it...)

	return nReturnVal;
}
示例#2
0
int main (int argc, char * const argv[])
{
	if (argc < 3)
	{
		printf("Usage:  createmint server_user_id asset_type_id\n\n"
			   "For now, expiration dates are automatically set:\n"
			   "FROM: Today, Now.\n"
			   "TO: 6 months from now.\n\n"
			   "It is recommended that you issue a new series (create a new mint) every 3 months for each\n"
			   "asset type, so that there is a 3-month overlap. In the future, this will be configured inside\n"
			   "the contracts themselves.\n\n"
			   "Server user ID needs to match the Server User ID from notaryServer.xml\n"
			   "Asset Type ID needs to match the Asset ID (aka Hashed Contract ID) of the currency contract.\n\n");
		exit(1);
	}
	
	SSL_library_init();
	SSL_load_error_strings();

	OTString strServerID(argv[1]), strAssetTypeID(argv[2]);
	OTIdentifier ASSET_TYPE_ID(strAssetTypeID);
	
	
	OTString strMintPath;
	bool bFileIsPresent = false;		
	int nSeries = 0;
	
	for (nSeries = 0; nSeries < 1000; nSeries++)
	{
		struct stat st;

		strMintPath.Format("%s%smints%s%s.%d", 
									 OTLog::Path(), OTLog::PathSeparator(),
									 OTLog::PathSeparator(),
									 strAssetTypeID.Get(), nSeries);
		
		bFileIsPresent = (stat(strMintPath.Get(), &st) == 0);
		
		if (!bFileIsPresent)
			break;
	}
	
	// if bFileIsPresent is STILL true, that means we got all the way up to 1000 and the 
	// file was present every time.
	// Geez, there must be 1000 mints on this computer.  At one new Mint per 3 months,
	// that's 4 per year, that's 250 years already!!
	if (bFileIsPresent)
	{
		fprintf(stdout, "This program automatically finds the next series, up to 1000. You\n"
				"have reached 1000. You will have to change the source code of this\n"
				"program in order to continue. Sorry.\n");
		exit(1);
	}
	
	
	// nSeries now contains the number we need to use for the next series.
	// and strMintPath now contains the correct file path.
	
	OTMint * pMint = new OTMint(strAssetTypeID, strMintPath, strAssetTypeID);
	
	if (pMint && pMint->LoadContract())
	{
		printf("The mint already exists. Delete it first if you wish to re-create it.\n");
	}
	else 
	{
		fprintf(stderr, "Mint file does not yet exist, for series %d and asset type:\n%s\nCreating......\n", 
				nSeries, strAssetTypeID.Get());
		
		if (pMint)
		{	
			// TODO: read the denominations out of the asset contract itself, instead of hardcoding them here.
			
			
			// Calculate FROM as Today, Now, 
			// then calculate TO as 6 months from now, 
			// and EXPIRATION as 3 months from now.
			//
			// TODO: Let these numbers be configured either in server operator contract, or issuer contract.
			// In the meantime, 3 and 6 months are good enough.
			
			OTPseudonym theNym;
			theNym.SetIdentifier(strServerID);
			
			// 1 hour	==     3600 Seconds
			// 1 day	==    86400 Seconds
			// 30 days	==  2592000 Seconds
			// 3 months ==  7776000 Seconds
			// 6 months == 15552000 Seconds
			
			
			// This part requires the server operator to enter his passphrase.
			// Which is why the server can't just fire it off automatically and
			// make a mint available to the client.  The client has to wait a day or
			// until the operator is able to run this script and type the passphrase.
			if (theNym.Loadx509CertAndPrivateKey())
			{								
				const time_t	CURRENT_TIME	= time(NULL), 
								VALID_TO		= CURRENT_TIME + 15552000,	// Tokens generated by this mint are valid from today until 6 months from today
								MINT_EXPIRATION	= CURRENT_TIME + 7776000;	// The mint itself will expire in 3 months from today, and be replaced with a new one.
				
				pMint->GenerateNewMint(nSeries, CURRENT_TIME, VALID_TO, MINT_EXPIRATION,
														// penny, nickel, dime, quarter, $1, $5, $10, $20, $100, $1000
									   ASSET_TYPE_ID, theNym, 1, 5, 10, 25, 100, 500, 1000, 2000, 10000, 100000);
												// should be: 1, 2, 4,   8,  16,  32,   64,  128,   256,    512, 1024, 2048, 4096, 8192, 16384, 32768, 65536
				
				pMint->SetSavePrivateKeys(); // This causes the next serialization to save the private, not just public, keys.
				pMint->SignContract(theNym);
				pMint->SaveContract(strMintPath.Get());  // save the mint file.
				
				// Now I sign it again, to get the private keys out of there.
				pMint->ReleaseSignatures();
				pMint->SignContract(theNym);
				strMintPath.Format("%s%smints%s%s.PUBLIC", OTLog::Path(), OTLog::PathSeparator(),
								   OTLog::PathSeparator(), strAssetTypeID.Get());
				pMint->SaveContract(strMintPath.Get());
				printf("Done.\n");
			}
			else {
				printf("Error calling theNym.Loadx509CertAndPrivateKey()\n");
			}
		}
		else 
		{
			fprintf(stdout, "Error allocating memory for new OTMint.\n");
		}
	}
	
	return 0;
}
示例#3
0
int main (int argc, char **argv) 
{
	OTLog::vOutput(0, "\n\nWelcome to Open Transactions... Test Client -- version %s\n"
				   "(transport build: OTMessage -> TCP -> SSL)\n"
				   "IF YOU PREFER TO USE ZMQ (message based), then rebuild from main folder like this:\n"
				   "cd ..; make clean; make\n\n", 
				   OTLog::Version());

	OT_API::InitOTAPI();

	// -----------------------------------------------------------------------
	// The beginnings of an INI file!!

	OTString strPath;

	{
		CSimpleIniA ini; // We're assuming this file is on the path.
		SI_Error rc = ini.LoadFile("./.ot_ini"); // todo: stop hardcoding. 

		if (rc >=0)
		{
			const char * pVal = ini.GetValue("paths", "client_path", SERVER_PATH_DEFAULT); // todo stop hardcoding.

			if (NULL != pVal)
				strPath.Set(pVal);
			else
				strPath.Set(SERVER_PATH_DEFAULT);
		}
		else 
		{
			strPath.Set(SERVER_PATH_DEFAULT);
		}
	}
	// -----------------------------------------------------------------------

	OTString strCAFile, strKeyFile, strSSLPassword;

	if (argc < 2)
	{
		OTLog::vOutput(0, "\n==> USAGE:    %s   <SSL-password>  <absolute_path_to_data_folder>\n\n"
#if defined (FELLOW_TRAVELER)					   
				"(Password defaults to '%s' if left blank.)\n"
				"(Folder defaults to '%s' if left blank.)\n"
#else
				"(The test password is always 'test'.\n'cd data_folder' then 'pwd' to see the absolute path.)\n"
#endif
					   "\n\n", argv[0]
#if defined (FELLOW_TRAVELER)					   
					   , KEY_PASSWORD, 
					   strPath.Get()
#endif					   
					   );
	
#if defined (FELLOW_TRAVELER)
		strSSLPassword.Set(KEY_PASSWORD);

		OTString strClientPath(strPath.Get());
        g_OT_API.Init(strClientPath);  // SSL gets initialized in here, before any keys are loaded.
#else
		exit(1);
#endif
	}
	else if (argc < 3)
	{
		OTLog::vOutput(0, "\n==> USAGE:    %s   <SSL-password>  <absolute_path_to_data_folder>\n\n"
#if defined (FELLOW_TRAVELER)					      
				"(Folder defaults to '%s' if left blank.)\n"
#endif
					   "\n\n", argv[0]
#if defined (FELLOW_TRAVELER)
					   , strPath.Get()
#endif
					   );
		
#if defined (FELLOW_TRAVELER)					   
		strSSLPassword.Set(argv[1]);

		OTString strClientPath(strPath.Get());
        g_OT_API.Init(strClientPath);  // SSL gets initialized in here, before any keys are loaded.
#else
		exit(1);
#endif
	}
	else 
	{
		strSSLPassword.Set(argv[1]);

		OTString strClientPath(argv[2]);
        g_OT_API.Init(strClientPath);  // SSL gets initialized in here, before any keys are loaded.
	}	

	OTLog::vOutput::(0, "Using as path to data folder:  %s\n", OTLog::Path());

	strCAFile. Format("%s%s%s", OTLog::Path(), OTLog::PathSeparator(), CA_FILE);
	strKeyFile.Format("%s%s%s", OTLog::Path(), OTLog::PathSeparator(), KEY_FILE);


	// ------------------------------------------------------------------------------

	//
	// Basically, loop:
	//
	//	1) Present a prompt, and get a user string of input. Wait for that.
	//
	//	2) Process it out as an OTMessage to the server. It goes down the pipe.
	//
	//	3) Sleep for 1 second.
	//
	//	4) Awake and check for messages to be read in response from the server.
	//	   Loop. As long as there are any responses there, then process and handle
	//	   them all.
	//	   Then continue back up to the prompt at step (1).

    char buf[200] = "";
 	int retVal = 0;

	int nExpectResponse = 0;

	OTLog::Output(0, "You may wish to 'load' then 'connect' then 'stat'.\n");
	OTLog::vOutput(4, "Starting client loop. u_header size in C code is %d.\n", OT_CMD_HEADER_SIZE);


	for(;;)
	{
		buf[0] = 0; // Making it fresh again.

		nExpectResponse = 0;

		// 1) Present a prompt, and get a user string of input. Wait for that.
		OTLog::Output(0, "\nWallet> ");

		if (NULL == fgets(buf, sizeof(buf)-10, stdin)) // Leaving myself 10 extra bytes at the end for safety's sake.
            break;

		OTLog::Output(0, ".\n..\n...\n....\n.....\n......\n.......\n........\n.........\n..........\n"
				"...........\n............\n.............\n");

		// so we can process the user input
		std::string strLine = buf;

		// 1.5 The one command that doesn't involve a message to the server (so far)
		//     is the command to load the wallet from disk (which we do before we can
		//     do anything else.)  That and maybe the message to CONNECT to the server.
		// Load wallet.xml
		if (strLine.compare(0,4,"load") == 0)
		{
			OTLog::Output(0, "User has instructed to load wallet.xml...\n");
			g_OT_API.GetWallet()->LoadWallet("wallet.xml");
 
//			g_OT_API.GetWallet()->SaveWallet("NEWwallet.xml"); // todo remove this test code.

			continue;
		}

		else if (strLine.compare(0,5,"clear") == 0)
		{
			if (NULL == g_pTemporaryNym)
			{
				OTLog::Output(0, "No Nym yet available. Try 'load'.\n");
				continue;
			}

			g_pTemporaryNym->RemoveAllNumbers();

			g_pTemporaryNym->SaveSignedNymfile(*g_pTemporaryNym);

			OTLog::Output(0, "Successfully removed all issued and transaction numbers. Saving nym...\n");

			continue;
		}			

		else if (strLine.compare(0,7,"payment") == 0)
		{
			if (NULL == g_pTemporaryNym)
			{
				OTLog::Output(0, "No Nym yet available to sign the payment plan with. Try 'load'.\n");
				continue;
			}

			OTLog::Output(0, "Enter your Asset Account ID that the payments will come from: ");
			OTString strTemp;
			strTemp.OTfgets(std::cin);

			const OTIdentifier ACCOUNT_ID(strTemp), USER_ID(*g_pTemporaryNym);
			OTAccount * pAccount = g_OT_API.GetWallet()->GetAccount(ACCOUNT_ID);

			if (NULL == pAccount)
			{
				OTLog::Output(0, "That account isn't loaded right now. Try 'load'.\n");
				continue;
			}

			// To write a payment plan, like a cheque, we need to burn one of our transaction numbers. (Presumably
			// the wallet is also storing a couple of these, since they are needed to perform any transaction.)
			//
			// I don't have to contact the server to write a payment plan -- as long as I already have a transaction
			// number I can use to write it. Otherwise I'd have to ask the server to send me one first.
			OTString strServerID(pAccount->GetRealServerID());
			long lTransactionNumber=0;

			if (false == g_pTemporaryNym->GetNextTransactionNum(*g_pTemporaryNym, strServerID, lTransactionNumber))
			{
				OTLog::Output(0, "Payment Plans are written offline, but you still need a transaction number\n"
							  "(and you have none, currently.) Try using 'n' to request another transaction number.\n");
				continue;
			}

			// -----------------------------------------------------------------------

			OTString str_RECIPIENT_USER_ID, str_RECIPIENT_ACCT_ID, strConsideration;

			// Get the Recipient Nym ID
			OTLog::Output(0, "Enter the Recipient's User ID (NymID): ");
			str_RECIPIENT_USER_ID.OTfgets(std::cin);		


			// THEN GET AN ACCOUNT ID in that same asset type
			OTLog::Output(0, "Enter the Recipient's ACCOUNT ID (of the same asset type as your account): ");
			str_RECIPIENT_ACCT_ID.OTfgets(std::cin);		

			OTLog::Output(0, "Enter a memo describing consideration for the payment plan: ");
			strConsideration.OTfgets(std::cin);		

			const OTIdentifier	RECIPIENT_USER_ID(str_RECIPIENT_USER_ID), 
								RECIPIENT_ACCT_ID(str_RECIPIENT_ACCT_ID);


			OTPaymentPlan thePlan(pAccount->GetRealServerID(), pAccount->GetAssetTypeID(),
								  pAccount->GetRealAccountID(),	pAccount->GetUserID(),
								  RECIPIENT_ACCT_ID, RECIPIENT_USER_ID);

			// -----------------------------------------------------------------------

			// Valid date range (in seconds)
			OTLog::Output(0, 
						  " 6 minutes	==      360 Seconds\n"
						  "10 minutes	==      600 Seconds\n"
						  "1 hour		==     3600 Seconds\n"
						  "1 day		==    86400 Seconds\n"
						  "30 days			==  2592000 Seconds\n"
						  "3 months		==  7776000 Seconds\n"
						  "6 months		== 15552000 Seconds\n\n"
						  );

			long lExpirationInSeconds = 86400;
			OTLog::vOutput(0, "How many seconds before payment plan expires? (defaults to 1 day: %ld): ", lExpirationInSeconds);
			strTemp.Release();
			strTemp.OTfgets(std::cin);

			if (strTemp.GetLength() > 1)
				lExpirationInSeconds = atol(strTemp.Get());


			// -----------------------------------------------------------------------

			time_t	VALID_FROM	= time(NULL); // This time is set to TODAY NOW

			OTLog::vOutput(0, "Payment plan becomes valid for processing STARTING date\n"
						   "(defaults to now, in seconds) [%ld]: ", VALID_FROM);
			strTemp.Release();
			strTemp.OTfgets(std::cin);

			if (strTemp.GetLength() > 2)
				VALID_FROM = atol(strTemp.Get());

			const time_t VALID_TO = VALID_FROM + lExpirationInSeconds; // now + 86400

			// -----------------------------------------------------------------------

			bool bSuccessSetAgreement = thePlan.SetAgreement(lTransactionNumber, strConsideration, VALID_FROM,	VALID_TO);

			if (!bSuccessSetAgreement)
			{
				OTLog::Output(0, "Failed trying to set the agreement!\n");

				// IF FAILED, ADD TRANSACTION NUMBER BACK TO LIST OF AVAILABLE NUMBERS.
				g_pTemporaryNym->AddTransactionNum(*g_pTemporaryNym, strServerID, lTransactionNumber, true); // bSave=true								

				continue;
			}

			bool bSuccessSetInitialPayment	= true; // the default, in case user chooses not to even have this payment.
			bool bSuccessSetPaymentPlan		= true; // the default, in case user chooses not to have a payment plan
			// -----------------------------------------------------------------------

			OTLog::Output(0, "What is the Initial Payment Amount, if any? [0]: ");
			strTemp.Release(); strTemp.OTfgets(std::cin);
			long lInitialPayment = atol(strTemp.Get());

			if (lInitialPayment > 0)
			{
				time_t	PAYMENT_DELAY = 60; // 60 seconds.

				OTLog::vOutput(0, "From the Start Date forward, how long until the Initial Payment should charge?\n"
							   "(defaults to one minute, in seconds) [%d]: ", PAYMENT_DELAY);
				strTemp.Release();
				strTemp.OTfgets(std::cin);

				if ((strTemp.GetLength() > 1) && atol(strTemp.Get())>0)
					PAYMENT_DELAY = atol(strTemp.Get());

				// -----------------------------------------------------------------------

				 bSuccessSetInitialPayment = thePlan.SetInitialPayment(lInitialPayment, PAYMENT_DELAY);
			}

			if (!bSuccessSetInitialPayment)
			{
				OTLog::Output(0, "Failed trying to set the initial payment!\n");

				// IF FAILED, ADD TRANSACTION NUMBER BACK TO LIST OF AVAILABLE NUMBERS.
				g_pTemporaryNym->AddTransactionNum(*g_pTemporaryNym, strServerID, lTransactionNumber, true); // bSave=true								

				continue;
			}

			// -----------------------------------------------------------------------

			OTLog::Output(0, "What is the regular payment amount, if any? [0]: ");
			strTemp.Release(); strTemp.OTfgets(std::cin);
			long lRegularPayment = atol(strTemp.Get());

			if (lRegularPayment > 0) // If there are regular payments.
			{
				// -----------------------------------------------------------------------

				time_t	PAYMENT_DELAY = 120; // 120 seconds.

				OTLog::vOutput(0, "From the Start Date forward, how long until the Regular Payments start?\n"
							   "(defaults to two minutes, in seconds) [%d]: ", PAYMENT_DELAY);
				strTemp.Release();
				strTemp.OTfgets(std::cin);

				if ((strTemp.GetLength() > 1) && atol(strTemp.Get())>0)
					PAYMENT_DELAY = atol(strTemp.Get());

				// -----------------------------------------------------------------------

				time_t	PAYMENT_PERIOD = 30; // 30 seconds.

				OTLog::vOutput(0, "Once payments begin, how much time should elapse between each payment?\n"
							   "(defaults to thirty seconds) [%d]: ", PAYMENT_PERIOD);
				strTemp.Release();
				strTemp.OTfgets(std::cin);

				if ((strTemp.GetLength() > 1) && atol(strTemp.Get())>0)
					PAYMENT_PERIOD = atol(strTemp.Get());

				// -----------------------------------------------------------------------

				time_t	PLAN_LENGTH = 0; // 0 seconds (for no max length).

				OTLog::vOutput(0, "From start date, do you want the plan to expire after a certain maximum time?\n"
							   "(defaults to 0 for no) [%d]: ", PLAN_LENGTH);
				strTemp.Release();
				strTemp.OTfgets(std::cin);

				if (strTemp.GetLength() > 1)
					PLAN_LENGTH = atol(strTemp.Get());

				// -----------------------------------------------------------------------

				OTLog::Output(0, "Should there be some maximum number of payments? (Zero for no maximum.) [0]: ");
				strTemp.Release(); strTemp.OTfgets(std::cin);
				int nMaxPayments = atoi(strTemp.Get());

				bSuccessSetPaymentPlan = thePlan.SetPaymentPlan(lRegularPayment, PAYMENT_DELAY, PAYMENT_PERIOD, PLAN_LENGTH, nMaxPayments);
			}

			if (!bSuccessSetPaymentPlan)
			{
				OTLog::Output(0, "Failed trying to set the payment plan!\n");

				// IF FAILED, ADD TRANSACTION NUMBER BACK TO LIST OF AVAILABLE NUMBERS.
				g_pTemporaryNym->AddTransactionNum(*g_pTemporaryNym, strServerID, lTransactionNumber, true); // bSave=true								

				continue;
			}

			thePlan.SignContract(*g_pTemporaryNym);
			thePlan.SaveContract();

			OTString strPlan(thePlan);

			OTLog::vOutput(0, "\n\n(Make sure Both Parties sign the payment plan before submitting to server):\n\n\n%s\n", 
						   strPlan.Get());

			continue;			
		}


		else if (strLine.compare(0,6,"cheque") == 0)
		{
			if (NULL == g_pTemporaryNym)
			{
				OTLog::Output(0, "No Nym yet available to sign the cheque with. Try 'load'.\n");
				continue;
			}


			OTLog::Output(0, "Enter the ID for your Asset Account that the cheque will be drawn on: ");
			OTString strTemp;
			strTemp.OTfgets(std::cin);

			const OTIdentifier ACCOUNT_ID(strTemp), USER_ID(*g_pTemporaryNym);
			OTAccount * pAccount = g_OT_API.GetWallet()->GetAccount(ACCOUNT_ID);

			if (NULL == pAccount)
			{
				OTLog::Output(0, "That account isn't loaded right now. Try 'load'.\n");
				continue;
			}

			// To write a cheque, we need to burn one of our transaction numbers. (Presumably the wallet
			// is also storing a couple of these, since they are needed to perform any transaction.)
			//
			// I don't have to contact the server to write a cheque -- as long as I already have a transaction
			// number I can use to write it. Otherwise I'd have to ask the server to send me one first.
			OTString strServerID(pAccount->GetRealServerID());
			long lTransactionNumber=0;

			if (false == g_pTemporaryNym->GetNextTransactionNum(*g_pTemporaryNym, strServerID, lTransactionNumber))
			{
				OTLog::Output(0, "Cheques are written offline, but you still need a transaction number\n"
						"(and you have none, currently.) Try using 'n' to request another transaction number.\n");
				continue;
			}


			OTCheque theCheque(pAccount->GetRealServerID(), pAccount->GetAssetTypeID());

			// Recipient
			OTLog::Output(0, "Enter a User ID for the recipient of this cheque (defaults to blank): ");
			OTString strRecipientUserID;
			strRecipientUserID.OTfgets(std::cin);
			const OTIdentifier RECIPIENT_USER_ID(strRecipientUserID.Get());

			// Amount
			OTLog::Output(0, "Enter an amount: ");
			strTemp.Release();
			strTemp.OTfgets(std::cin);
			const long lAmount = atol(strTemp.Get());

			// -----------------------------------------------------------------------

			// Memo
			OTLog::Output(0, "Enter a memo for your check: ");
			OTString strChequeMemo;
			strChequeMemo.OTfgets(std::cin);

			// -----------------------------------------------------------------------

			// Valid date range (in seconds)
			OTLog::Output(0, 
					" 6 minutes	==      360 Seconds\n"
					"10 minutes	==      600 Seconds\n"
					"1 hour		==     3600 Seconds\n"
					"1 day		==    86400 Seconds\n"
					"30 days	==  2592000 Seconds\n"
					"3 months	==  7776000 Seconds\n"
					"6 months	== 15552000 Seconds\n\n"
					);

			long lExpirationInSeconds = 3600;
			OTLog::vOutput(0, "How many seconds before cheque expires? (defaults to 1 hour: %ld): ", lExpirationInSeconds);
			strTemp.Release();
			strTemp.OTfgets(std::cin);

			if (strTemp.GetLength() > 1)
				lExpirationInSeconds = atol(strTemp.Get());


			// -----------------------------------------------------------------------

			time_t	VALID_FROM	= time(NULL); // This time is set to TODAY NOW

			OTLog::vOutput(0, "Cheque may be cashed STARTING date (defaults to now, in seconds) [%ld]: ", VALID_FROM);
			strTemp.Release();
			strTemp.OTfgets(std::cin);

			if (strTemp.GetLength() > 2)
				VALID_FROM = atol(strTemp.Get());


			const time_t VALID_TO = VALID_FROM + lExpirationInSeconds; // now + 3600

			// -----------------------------------------------------------------------

			bool bIssueCheque = theCheque.IssueCheque(lAmount, lTransactionNumber, VALID_FROM, VALID_TO, 
													  ACCOUNT_ID, USER_ID, strChequeMemo,
													  (strRecipientUserID.GetLength() > 2) ? &(RECIPIENT_USER_ID) : NULL);

			if (bIssueCheque)
			{
				theCheque.SignContract(*g_pTemporaryNym);
				theCheque.SaveContract();

				OTString strCheque(theCheque);

				OTLog::vOutput(0, "\n\nOUTPUT:\n\n\n%s\n", strCheque.Get());
			}
			else 
			{
				OTLog::Output(0, "Failed trying to issue the cheque!\n");

				// IF FAILED, ADD TRANSACTION NUMBER BACK TO LIST OF AVAILABLE NUMBERS.
				g_pTemporaryNym->AddTransactionNum(*g_pTemporaryNym, strServerID, lTransactionNumber, true); // bSave=true								
			}

			continue;
		}

		else if (strLine.compare(0,7,"decrypt") == 0)
		{
			if (NULL == g_pTemporaryNym)
			{
				OTLog::Output(0, "No Nym yet available to decrypt with.\n");
				continue;
			}

			OTLog::Output(0, "Enter text to be decrypted:\n> ");

			OTASCIIArmor theArmoredText;
			char decode_buffer[200]; // Safe since we only read sizeof - 1

			do {
				decode_buffer[0] = 0;
				if (NULL != fgets(decode_buffer, sizeof(decode_buffer)-1, stdin))
				{
					theArmoredText.Concatenate("%s\n", decode_buffer);
					OTLog::Output(0, "> ");
				}
				else 
				{
					break;
				}
			} while (strlen(decode_buffer)>1);


			OTEnvelope	theEnvelope(theArmoredText);
			OTString	strDecodedText;

			theEnvelope.Open(*g_pTemporaryNym, strDecodedText);

			OTLog::vOutput(0, "\n\nDECRYPTED TEXT:\n\n%s\n\n", strDecodedText.Get());

			continue;
		}

		else if (strLine.compare(0,6,"decode") == 0)
		{
			OTLog::Output(0, "Enter text to be decoded:\n> ");

			OTASCIIArmor theArmoredText;
			char decode_buffer[200]; // Safe since we only read sizeof - 1.

			do {
				decode_buffer[0] = 0;
				if (NULL != fgets(decode_buffer, sizeof(decode_buffer)-1, stdin))
				{
					theArmoredText.Concatenate("%s\n", decode_buffer);
					OTLog::Output(0, "> ");
				}
				else 
				{
					break;
				}

			} while (strlen(decode_buffer)>1);

			OTString strDecodedText(theArmoredText);

			OTLog::vOutput(0, "\n\nDECODED TEXT:\n\n%s\n\n", strDecodedText.Get());

			continue;
		}

		else if (strLine.compare(0,6,"encode") == 0)
		{
			OTLog::Output(0, "Enter text to be ascii-encoded (terminate with ~ on a new line):\n> ");

			OTString strDecodedText;
			char decode_buffer[200]; // Safe since we only read sizeof - 1.

			do {
				decode_buffer[0] = 0;

				if ((NULL != fgets(decode_buffer, sizeof(decode_buffer)-1, stdin)) &&
					(decode_buffer[0] != '~'))
				{
					strDecodedText.Concatenate("%s", decode_buffer);
					OTLog::Output(0, "> ");
				}
				else 
				{
					break;
				}

			} while (decode_buffer[0] != '~');

			OTASCIIArmor theArmoredText(strDecodedText);

			OTLog::vOutput(0, "\n\nENCODED TEXT:\n\n%s\n\n", theArmoredText.Get());

			continue;
		}

		else if (strLine.compare(0,4,"hash") == 0)
		{
			OTLog::Output(0, "Enter text to be hashed (terminate with ~ on a new line):\n> ");

			OTString strDecodedText;
			char decode_buffer[200]; // Safe since we only read sizeof - 1.

			do {
				decode_buffer[0] = 0;

				if ((NULL != fgets(decode_buffer, sizeof(decode_buffer)-1, stdin)) &&
					(decode_buffer[0] != '~'))
				{
					strDecodedText.Concatenate("%s\n", decode_buffer);
					OTLog::Output(0, "> ");
				}
				else 
				{
					break;
				}

			} while (decode_buffer[0] != '~');

			OTIdentifier theIdentifier;
			theIdentifier.CalculateDigest(strDecodedText);

			OTString strHash(theIdentifier);

			OTLog::vOutput(0, "\n\nMESSAGE DIGEST:\n\n%s\n\n", strHash.Get());

			continue;
		}

		else if (strLine.compare(0,4,"stat") == 0)
		{
			OTLog::Output(0, "User has instructed to display wallet contents...\n");

			OTString strStat;
			g_OT_API.GetWallet()->DisplayStatistics(strStat);
			OTLog::vOutput(0, "%s\n", strStat.Get());

			continue;
		}

		else if (strLine.compare(0,4,"help") == 0)
		{
			OTLog::Output(0, "User has instructed to display the help file...\n");

			system("more ../docs/CLIENT-COMMANDS.txt");

			continue;
		}

		else if (strLine.compare(0,4,"quit") == 0)
		{
			OTLog::Output(0, "User has instructed to exit the wallet...\n");

			break;
		}

		// 1.6 Connect to the first server in the wallet. (assuming it loaded a server contract.)
		else if (strLine.compare(0,7,"connect") == 0)
		{
			OTLog::Output(0, "User has instructed to connect to the first server available in the wallet.\n");

			if (NULL == g_pTemporaryNym)
			{
				OTLog::Output(0, "No Nym yet available to connect with. Try 'load'.\n");
				continue;
			}

			// Wallet, after loading, should contain a list of server
			// contracts. Let's pull the hostname and port out of
			// the first contract, and connect to that server.
			bool bConnected = g_OT_API.GetClient()->ConnectToTheFirstServerOnList(*g_pTemporaryNym, strCAFile, strKeyFile, strSSLPassword); 

			if (bConnected)
				OTLog::Output(0, "Success. (Connected to the first notary server on your wallet's list.)\n");
			else {
				OTLog::Output(0, "Either the wallet is not loaded, or there was an error connecting to server.\n");
			}

			continue;
		}

		if (!g_OT_API.GetClient()->IsConnected())
		{
			OTLog::Output(0, "(You are not connected to a notary server--you cannot send commands.)\n");
			continue;
		}

		// 2) Process it out as an OTMessage to the server. It goes down the pipe.
		g_OT_API.GetClient()->ProcessMessageOut(buf, &nExpectResponse);
		
		// 3) Sleep for 1 second.
#ifdef _WIN32
		OT_Sleep(1000);
#else
		sleep (1);
#endif

		bool bFoundMessage = false;

		// 4) While there are messages to be read in response from the server,
		//	  then process and handle them all.
		do 
		{
			OTMessage * pMsg = new OTMessage;

			OT_ASSERT(NULL != pMsg);

			// If this returns true, that means a Message was
			// received and processed into an OTMessage object (theMsg)
			bFoundMessage = g_OT_API.GetClient()->ProcessInBuffer(*pMsg);

			if (true == bFoundMessage)
			{
//				OTString strReply;
//				theMsg.SaveContract(strReply);
//				OTLog::vOutput(0, "\n\n**********************************************\n"
//						"Successfully in-processed server response.\n\n%s\n", strReply.Get());
				g_OT_API.GetClient()->ProcessServerReply(*pMsg); // the Client takes ownership and will handle cleanup.
			}
			else 
			{
				delete pMsg;
				pMsg = NULL;
			}


		} while (true == bFoundMessage);
	} // for

	OTLog::Output(0, "Finished running client.\n");

#ifdef _WIN32
	WSACleanup();
#endif

	return retVal;
}
// THIS FUNCTION IS CALLED BY THE CUSTOMER
//
// (Transaction number and closing number are retrieved from Nym at this time.)
bool OTAgreement::Confirm(OTPseudonym & MERCHANT_NYM, OTPseudonym & PAYER_NYM)
{
    // ----------------------------------------------------------------------------
    OTIdentifier id_MERCHANT_NYM, id_PAYER_NYM;
    MERCHANT_NYM.GetIdentifier(id_MERCHANT_NYM);
    PAYER_NYM.GetIdentifier(id_PAYER_NYM);
    
    if (GetRecipientUserID() == GetSenderUserID())
    {
        OTLog::Output(0, "OTAgreement::Confirm: Error: Sender and recipient have the same Nym ID (not allowed.)\n");
        return false;        
    }
    else if (GetRecipientUserID() != id_MERCHANT_NYM)
    {
        OTLog::Output(0, "OTAgreement::Confirm: Merchant has wrong NymID (should be same as RecipientUserID.)\n");
        return false;        
    }
    else if (GetSenderUserID() != id_PAYER_NYM)
    {
        OTLog::Output(0, "OTAgreement::Confirm: Payer has wrong NymID (should be same as SenderUserID.)\n");
        return false;        
    }
    else if (PAYER_NYM.GetTransactionNumCount(GetServerID()) < 2) // Need opening and closing numbers (that's 2)... 
    {
        OTLog::Output(0, "OTAgreement::Confirm: Failure. You need at least 2 transaction numbers available to do this.\n");
		return false;
    }
    else if (GetRecipientCountClosingNumbers() < 2)
    {
        OTLog::Output(0, "OTAgreement::Confirm: Failure. (The merchant was supposed to attach 2 transaction numbers.)\n");
		return false;
    }
    // ----------------------------------------------------------------------------
    // This is the single reason why MERCHANT_NYM was even passed in here!
    // Supposedly merchant has already signed.  Let's verify this!!
    //
    if (false == this->VerifySignature(MERCHANT_NYM))
    {
        OTLog::Output(0, "OTAgreement::Confirm: Merchant's signature failed to verify.\n");
        return false;
    }
    // ----------------------------------------------------------------------------
    
    // Now that we KNOW the merchant signed it... SAVE MERCHANT's COPY.
    // Let's save a copy of the one the merchant signed, before changing it and re-signing it,
    // (to add my own transaction numbers...)
    //
    OTString strTemp;
    this->SaveContractRaw(strTemp);
    this->SetMerchantSignedCopy(strTemp);

    // *******************************************************************
    
    // The payer has to submit TWO transaction numbers in order to activate this agreement...
    //
    OTString strServerID(GetServerID());
	long lTransactionNumber=0, lClosingTransactionNo=0;
	
	if (false == PAYER_NYM.GetNextTransactionNum(PAYER_NYM, strServerID, lTransactionNumber))
	{
		OTLog::Error("OTAgreement::Confirm: Error: Strangely unable to get a transaction number.\n");
		return false;
	}
	else if (false == PAYER_NYM.GetNextTransactionNum(PAYER_NYM, strServerID, lClosingTransactionNo))
	{
 		OTLog::Error("OTAgreement::Confirm: Error: Strangely unable to get a closing transaction number.\n");
        PAYER_NYM.AddTransactionNum(PAYER_NYM, strServerID, lTransactionNumber, true); // bSave=true
        // (Since the first one was successful, we just put it back before returning.)
		return false;
	}
    
    // At this point we now HAVE 2 transaction numbers (for payer / sender)...
    // We can't return without USING THEM or PUTTING THEM BACK.
    //
    // ---------------------------------------------------------
 
     
	this->SetTransactionNum(lTransactionNumber); // Set the Transaction Number
    this->AddClosingTransactionNo(lClosingTransactionNo); // and the Closing Number (both for sender)...

	// ------------------------------------------- 
	
    // CREATION DATE was set in the Merchant's proposal, and it's RESET here in the Confirm.
    // This way, (since we still have the original proposal) we can see BOTH times.
    //
	time_t CURRENT_TIME = time(NULL);
	// Set the Creation Date.
	SetCreationDate(CURRENT_TIME);
    
	// ------------------------------------------- 
    
	OTLog::Output(4, "OTAgreement::Confirm(): Success!\n");
    
	return true;
}
// THIS FUNCTION IS CALLED BY THE MERCHANT
//
// (lMerchantTransactionNumber, lMerchantClosingNumber are set internally in this call, from MERCHANT_NYM.)
bool OTAgreement::SetProposal(OTPseudonym & MERCHANT_NYM,       const OTString & strConsideration,
                              const time_t VALID_FROM/*=0*/,  const time_t VALID_TO/*=0*/)
{
    // ----------------------------------------------------------------------------
    OTIdentifier id_MERCHANT_NYM;
    MERCHANT_NYM.GetIdentifier(id_MERCHANT_NYM);
    
    if (GetRecipientUserID() != id_MERCHANT_NYM)
    {
        OTLog::Output(0, "OTAgreement::SetProposal: Merchant has wrong NymID (should be same as RecipientUserID.)\n");
        return false;        
    }
    else if (GetRecipientUserID() == GetSenderUserID())
    {
        OTLog::Output(0, "OTAgreement::SetProposal: Error: Sender and recipient have the same Nym ID (not allowed.)\n");
        return false;        
    }
    else if (MERCHANT_NYM.GetTransactionNumCount(GetServerID()) < 2) // Need opening and closing numbers (that's 2)... 
    {
        OTLog::Output(0, "OTAgreement::SetProposal: Failure. You need at least 2 transaction numbers available to do this.\n");
		return false;
    }

    // ------------------------------------------- 
	// Set the CREATION DATE
    //
	const time_t CURRENT_TIME = time(NULL);
	
	// Set the Creation Date.
	SetCreationDate(CURRENT_TIME);

    // -----------------------------------------
    // Putting this above here so I don't have to put the transaction numbers back if this fails:
    // ------------------------------------------- 
    // VALID_FROM
    //
	// The default "valid from" time is NOW.
	if (0 >= VALID_FROM) // if it's 0 or less, set to current time.
		SetValidFrom(CURRENT_TIME);
	else // Otherwise use whatever was passed in.
		SetValidFrom(VALID_FROM);
    // ------------------------------------------- 
    // VALID_TO
    //
	// The default "valid to" time is 0 (which means no expiration date / cancel anytime.)
	if (0 == VALID_TO) // VALID_TO is 0
	{
		SetValidTo(VALID_TO); // Keep it at zero then, so it won't expire.
	}
	else if (0 < VALID_TO) // VALID_TO is ABOVE zero...
	{
		if (VALID_TO < VALID_FROM) // If Valid-To date is EARLIER than Valid-From date...
		{
			long lValidTo = VALID_TO, lValidFrom = VALID_FROM;
			OTLog::vError("OTAgreement::SetProposal: VALID_TO (%ld) is earlier than VALID_FROM (%ld)\n", 
                          lValidTo, lValidFrom);
			return false;
		}
		
		SetValidTo(VALID_TO); // Set it to whatever it is, since it is now validated as higher than Valid-From.
	}
	else // VALID_TO is a NEGATIVE number... Error.
	{
		long lValidTo = VALID_TO;
		OTLog::vError("Negative value for valid_to in SetAgreement: %ld\n", lValidTo);
        
		return false;
	}

    // ----------------------------------------------------------------------------
    // Since we'll be needing 2 transaction numbers to do this, let's grab 'em...
    //
    OTString strServerID(GetServerID());
    
	long lTransactionNumber=0, lClosingTransactionNo=0;
	
    if (MERCHANT_NYM.GetTransactionNumCount(GetServerID()) < 2) // Need opening and closing numbers (that's 2)... 
    {
        OTLog::Output(0, "OTAgreement::SetProposal: Failure. You need at least 2 transaction numbers available to do this.\n");
		return false;
    }
	else if (false == MERCHANT_NYM.GetNextTransactionNum(MERCHANT_NYM, strServerID, lTransactionNumber))
	{
		OTLog::Error("OTAgreement::SetProposal: Error: Strangely unable to get a transaction number.\n");
		return false;
	}
	else if (false == MERCHANT_NYM.GetNextTransactionNum(MERCHANT_NYM, strServerID, lClosingTransactionNo))
	{
 		OTLog::Error("OTAgreement::SetProposal: Error: Strangely unable to get a closing transaction number.\n");
        MERCHANT_NYM.AddTransactionNum(MERCHANT_NYM, strServerID, lTransactionNumber, true); // bSave=true
        // (Since the first one was successful, we just put it back before returning.)
		return false;
	}

    // At this point we now have 2 transaction numbers...
    // We can't return without either USING THEM, or PUTTING THEM BACK.
    //
    // ---------------------------------------------------------
    
	// Set the Transaction Number and the Closing transaction number... (for merchant / recipient.)
    //
    this->AddRecipientClosingTransactionNo(lTransactionNumber);
    this->AddRecipientClosingTransactionNo(lClosingTransactionNo);
    // (They just both go onto this same list.)
    
	// ------------------------------------------- 

	// Set the Consideration memo...
	m_strConsideration.Set(strConsideration);
            
	// ------------------------------------------- 
    
	OTLog::Output(4, "Successfully performed OTPaymentPlan::SetProposal()\n");
	
	return true;
}
int main (int argc, char * const argv[])
{
	if (argc < 5)
	{
		printf("\n\nUsage:  createmint  server_id  server_user_id  asset_type_id  full_path_to_data_folder\n\n"
			   "For now, expiration dates are automatically set:\n"
			   "-- FROM: Today, Now.\n"
			   "-- TO: 6 months from now.\n\n"
			   "It is recommended that you issue a new series (create a new mint) every 3 months for each\n"
			   "asset type, so that there is a 3-month overlap. In the future, this will be configured inside\n"
			   "the contracts themselves.\n\n"
			   "Server ID needs to match the Server ID from notaryServer.xml\n"
			   "Server user ID needs to match the Server User ID from notaryServer.xml\n"
			   "Asset Type ID needs to match the Asset ID (aka Hashed Contract ID) of the currency contract.\n"
			   "The path to the data folder is necessary for initialization of the OT library.\n\n");
		exit(1);
	}
	
	SSL_library_init();
	SSL_load_error_strings();

	// -----------------------------------------
	
	OTString		strServerID(argv[1]), strServerNymID(argv[2]), strAssetTypeID(argv[3]);
	OTIdentifier	ASSET_TYPE_ID(strAssetTypeID), SERVER_ID(strServerID);
	
	std::string strDataFolderPath(argv[4]), strWalletFile("wallet.xml");
	
	bool bSuccessInitDefault = OTDB::InitDefaultStorage(OTDB_DEFAULT_STORAGE, 
														OTDB_DEFAULT_PACKER, strDataFolderPath, strWalletFile);
		
	// -----------------------------------------
	
	OTString strMintPath;
	bool bFileIsPresent = false;		
	int nSeries = 0;
	
	for (nSeries = 0; nSeries < 1000; nSeries++)
	{
		struct stat st;

		strMintPath.Format("%s%s%s%s%s%s%s%s%d", 
						   OTLog::Path(), 
						   OTLog::PathSeparator(),
						   OTLog::MintFolder(),
						   OTLog::PathSeparator(),
						   strServerID.Get(),
						   OTLog::PathSeparator(),
						   strAssetTypeID.Get(), ".", nSeries);
		
		bFileIsPresent = (stat(strMintPath.Get(), &st) == 0);
		
		if (!bFileIsPresent)
			break;
	}
	
	// if bFileIsPresent is STILL true, that means we got all the way up to 1000 and the 
	// file was present every time.
	// Geez, there must be 1000 mints on this computer.  At one new Mint per 3 months,
	// that's 4 per year, that's 250 years already!!
	if (bFileIsPresent)
	{
		OTLog::Output(0, "This program automatically finds the next series, up to 1000. You\n"
				"have reached 1000. You will have to change the source code of this\n"
				"program in order to continue. Sorry.\n");
		exit(1);
	}
	
	
	// nSeries now contains the number we need to use for the next series.
	// and strMintPath now contains the correct file path.
	
	OTMint * pMint = new OTMint(strServerID, strServerNymID, strAssetTypeID);
	
	OTString strSeries; strSeries.Format("%s%d", ".", nSeries);
	//
	if ((NULL != pMint) && pMint->LoadMint(strSeries.Get()))
	{
		OTLog::Output(0, "The mint already exists. Delete it first if you wish to re-create it.\n");
	}
	else 
	{
		OTLog::vOutput(0, "Mint file does not (yet) exist for series %d and asset type:\n%s\n Creating......\n", 
				nSeries, strAssetTypeID.Get());
		
		if (pMint)
		{	
			// TODO: read the denominations out of the asset contract itself, instead of hardcoding them here.
			
			
			// Calculate FROM as Today, Now, 
			// then calculate TO as 6 months from now, 
			// and EXPIRATION as 3 months from now.
			//
			// TODO: Let these numbers be configured either in server operator contract, or issuer contract.
			// In the meantime, 3 and 6 months are good enough.
			
			OTPseudonym theNym;
			theNym.SetIdentifier(strServerNymID);
			
			// 1 hour	==     3600 Seconds
			// 1 day	==    86400 Seconds
			// 30 days	==  2592000 Seconds
			// 3 months ==  7776000 Seconds
			// 6 months == 15552000 Seconds
			
			
			// This part requires the server operator to enter his passphrase.
			// Which is why the server can't just fire it off automatically and
			// make a mint available to the client.  The client has to wait a day or
			// until the operator is able to run this script and type the passphrase.
			if (theNym.Loadx509CertAndPrivateKey())
			{								
				const time_t	CURRENT_TIME	= time(NULL), 
								VALID_TO		= CURRENT_TIME + 15552000,	// Tokens generated by this mint are valid from today until 6 months from today
								MINT_EXPIRATION	= CURRENT_TIME + 7776000;	// The mint itself will expire in 3 months from today, and be replaced with a new one.
				
				pMint->GenerateNewMint(nSeries, CURRENT_TIME, VALID_TO, MINT_EXPIRATION, ASSET_TYPE_ID, SERVER_ID,
														// penny, nickel, dime, quarter, $1, $5, $10, $20, $100, $1000
													theNym, 1, 5, 10, 25, 100, 500, 1000, 2000, 10000, 100000);
												// should be: 1, 2, 4,   8,  16,  32,   64,  128,   256,    512, 1024, 2048, 4096, 8192, 16384, 32768, 65536
				
				// ---------------------------------------
				
//				OTString strFilename;// strPUBLICFilename;		
//				strFilename.		Format("%s%s%s",		strServerID.Get(), OTLog::PathSeparator(), strAssetTypeID.Get());
//				strPUBLICFilename.	Format("%s%s%s%sPUBLIC",strServerID.Get(), OTLog::PathSeparator(), strAssetTypeID.Get(), ".");
				
				OTString strServerFolder;
				strServerFolder.Format("%s%s%s", OTLog::MintFolder(), OTLog::PathSeparator(), strServerID.Get());
				
				if (OTLog::ConfirmOrCreateFolder(OTLog::MintFolder()) &&
					OTLog::ConfirmOrCreateFolder(strServerFolder.Get()))
				{
					// -------------------------------------------------------------------
					// This causes the next serialization to save the private, not just public, keys.
					pMint->SetSavePrivateKeys(); // <== PRIVATE MINT VERSION.
					
					pMint->SignContract(theNym);
					pMint->SaveContract();
					
					pMint->SaveMint();		// save the private mint file as: path/mints/server_id/Asset_TypeID (overwriting the last "current mint"...)
					pMint->SaveMint(strSeries.Get()); // save the private mint file as: path/mints/server_id/Asset_TypeID.nSeries (These accumulate.)
					
					// When the server needs to reference the "current" private keys, then it just loads the
					// mint without specifying the series number. Since this file is overwritten with each new 
					// mint, it is thus always the latest one.  (Similarly, the latest public is always asset_id.PUBLIC)
					// On the other hand, if I need to load the keys for a specific series, (since more than one may be
					// redeemable, even if only the latest one is being issued) then they are all also saved by series
					// number, which is not overwritten with each new mint, but accumulates.
					// That is why above, you see me save the mint twice in two different files, and below you see
					// it being saved with the .PUBLIC appending to the filename.
					
//					pMint->SaveContract(OTLog::MintFolder(), strFilename.Get());  // save the mint file.
					
					// -------------------------------------------------------------------
					// Now I sign it again, to get the private keys out of there.
					pMint->ReleaseSignatures();
					pMint->SignContract(theNym);
					pMint->SaveContract();
					
					pMint->SaveMint(".PUBLIC");  // save the public mint file.
//					pMint->SaveContract(OTLog::MintFolder(), strPUBLICFilename.Get());  // save the public mint file.
					
					OTLog::Output(0, "Done.\n");
				}
				else
					OTLog::Output(0, "Error calling OTLog::ConfirmOrCreateFolder() for path/mints/server_id\n");
			}
			else
				OTLog::Output(0, "Error calling theNym.Loadx509CertAndPrivateKey()\n");
		}
		else 
		{
			OTLog::Output(0, "Error allocating memory for new OTMint.\n");
		}
	}
	
	return 0;
}