/** Initialise wallet (load it if it's there).
  * \return #WALLET_NO_ERROR on success, or one of #WalletErrorsEnum if an
  *         error occurred.
  */
WalletErrors initWallet(void)
{
	uint8_t buffer[32];
	uint8_t hash[32];
	uint32_t version;

	wallet_loaded = 0;

	// Read version.
	if (nonVolatileRead(buffer, OFFSET_VERSION, 4) != NV_NO_ERROR)
	{
		last_error = WALLET_READ_ERROR;
		return last_error;
	}
	version = readU32LittleEndian(buffer);
	if ((version != VERSION_UNENCRYPTED) && (version != VERSION_IS_ENCRYPTED))
	{
		last_error = WALLET_NOT_THERE;
		return last_error;
	}

	// Calculate checksum and check that it matches.
	if (calculateWalletChecksum(hash) != NV_NO_ERROR)
	{
		last_error = WALLET_READ_ERROR;
		return last_error;
	}
	if (encryptedNonVolatileRead(buffer, OFFSET_CHECKSUM, 32) != NV_NO_ERROR)
	{
		last_error = WALLET_READ_ERROR;
		return last_error;
	}
	if (bigCompare(buffer, hash) != BIGCMP_EQUAL)
	{
		last_error = WALLET_NOT_THERE;
		return last_error;
	}

	// Read number of addresses.
	if (encryptedNonVolatileRead(buffer, OFFSET_NUM_ADDRESSES, 4) != NV_NO_ERROR)
	{
		last_error = WALLET_READ_ERROR;
		return last_error;
	}
	num_addresses = readU32LittleEndian(buffer);

	wallet_loaded = 1;
	last_error = WALLET_NO_ERROR;
	return last_error;
}
// Receive a packet, copying it into a memory buffer and returning the buffer
static uint8_t *receivePacket(int fd)
{
	uint8_t packet_header[5];
	uint8_t *packet_buffer;
	uint32_t length;
	uint32_t i;

	for (i = 0; i < 5; i++)
	{
		packet_header[i] = receiveByte(fd);
	}
	length = readU32LittleEndian(&(packet_header[1]));
	if (length > PACKET_LENGTH_LIMIT)
	{
		printf("Got absurdly large packet length of %d\n", length);
		printf("Exiting, since the packet is probably garbled\n");
		exit(1);
	}
	packet_buffer = malloc(length + 5);
	memcpy(packet_buffer, packet_header, 5);
	for (i = 0; i < length; i++)
	{
		packet_buffer[i + 5] = receiveByte(fd);
	}
	return packet_buffer;
}
// Display packet contents on screen
static void displayPacket(uint8_t *packet_data, uint32_t buffer_length)
{
	uint8_t command;
	uint8_t one_byte;
	uint32_t length;
	uint32_t i;

	command = packet_data[0];
	length = readU32LittleEndian(&(packet_data[1]));
	printf("command 0x%02x (%s)\n", command, packetCommandToText(command));
	printf("Payload length: %d\n", length);

	// display hex bytes
	for (i = 0; i < length; i++)
	{
		if (i && !(i & 15))
		{
			printf("\n");
		}
		one_byte = packet_data[i + 5];
		printf(" %02x", one_byte);
		if ((i + 5) >= buffer_length)
		{
			printf(" ***unexpected end of packet***");
			break;
		}
	}
	printf("\n");
	// display ASCII
	for (i = 0; i < length; i++)
	{
		if (i && !(i & 15))
		{
			printf("\n");
		}
		one_byte = packet_data[i + 5];
		if ((one_byte < 32) || (one_byte > 126))
		{
			printf(".");
		}
		else
		{
			printf("%c", packet_data[i + 5]);
		}
		if ((i + 5) >= buffer_length)
		{
			break;
		}
	}
	printf("\n");
}
// Receive real number from serial link. The real number should be in Q16.16
// representation. This is so that the device under test doesn't have to
// do the conversion to floating-point.
static double receiveDouble(void)
{
    uint8_t buffer[4];
    int j;

    for (j = 0; j < 4; j++)
    {
        buffer[j] = receiveByte();
    }
    // The cast from uint32_t to fix16_t isn't platform-independent, because
    // the C99 specification doesn't make any guarantees about conversions
    // to signed integer types (...if the destination type cannot store the
    // source value, which will be the case if the fix16_t is negative).
    // But it should work on nearly every contemporary platform.
    return fix16_to_dbl((fix16_t)readU32LittleEndian(buffer));
}
// Send a byte to the serial link, waiting for acknowledgement if required.
static void sendByte(uint8_t data)
{
    uint8_t ack_buffer[5];
    uint8_t buffer;

    buffer = data;
    write(fd_serial, &buffer, 1);
    tx_bytes_to_ack--;
    if (!tx_bytes_to_ack)
    {
        read(fd_serial, ack_buffer, 5);
        if (ack_buffer[0] != 0xff)
        {
            printf("Unexpected acknowledgement format (%d)\n", (int)ack_buffer[0]);
            printf("Exiting, since the serial link is probably dodgy\n");
            exit(1);
        }
        tx_bytes_to_ack = readU32LittleEndian(&(ack_buffer[1]));
    }
}
int main(int argc, char **argv)
{
    int i;
    int matches;
    int succeeded;
    int failed;
    char *newline_position;
    char buffer[512];
    int input_array[SAMPLE_COUNT];
    double expected_array[OUTPUTS_TO_CHECK];
    double output_array[OUTPUTS_TO_CHECK];
    FILE *f_vectors; // file containing test vectors
    struct termios options;
    struct termios old_options;
    uint8_t cycles_buffer[4];

    if (argc != 2)
    {
        printf("Usage: %s <serial device>\n", argv[0]);
        printf("\n");
        printf("Example: %s /dev/ttyUSB0\n", argv[0]);
        exit(1);
    }

    // Attempt to open serial link.
    fd_serial = open(argv[1], O_RDWR | O_NOCTTY);
    if (fd_serial == -1)
    {
        printf("Could not open device \"%s\"\n", argv[1]);
        printf("Make sure you have permission to open it. In many systems, only\n");
        printf("root can access devices by default.\n");
        exit(1);
    }

    fcntl(fd_serial, F_SETFL, 0); // block on reads
    tcgetattr(fd_serial, &old_options); // save configuration
    memcpy(&options, &old_options, sizeof(options));
    cfsetispeed(&options, B57600); // baud rate 57600
    cfsetospeed(&options, B57600);
    options.c_cflag |= (CLOCAL | CREAD); // enable receiver and set local mode on
    options.c_cflag &= ~PARENB; // no parity
    options.c_cflag &= ~CSTOPB; // 1 stop bit
    options.c_cflag &= ~CSIZE; // character size mask
    options.c_cflag |= CS8; // 8 data bits
    options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // raw input
    options.c_lflag &= ~(XCASE | ECHOK | ECHONL | ECHOCTL | ECHOPRT | ECHOKE); // disable more stuff
    options.c_iflag &= ~(IXON | IXOFF | IXANY); // no software flow control
    options.c_iflag &= ~(INPCK | INLCR | IGNCR | ICRNL | IUCLC); // disable more stuff
    options.c_oflag &= ~OPOST; // raw output
    tcsetattr(fd_serial, TCSANOW, &options);
    rx_bytes_to_ack = DEFAULT_ACKNOWLEDGE_INTERVAL;
    tx_bytes_to_ack = DEFAULT_ACKNOWLEDGE_INTERVAL;

    // Attempt to open file containing test vectors.
    f_vectors = fopen("statistics_test_vectors.txt", "r");
    if (f_vectors == NULL)
    {
        printf("Could not open \"statistics_test_vectors.txt\" for reading\n");
        exit(1);
    }

    sendByte(0); // set device testing mode (see testStatistics() in ../hwrng.c)

    succeeded = 0;
    failed = 0;
    while (!feof(f_vectors))
    {
        // Read name of test.
        fgets(buffer, sizeof(buffer), f_vectors);
        // Remove newlines.
        newline_position = strrchr(buffer, '\n');
        if (newline_position != NULL)
        {
            *newline_position = '\0';
        }
        newline_position = strrchr(buffer, '\r');
        if (newline_position != NULL)
        {
            *newline_position = '\0';
        }
        printf("%s: ", buffer);

        readIntegerArray(f_vectors, input_array, SAMPLE_COUNT);
        readRealArray(f_vectors, expected_array, OUTPUTS_TO_CHECK);
        sendIntegerArray(input_array, SAMPLE_COUNT);
        receiveRealArray(output_array, OUTPUTS_TO_CHECK);
        matches = realArraysEqualWithinTolerance(expected_array, output_array, OUTPUTS_TO_CHECK);
        // Get number of cycles required to do all tests.
        for (i = 0; i < 4; i++)
        {
            cycles_buffer[i] = receiveByte();
        }
        printf("cycles = %u ", readU32LittleEndian(cycles_buffer));
        if (matches)
        {
            printf("[pass]\n");
            succeeded++;
        }
        else
        {
            printf("[fail]\n");
            // Make failure noticable.
            printf("************************\n");
            printf("FAIL FAIL FAIL FAIL FAIL\n");
            printf("************************\n");
            failed++;
        }
    }

    printf("Tests which succeeded: %d\n", succeeded);
    printf("Tests which failed: %d\n", failed);

    fclose(f_vectors);
    tcsetattr(fd_serial, TCSANOW, &old_options); // restore configuration
    close(fd_serial);
    exit(0);
}
int main(int argc, char **argv)
{
	char filename[256];
	char *newline;
	int abort;
	FILE *file_to_send;
	int fd_serial;
	uint8_t *packet_buffer;
	long int size;
	long int i;
	struct termios options;

	if (argc != 2)
	{
		printf("Hardware BitCoin wallet tester\n");
		printf("Usage: %s <serial device>\n", argv[0]);
		printf("\n");
		printf("Example: %s /dev/ttyUSB0\n", argv[0]);
		exit(1);
	}

	// Attempt to open serial link.
	fd_serial = open(argv[1], O_RDWR | O_NOCTTY);
	if (fd_serial == -1)
	{
		printf("Could not open device \"%s\"\n", argv[1]);
		printf("Make sure you have permission to open it. In many systems, only\n");
		printf("root can access devices by default.\n");
		exit(1);
	}

	fcntl(fd_serial, F_SETFL, 0); // block on reads
	tcgetattr(fd_serial, &options);
	cfsetispeed(&options, B57600); // baud rate 57600
	cfsetospeed(&options, B57600);
	options.c_cflag |= (CLOCAL | CREAD); // enable receiver and set local mode on
	options.c_cflag &= ~PARENB; // no parity
	options.c_cflag &= ~CSTOPB; // 1 stop bit
	options.c_cflag &= ~CSIZE; // character size mask
	options.c_cflag |= CS8; // 8 data bits
	options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // raw input
	options.c_iflag &= ~(IXON | IXOFF | IXANY); // no software flow control
	options.c_oflag &= ~OPOST; // raw output
	tcsetattr(fd_serial, TCSANOW, &options);

	tx_bytes_to_ack = DEFAULT_ACKNOWLEDGE_INTERVAL;
	rx_bytes_to_ack = DEFAULT_ACKNOWLEDGE_INTERVAL;
	abort = 0;
	do
	{
		// Get filename from user.
		printf("Enter file to send (blank to quit): ");
		fgets(filename, sizeof(filename), stdin);
		newline = strrchr(filename, '\r');
		if (newline != NULL)
		{
			*newline = '\0';
		}
		newline = strrchr(filename, '\n');
		if (newline != NULL)
		{
			*newline = '\0';
		}
		if (strcmp(filename, ""))
		{
			file_to_send = fopen(filename, "rb");
			if (file_to_send == NULL)
			{
				printf("Couldn't open file \"%s\"\n", filename);
			}
			else
			{
				// Get file length then read entire contents of file.
				fseek(file_to_send, 0, SEEK_END);
				size = ftell(file_to_send);
				fseek(file_to_send, 0, SEEK_SET);
				packet_buffer = malloc(size);
				fread(packet_buffer, size, 1, file_to_send);
				fclose(file_to_send);
				printf("Sending packet: ");
				displayPacket(packet_buffer, size);
				// Send the packet.
				for (i = 0; i < size; i++)
				{
					sendByte(packet_buffer[i], fd_serial);
				}
				free(packet_buffer);
				// Get and display response packet.
				packet_buffer = receivePacket(fd_serial);
				size = 5 + readU32LittleEndian(&(packet_buffer[1]));
				printf("Received packet: ");
				displayPacket(packet_buffer, size);
				free(packet_buffer);
			}
		}
		else
		{
			abort = 1;
		}
	} while (!abort);

	close(fd_serial);

	exit(0);
}
int main(void)
{
	uint8_t temp[128];
	uint8_t address1[20];
	uint8_t address2[20];
	uint8_t name[NAME_LENGTH];
	uint8_t encryption_key[WALLET_ENCRYPTION_KEY_LENGTH];
	uint8_t new_encryption_key[WALLET_ENCRYPTION_KEY_LENGTH];
	uint8_t version[4];
	uint8_t *address_buffer;
	uint8_t one_byte;
	AddressHandle *handles_buffer;
	AddressHandle ah;
	PointAffine public_key;
	PointAffine *public_key_buffer;
	int abort;
	int is_zero;
	int abort_duplicate;
	int abort_error;
	int i;
	int j;

	initTests(__FILE__);

	initWalletTest();
	memset(encryption_key, 0, WALLET_ENCRYPTION_KEY_LENGTH);
	setEncryptionKey(encryption_key);
	// Blank out non-volatile storage area (set to all nulls).
	temp[0] = 0;
	for (i = 0; i < TEST_FILE_SIZE; i++)
	{
		fwrite(temp, 1, 1, wallet_test_file);
	}

	// sanitiseNonVolatileStorage() should nuke everything.
	if (sanitiseNonVolatileStorage(0, 0xffffffff) == WALLET_NO_ERROR)
	{
		reportSuccess();
	}
	else
	{
		printf("Cannot nuke NV storage using sanitiseNonVolatileStorage()\n");
		reportFailure();
	}

	// Check that the version field is "wallet not there".
	if (getWalletInfo(version, temp) == WALLET_NO_ERROR)
	{
		reportSuccess();
	}
	else
	{
		printf("getWalletInfo() failed after sanitiseNonVolatileStorage() was called\n");
		reportFailure();
	}
	if (readU32LittleEndian(version) == VERSION_NOTHING_THERE)
	{
		reportSuccess();
	}
	else
	{
		printf("sanitiseNonVolatileStorage() does not set version to nothing there\n");
		reportFailure();
	}

	// initWallet() hasn't been called yet, so nearly every function should
	// return WALLET_NOT_THERE somehow.
	checkFunctionsReturnWalletNotThere();

	// The non-volatile storage area was blanked out, so there shouldn't be a
	// (valid) wallet there.
	if (initWallet() == WALLET_NOT_THERE)
	{
		reportSuccess();
	}
	else
	{
		printf("initWallet() doesn't recognise when wallet isn't there\n");
		reportFailure();
	}

	// Try creating a wallet and testing initWallet() on it.
	memcpy(name, "123456789012345678901234567890abcdefghij", NAME_LENGTH);
	if (newWallet(name) == WALLET_NO_ERROR)
	{
		reportSuccess();
	}
	else
	{
		printf("Could not create new wallet\n");
		reportFailure();
	}
	if (initWallet() == WALLET_NO_ERROR)
	{
		reportSuccess();
	}
	else
	{
		printf("initWallet() does not recognise new wallet\n");
		reportFailure();
	}
	if ((getNumAddresses() == 0) && (walletGetLastError() == WALLET_EMPTY))
	{
		reportSuccess();
	}
	else
	{
		printf("New wallet isn't empty\n");
		reportFailure();
	}

	// Check that the version field is "unencrypted wallet".
	if (getWalletInfo(version, temp) == WALLET_NO_ERROR)
	{
		reportSuccess();
	}
	else
	{
		printf("getWalletInfo() failed after newWallet() was called\n");
		reportFailure();
	}
	if (readU32LittleEndian(version) == VERSION_UNENCRYPTED)
	{
		reportSuccess();
	}
	else
	{
		printf("newWallet() does not set version to unencrypted wallet\n");
		reportFailure();
	}

	// Check that sanitise_nv_wallet() deletes wallet.
	if (sanitiseNonVolatileStorage(0, 0xffffffff) == WALLET_NO_ERROR)
	{
		reportSuccess();
	}
	else
	{
		printf("Cannot nuke NV storage using sanitiseNonVolatileStorage()\n");
		reportFailure();
	}
	if (initWallet() == WALLET_NOT_THERE)
	{
		reportSuccess();
	}
	else
	{
		printf("sanitiseNonVolatileStorage() isn't deleting wallet\n");
		reportFailure();
	}

	// Make some new addresses, then create a new wallet and make sure the
	// new wallet is empty (i.e. check that newWallet() deletes existing
	// wallet).
	newWallet(name);
	if (makeNewAddress(temp, &public_key) != BAD_ADDRESS_HANDLE)
	{
		reportSuccess();
	}
	else
	{
		printf("Couldn't create new address in new wallet\n");
		reportFailure();
	}
	newWallet(name);
	if ((getNumAddresses() == 0) && (walletGetLastError() == WALLET_EMPTY))
	{
		reportSuccess();
	}
	else
	{
		printf("newWallet() doesn't delete existing wallet\n");
		reportFailure();
	}

	// Unload wallet and make sure everything realises that the wallet is
	// not loaded.
	if (uninitWallet() == WALLET_NO_ERROR)
	{
		reportSuccess();
	}
	else
	{
		printf("uninitWallet() failed to do its basic job\n");
		reportFailure();
	}
	checkFunctionsReturnWalletNotThere();

	// Load wallet again. Since there is actually a wallet there, this
	// should succeed.
	if (initWallet() == WALLET_NO_ERROR)
	{
		reportSuccess();
	}
	else
	{
		printf("uninitWallet() appears to be permanent\n");
		reportFailure();
	}

	// Change bytes in non-volatile memory and make sure initWallet() fails
	// because of the checksum check.
	if (uninitWallet() != WALLET_NO_ERROR)
	{
		printf("uninitWallet() failed to do its basic job 2\n");
		reportFailure();
	}
	abort = 0;
	for (i = 0; i < WALLET_RECORD_LENGTH; i++)
	{
		if (nonVolatileRead(&one_byte, (uint32_t)i, 1) != NV_NO_ERROR)
		{
			printf("NV read fail\n");
			abort = 1;
			break;
		}
		one_byte++;
		if (nonVolatileWrite(&one_byte, (uint32_t)i, 1) != NV_NO_ERROR)
		{
			printf("NV write fail\n");
			abort = 1;
			break;
		}
		if (initWallet() == WALLET_NO_ERROR)
		{
			printf("Wallet still loads when wallet checksum is wrong, offset = %d\n", i);
			abort = 1;
			break;
		}
		one_byte--;
		if (nonVolatileWrite(&one_byte, (uint32_t)i, 1) != NV_NO_ERROR)
		{
			printf("NV write fail\n");
			abort = 1;
			break;
		}
	}
	if (!abort)
	{
		reportSuccess();
	}
	else
	{
		reportFailure();
	}

	// Create 2 new wallets and check that their addresses aren't the same
	newWallet(name);
	if (makeNewAddress(address1, &public_key) != BAD_ADDRESS_HANDLE)
	{
		reportSuccess();
	}
	else
	{
		printf("Couldn't create new address in new wallet\n");
		reportFailure();
	}
	newWallet(name);
	memset(address2, 0, 20);
	memset(&public_key, 0, sizeof(PointAffine));
	if (makeNewAddress(address2, &public_key) != BAD_ADDRESS_HANDLE)
	{
		reportSuccess();
	}
	else
	{
		printf("Couldn't create new address in new wallet\n");
		reportFailure();
	}
	if (memcmp(address1, address2, 20))
	{
		reportSuccess();
	}
	else
	{
		printf("New wallets are creating identical addresses\n");
		reportFailure();
	}

	// Check that makeNewAddress() wrote to its outputs.
	is_zero = 1;
	for (i = 0; i < 20; i++)
	{
		if (address2[i] != 0)
		{
			is_zero = 0;
			break;
		}
	}
	if (is_zero)
	{
		printf("makeNewAddress() doesn't write the address\n");
		reportFailure();
	}
	else
	{
		reportSuccess();
	}
	if (bigIsZero(public_key.x))
	{
		printf("makeNewAddress() doesn't write the public key\n");
		reportFailure();
	}
	else
	{
		reportSuccess();
	}

	// Make some new addresses, up to a limit.
	// Also check that addresses are unique.
	newWallet(name);
	abort = 0;
	address_buffer = malloc(MAX_TESTING_ADDRESSES * 20);
	for (i = 0; i < MAX_TESTING_ADDRESSES; i++)
	{
		if (makeNewAddress(&(address_buffer[i * 20]), &public_key) == BAD_ADDRESS_HANDLE)
		{
			printf("Couldn't create new address in new wallet\n");
			abort = 1;
			break;
		}
		for (j = 0; j < i; j++)
		{
			if (!memcmp(&(address_buffer[i * 20]), &(address_buffer[j * 20]), 20))
			{
				printf("Wallet addresses aren't unique\n");
				abort = 1;
				break;
			}
		}
		if (abort)
		{
			break;
		}
	}
	free(address_buffer);
	if (!abort)
	{
		reportSuccess();
	}
	else
	{
		reportFailure();
	}

	// The wallet should be full now.
	// Check that making a new address now causes an appropriate error.
	if (makeNewAddress(temp, &public_key) == BAD_ADDRESS_HANDLE)
	{
		if (walletGetLastError() == WALLET_FULL)
		{
			reportSuccess();
		}
		else
		{
			printf("Creating a new address on a full wallet gives incorrect error\n");
			reportFailure();
		}
	}
	else
	{
		printf("Creating a new address on a full wallet succeeds (it's not supposed to)\n");
		reportFailure();
	}

	// Check that getNumAddresses() fails when the wallet is empty.
	newWallet(name);
	if (getNumAddresses() == 0)
	{
		if (walletGetLastError() == WALLET_EMPTY)
		{
			reportSuccess();
		}
		else
		{
			printf("getNumAddresses() doesn't recognise wallet is empty\n");
			reportFailure();
		}
	}
	else
	{
		printf("getNumAddresses() succeeds when used on empty wallet\n");
		reportFailure();
	}

	// Create a bunch of addresses in the (now empty) wallet and check that
	// getNumAddresses() returns the right number.
	address_buffer = malloc(MAX_TESTING_ADDRESSES * 20);
	public_key_buffer = malloc(MAX_TESTING_ADDRESSES * sizeof(PointAffine));
	handles_buffer = malloc(MAX_TESTING_ADDRESSES * sizeof(AddressHandle));
	abort = 0;
	for (i = 0; i < MAX_TESTING_ADDRESSES; i++)
	{
		ah = makeNewAddress(&(address_buffer[i * 20]), &(public_key_buffer[i]));
		handles_buffer[i] = ah;
		if (ah == BAD_ADDRESS_HANDLE)
		{
			printf("Couldn't create new address in new wallet\n");
			abort = 1;
			reportFailure();
			break;
		}
	}
	if (!abort)
	{
		reportSuccess();
	}
	if (getNumAddresses() == MAX_TESTING_ADDRESSES)
	{
		reportSuccess();
	}
	else
	{
		printf("getNumAddresses() returns wrong number of addresses\n");
		reportFailure();
	}

	// The wallet should contain unique addresses.
	abort_duplicate = 0;
	for (i = 0; i < MAX_TESTING_ADDRESSES; i++)
	{
		for (j = 0; j < i; j++)
		{
			if (!memcmp(&(address_buffer[i * 20]), &(address_buffer[j * 20]), 20))
			{
				printf("Wallet has duplicate addresses\n");
				abort_duplicate = 1;
				reportFailure();
				break;
			}
		}
	}
	if (!abort_duplicate)
	{
		reportSuccess();
	}

	// The wallet should contain unique public keys.
	abort_duplicate = 0;
	for (i = 0; i < MAX_TESTING_ADDRESSES; i++)
	{
		for (j = 0; j < i; j++)
		{
			if (bigCompare(public_key_buffer[i].x, public_key_buffer[j].x) == BIGCMP_EQUAL)
			{
				printf("Wallet has duplicate public keys\n");
				abort_duplicate = 1;
				reportFailure();
				break;
			}
		}
	}
	if (!abort_duplicate)
	{
		reportSuccess();
	}

	// The address handles should start at 1 and be sequential.
	abort = 0;
	for (i = 0; i < MAX_TESTING_ADDRESSES; i++)
	{
		if (handles_buffer[i] != (AddressHandle)(i + 1))
		{
			printf("Address handle %d should be %d, but got %d\n", i, i + 1, (int)handles_buffer[i]);
			abort = 1;
			reportFailure();
			break;
		}
	}
	if (!abort)
	{
		reportSuccess();
	}

	// While there's a bunch of addresses in the wallet, check that
	// getAddressAndPublicKey() obtains the same address and public key as
	// makeNewAddress().
	abort_error = 0;
	abort = 0;
	for (i = 0; i < MAX_TESTING_ADDRESSES; i++)
	{
		ah = handles_buffer[i];
		if (getAddressAndPublicKey(address1, &public_key, ah) != WALLET_NO_ERROR)
		{
			printf("Couldn't obtain address in wallet\n");
			abort_error = 1;
			reportFailure();
			break;
		}
		if ((memcmp(address1, &(address_buffer[i * 20]), 20))
			|| (bigCompare(public_key.x, public_key_buffer[i].x) != BIGCMP_EQUAL)
			|| (bigCompare(public_key.y, public_key_buffer[i].y) != BIGCMP_EQUAL))
		{
			printf("getAddressAndPublicKey() returned mismatching address or public key, ah = %d\n", i);
			abort = 1;
			reportFailure();
			break;
		}
	}
	if (!abort)
	{
		reportSuccess();
	}
	if (!abort_error)
	{
		reportSuccess();
	}

	// Test getAddressAndPublicKey() and getPrivateKey() functions using
	// invalid and then valid address handles.
	if (getAddressAndPublicKey(temp, &public_key, 0) == WALLET_INVALID_HANDLE)
	{
		reportSuccess();
	}
	else
	{
		printf("getAddressAndPublicKey() doesn't recognise 0 as invalid address handle\n");
		reportFailure();
	}
	if (getPrivateKey(temp, 0) == WALLET_INVALID_HANDLE)
	{
		reportSuccess();
	}
	else
	{
		printf("getPrivateKey() doesn't recognise 0 as invalid address handle\n");
		reportFailure();
	}
	if (getAddressAndPublicKey(temp, &public_key, BAD_ADDRESS_HANDLE) == WALLET_INVALID_HANDLE)
	{
		reportSuccess();
	}
	else
	{
		printf("getAddressAndPublicKey() doesn't recognise BAD_ADDRESS_HANDLE as invalid address handle\n");
		reportFailure();
	}
	if (getPrivateKey(temp, BAD_ADDRESS_HANDLE) == WALLET_INVALID_HANDLE)
	{
		reportSuccess();
	}
	else
	{
		printf("getPrivateKey() doesn't recognise BAD_ADDRESS_HANDLE as invalid address handle\n");
		reportFailure();
	}
	if (getAddressAndPublicKey(temp, &public_key, handles_buffer[0]) == WALLET_NO_ERROR)
	{
		reportSuccess();
	}
	else
	{
		printf("getAddressAndPublicKey() doesn't recognise valid address handle\n");
		reportFailure();
	}
	if (getPrivateKey(temp, handles_buffer[0]) == WALLET_NO_ERROR)
	{
		reportSuccess();
	}
	else
	{
		printf("getPrivateKey() doesn't recognise valid address handle\n");
		reportFailure();
	}

	free(address_buffer);
	free(public_key_buffer);
	free(handles_buffer);

	// Check that changeEncryptionKey() works.
	memset(new_encryption_key, 0, WALLET_ENCRYPTION_KEY_LENGTH);
	new_encryption_key[0] = 1;
	if (changeEncryptionKey(new_encryption_key) == WALLET_NO_ERROR)
	{
		reportSuccess();
	}
	else
	{
		printf("Couldn't change encryption key\n");
		reportFailure();
	}

	// Check that the version field is "encrypted wallet".
	if (getWalletInfo(version, temp) == WALLET_NO_ERROR)
	{
		reportSuccess();
	}
	else
	{
		printf("getWalletInfo() failed after changeEncryptionKey() was called\n");
		reportFailure();
	}
	if (readU32LittleEndian(version) == VERSION_IS_ENCRYPTED)
	{
		reportSuccess();
	}
	else
	{
		printf("changeEncryptionKey() does not set version to encrypted wallet\n");
		reportFailure();
	}

	// Check name matches what was given in newWallet().
	if (!memcmp(temp, name, NAME_LENGTH))
	{
		reportSuccess();
	}
	else
	{
		printf("getWalletInfo() doesn't return correct name when wallet is loaded\n");
		reportFailure();
	}

	// Check that getWalletInfo() still works after unloading wallet.
	uninitWallet();
	if (getWalletInfo(version, temp) == WALLET_NO_ERROR)
	{
		reportSuccess();
	}
	else
	{
		printf("getWalletInfo() failed after uninitWallet() was called\n");
		reportFailure();
	}
	if (readU32LittleEndian(version) == VERSION_IS_ENCRYPTED)
	{
		reportSuccess();
	}
	else
	{
		printf("uninitWallet() caused wallet version to change\n");
		reportFailure();
	}

	// Check name matches what was given in newWallet().
	if (!memcmp(temp, name, NAME_LENGTH))
	{
		reportSuccess();
	}
	else
	{
		printf("getWalletInfo() doesn't return correct name when wallet is not loaded\n");
		reportFailure();
	}

	// Change wallet's name and check that getWalletInfo() reflects the
	// name change.
	initWallet();
	memcpy(name, "HHHHH HHHHHHHHHHHHHHHHH HHHHHHHHHHHHHH  ", NAME_LENGTH);
	if (changeWalletName(name) == WALLET_NO_ERROR)
	{
		reportSuccess();
	}
	else
	{
		printf("changeWalletName() couldn't change name\n");
		reportFailure();
	}
	getWalletInfo(version, temp);
	if (!memcmp(temp, name, NAME_LENGTH))
	{
		reportSuccess();
	}
	else
	{
		printf("getWalletInfo() doesn't reflect name change\n");
		reportFailure();
	}

	// Check that name change is preserved when unloading and loading a
	// wallet.
	uninitWallet();
	getWalletInfo(version, temp);
	if (!memcmp(temp, name, NAME_LENGTH))
	{
		reportSuccess();
	}
	else
	{
		printf("getWalletInfo() doesn't reflect name change after unloading wallet\n");
		reportFailure();
	}

	// Check that initWallet() succeeds (changing the name changes the
	// checksum, so this tests whether the checksum was updated).
	if (initWallet() == WALLET_NO_ERROR)
	{
		reportSuccess();
	}
	else
	{
		printf("initWallet() failed after name change\n");
		reportFailure();
	}
	getWalletInfo(version, temp);
	if (!memcmp(temp, name, NAME_LENGTH))
	{
		reportSuccess();
	}
	else
	{
		printf("getWalletInfo() doesn't reflect name change after reloading wallet\n");
		reportFailure();
	}

	// Check that loading the wallet with the old key fails.
	uninitWallet();
	setEncryptionKey(encryption_key);
	if (initWallet() == WALLET_NOT_THERE)
	{
		reportSuccess();
	}
	else
	{
		printf("Loading wallet with old encryption key succeeds\n");
		reportFailure();
	}

	// Check that loading the wallet with the new key succeeds.
	uninitWallet();
	setEncryptionKey(new_encryption_key);
	if (initWallet() == WALLET_NO_ERROR)
	{
		reportSuccess();
	}
	else
	{
		printf("Loading wallet with new encryption key fails\n");
		reportFailure();
	}

	// Test the getAddressAndPublicKey() and getPrivateKey() functions on an
	// empty wallet.
	newWallet(name);
	if (getAddressAndPublicKey(temp, &public_key, 0) == WALLET_EMPTY)
	{
		reportSuccess();
	}
	else
	{
		printf("getAddressAndPublicKey() doesn't deal with empty wallets correctly\n");
		reportFailure();
	}
	if (getPrivateKey(temp, 0) == WALLET_EMPTY)
	{
		reportSuccess();
	}
	else
	{
		printf("getPrivateKey() doesn't deal with empty wallets correctly\n");
		reportFailure();
	}

	fclose(wallet_test_file);

	finishTests();
	exit(0);
}
/** Get packet from stream and deal with it. This basically implements the
  * protocol described in the file PROTOCOL.
  * 
  * This function will always completely
  * read a packet before sending a response packet. As long as the host
  * does the same thing, deadlocks cannot occur. Thus a productive
  * communication session between the hardware Bitcoin wallet and a host
  * should consist of the wallet and host alternating between sending a
  * packet and receiving a packet.
  */
void processPacket(void)
{
	uint8_t command;
	// Technically, the length of buffer should also be >= 4, since it is used
	// in a couple of places to obtain 32 bit values. This is guaranteed by
	// the reference to WALLET_ENCRYPTION_KEY_LENGTH, since no-one in their
	// right mind would use encryption with smaller than 32 bit keys.
	uint8_t buffer[MAX(NAME_LENGTH, WALLET_ENCRYPTION_KEY_LENGTH)];
	uint32_t num_addresses;
	AddressHandle ah;
	WalletErrors wallet_return;

	command = streamGetOneByte();
	getBytesFromStream(buffer, 4);
	payload_length = readU32LittleEndian(buffer);

	// Checklist for each case:
	// 1. Have you checked or dealt with length?
	// 2. Have you fully read the input stream before writing (to avoid
	//    deadlocks)?
	// 3. Have you asked permission from the user (for potentially dangerous
	//    operations)?
	// 4. Have you checked for errors from wallet functions?
	// 5. Have you used the right check for the wallet functions?

	switch (command)
	{

	case PACKET_TYPE_PING:
		// Ping request.
		// Just throw away the data and then send response.
		readAndIgnoreInput();
		writeString(STRINGSET_MISC, MISCSTR_VERSION, PACKET_TYPE_PING_REPLY);
		break;

	// Commands PACKET_TYPE_PING_REPLY, PACKET_TYPE_SUCCESS and
	// PACKET_TYPE_FAILURE should never be received; they are only sent.

	case PACKET_TYPE_NEW_WALLET:
		// Create new wallet.
		if (!expectLength(WALLET_ENCRYPTION_KEY_LENGTH + NAME_LENGTH))
		{
			getBytesFromStream(buffer, WALLET_ENCRYPTION_KEY_LENGTH);
			setEncryptionKey(buffer);
			getBytesFromStream(buffer, NAME_LENGTH);
			if (askUser(ASKUSER_NUKE_WALLET))
			{
				writeString(STRINGSET_MISC, MISCSTR_PERMISSION_DENIED, PACKET_TYPE_FAILURE);
			}
			else
			{
				wallet_return = newWallet(buffer);
				translateWalletError(wallet_return, 0, NULL);
			}
		}
		break;

	case PACKET_TYPE_NEW_ADDRESS:
		// Create new address in wallet.
		if (!expectLength(0))
		{
			if (askUser(ASKUSER_NEW_ADDRESS))
			{
				writeString(STRINGSET_MISC, MISCSTR_PERMISSION_DENIED, PACKET_TYPE_FAILURE);
			}
			else
			{
				getAndSendAddressAndPublicKey(1);
			}
		}
		break;

	case PACKET_TYPE_GET_NUM_ADDRESSES:
		// Get number of addresses in wallet.
		if (!expectLength(0))
		{
			num_addresses = getNumAddresses();
			writeU32LittleEndian(buffer, num_addresses);
			wallet_return = walletGetLastError();
			translateWalletError(wallet_return, 4, buffer);
		}
		break;

	case PACKET_TYPE_GET_ADDRESS_PUBKEY:
		// Get address and public key corresponding to an address handle.
		if (!expectLength(4))
		{
			getAndSendAddressAndPublicKey(0);
		}
		break;

	case PACKET_TYPE_SIGN_TRANSACTION:
		// Sign a transaction.
		if (payload_length <= 4)
		{
			readAndIgnoreInput();
			writeString(STRINGSET_MISC, MISCSTR_INVALID_PACKET, PACKET_TYPE_FAILURE);
		}
		else
		{
			getBytesFromStream(buffer, 4);
			ah = readU32LittleEndian(buffer);
			// Don't need to subtract 4 off payload_length because
			// getBytesFromStream() has already done so.
			validateAndSignTransaction(ah, payload_length);
			payload_length = 0;
		}
		break;

	case PACKET_TYPE_LOAD_WALLET:
		// Load wallet.
		if (!expectLength(WALLET_ENCRYPTION_KEY_LENGTH))
		{
			getBytesFromStream(buffer, WALLET_ENCRYPTION_KEY_LENGTH);
			setEncryptionKey(buffer);
			wallet_return = initWallet();
			translateWalletError(wallet_return, 0, NULL);
		}
		break;

	case PACKET_TYPE_UNLOAD_WALLET:
		// Unload wallet.
		if (!expectLength(0))
		{
			clearEncryptionKey();
			sanitiseRam();
			memset(buffer, 0xff, sizeof(buffer));
			memset(buffer, 0, sizeof(buffer));
			wallet_return = uninitWallet();
			translateWalletError(wallet_return, 0, NULL);
		}
		break;

	case PACKET_TYPE_FORMAT:
		// Format storage.
		if (!expectLength(0))
		{
			if (askUser(ASKUSER_FORMAT))
			{
				writeString(STRINGSET_MISC, MISCSTR_PERMISSION_DENIED, PACKET_TYPE_FAILURE);
			}
			else
			{
				wallet_return = sanitiseNonVolatileStorage(0, 0xffffffff);
				translateWalletError(wallet_return, 0, NULL);
				uninitWallet(); // force wallet to unload
			}
		}
		break;

	case PACKET_TYPE_CHANGE_KEY:
		// Change wallet encryption key.
		if (!expectLength(WALLET_ENCRYPTION_KEY_LENGTH))
		{
			getBytesFromStream(buffer, WALLET_ENCRYPTION_KEY_LENGTH);
			wallet_return = changeEncryptionKey(buffer);
			translateWalletError(wallet_return, 0, NULL);
		}
		break;

	case PACKET_TYPE_CHANGE_NAME:
		// Change wallet name.
		if (!expectLength(NAME_LENGTH))
		{
			getBytesFromStream(buffer, NAME_LENGTH);
			if (askUser(ASKUSER_CHANGE_NAME))
			{
				writeString(STRINGSET_MISC, MISCSTR_PERMISSION_DENIED, PACKET_TYPE_FAILURE);
			}
			else
			{
				wallet_return = changeWalletName(buffer);
				translateWalletError(wallet_return, 0, NULL);
			}
		}
		break;

	case PACKET_TYPE_LIST_WALLETS:
		// List wallets.
		if (!expectLength(0))
		{
			listWallets();
		}
		break;

	default:
		// Unknown command.
		readAndIgnoreInput();
		writeString(STRINGSET_MISC, MISCSTR_INVALID_PACKET, PACKET_TYPE_FAILURE);
		break;

	}

#ifdef TEST_STREAM_COMM
	assert(payload_length == 0);
#endif

}
/** Send a packet containing an address and its corresponding public key.
  * This can generate new addresses as well as obtain old addresses. Both
  * use cases were combined into one function because they involve similar
  * processes.
  * \param generate_new If this is non-zero, a new address will be generated
  *                     and the address handle of the generated address will
  *                     be prepended to the output packet.
  *                     If this is zero, the address handle will be read from
  *                     the input stream. No address handle will be prepended
  *                     to the output packet.
  */
static NOINLINE void getAndSendAddressAndPublicKey(uint8_t generate_new)
{
	AddressHandle ah;
	PointAffine public_key;
	uint8_t address[20];
	uint8_t buffer[4];
	WalletErrors r;

	if (generate_new)
	{
		// Generate new address handle.
		r = WALLET_NO_ERROR;
		ah = makeNewAddress(address, &public_key);
		if (ah == BAD_ADDRESS_HANDLE)
		{
			r = walletGetLastError();
		}
	}
	else
	{
		// Read address handle from input stream.
		getBytesFromStream(buffer, 4);
		ah = readU32LittleEndian(buffer);
		r = getAddressAndPublicKey(address, &public_key, ah);
	}

	if (r == WALLET_NO_ERROR)
	{
		streamPutOneByte(PACKET_TYPE_SUCCESS); // type
		if (generate_new)
		{
			// 4 (address handle) + 20 (address) + 65 (public key)
			writeU32LittleEndian(buffer, 89);
		}
		else
		{
			// 20 (address) + 65 (public key)
			writeU32LittleEndian(buffer, 85);
		}
		writeBytesToStream(buffer, 4); // length
		if (generate_new)
		{
			writeU32LittleEndian(buffer, ah);
			writeBytesToStream(buffer, 4);
		}
		writeBytesToStream(address, 20);
		// The format of public keys sent is compatible with
		// "SEC 1: Elliptic Curve Cryptography" by Certicom research, obtained
		// 15-August-2011 from: http://www.secg.org/collateral/sec1_final.pdf
		// section 2.3 ("Data Types and Conversions"). The document basically
		// says that integers should be represented big-endian and that a 0x04
		// should be prepended to indicate that the public key is
		// uncompressed.
		streamPutOneByte(0x04);
		swapEndian256(public_key.x);
		swapEndian256(public_key.y);
		writeBytesToStream(public_key.x, 32);
		writeBytesToStream(public_key.y, 32);
	}
	else
	{
		translateWalletError(r, 0, NULL);
	} // end if (r == WALLET_NO_ERROR)
}