/* Pipe through jq for test vector! */
#include "config.h"
#include "../bigsize.c"
#include "../blindedpath.c"
#include "../blinding.c"
#include "../hmac.c"
#include "../onion_decode.c"
#include "../onion_encode.c"
#include "../sphinx.c"
#include <common/setup.h>
#include <wire/peer_wire.h>
#include <stdio.h>

/* AUTOGENERATED MOCKS START */
/* Generated stub for amount_asset_is_main */
bool amount_asset_is_main(struct amount_asset *asset UNNEEDED)
{ fprintf(stderr, "amount_asset_is_main called!\n"); abort(); }
/* Generated stub for amount_asset_to_sat */
struct amount_sat amount_asset_to_sat(struct amount_asset *asset UNNEEDED)
{ fprintf(stderr, "amount_asset_to_sat called!\n"); abort(); }
/* Generated stub for amount_feerate */
 bool amount_feerate(u32 *feerate UNNEEDED, struct amount_sat fee UNNEEDED, size_t weight UNNEEDED)
{ fprintf(stderr, "amount_feerate called!\n"); abort(); }
/* Generated stub for amount_msat */
struct amount_msat amount_msat(u64 millisatoshis UNNEEDED)
{ fprintf(stderr, "amount_msat called!\n"); abort(); }
/* Generated stub for amount_msat_eq */
bool amount_msat_eq(struct amount_msat a UNNEEDED, struct amount_msat b UNNEEDED)
{ fprintf(stderr, "amount_msat_eq called!\n"); abort(); }
/* Generated stub for amount_msat_less */
bool amount_msat_less(struct amount_msat a UNNEEDED, struct amount_msat b UNNEEDED)
{ fprintf(stderr, "amount_msat_less called!\n"); abort(); }
/* Generated stub for amount_sat */
struct amount_sat amount_sat(u64 satoshis UNNEEDED)
{ fprintf(stderr, "amount_sat called!\n"); abort(); }
/* Generated stub for amount_sat_add */
 bool amount_sat_add(struct amount_sat *val UNNEEDED,
				       struct amount_sat a UNNEEDED,
				       struct amount_sat b UNNEEDED)
{ fprintf(stderr, "amount_sat_add called!\n"); abort(); }
/* Generated stub for amount_sat_eq */
bool amount_sat_eq(struct amount_sat a UNNEEDED, struct amount_sat b UNNEEDED)
{ fprintf(stderr, "amount_sat_eq called!\n"); abort(); }
/* Generated stub for amount_sat_greater_eq */
bool amount_sat_greater_eq(struct amount_sat a UNNEEDED, struct amount_sat b UNNEEDED)
{ fprintf(stderr, "amount_sat_greater_eq called!\n"); abort(); }
/* Generated stub for amount_sat_sub */
 bool amount_sat_sub(struct amount_sat *val UNNEEDED,
				       struct amount_sat a UNNEEDED,
				       struct amount_sat b UNNEEDED)
{ fprintf(stderr, "amount_sat_sub called!\n"); abort(); }
/* Generated stub for amount_sat_to_asset */
struct amount_asset amount_sat_to_asset(struct amount_sat *sat UNNEEDED, const u8 *asset UNNEEDED)
{ fprintf(stderr, "amount_sat_to_asset called!\n"); abort(); }
/* Generated stub for amount_tx_fee */
struct amount_sat amount_tx_fee(u32 fee_per_kw UNNEEDED, size_t weight UNNEEDED)
{ fprintf(stderr, "amount_tx_fee called!\n"); abort(); }
/* Generated stub for fmt_amount_msat */
char *fmt_amount_msat(const tal_t *ctx UNNEEDED, struct amount_msat msat UNNEEDED)
{ fprintf(stderr, "fmt_amount_msat called!\n"); abort(); }
/* Generated stub for fromwire_amount_msat */
struct amount_msat fromwire_amount_msat(const u8 **cursor UNNEEDED, size_t *max UNNEEDED)
{ fprintf(stderr, "fromwire_amount_msat called!\n"); abort(); }
/* Generated stub for fromwire_amount_sat */
struct amount_sat fromwire_amount_sat(const u8 **cursor UNNEEDED, size_t *max UNNEEDED)
{ fprintf(stderr, "fromwire_amount_sat called!\n"); abort(); }
/* Generated stub for fromwire_channel_id */
bool fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED,
			 struct channel_id *channel_id UNNEEDED)
{ fprintf(stderr, "fromwire_channel_id called!\n"); abort(); }
/* Generated stub for fromwire_node_id */
void fromwire_node_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct node_id *id UNNEEDED)
{ fprintf(stderr, "fromwire_node_id called!\n"); abort(); }
/* Generated stub for fromwire_sciddir_or_pubkey */
void fromwire_sciddir_or_pubkey(const u8 **cursor UNNEEDED, size_t *max UNNEEDED,
				struct sciddir_or_pubkey *sciddpk UNNEEDED)
{ fprintf(stderr, "fromwire_sciddir_or_pubkey called!\n"); abort(); }
/* Generated stub for new_onionreply */
struct onionreply *new_onionreply(const tal_t *ctx UNNEEDED, const u8 *contents TAKES UNNEEDED)
{ fprintf(stderr, "new_onionreply called!\n"); abort(); }
/* Generated stub for pubkey_from_node_id */
bool pubkey_from_node_id(struct pubkey *key UNNEEDED, const struct node_id *id UNNEEDED)
{ fprintf(stderr, "pubkey_from_node_id called!\n"); abort(); }
/* Generated stub for towire_amount_msat */
void towire_amount_msat(u8 **pptr UNNEEDED, const struct amount_msat msat UNNEEDED)
{ fprintf(stderr, "towire_amount_msat called!\n"); abort(); }
/* Generated stub for towire_amount_sat */
void towire_amount_sat(u8 **pptr UNNEEDED, const struct amount_sat sat UNNEEDED)
{ fprintf(stderr, "towire_amount_sat called!\n"); abort(); }
/* Generated stub for towire_channel_id */
void towire_channel_id(u8 **pptr UNNEEDED, const struct channel_id *channel_id UNNEEDED)
{ fprintf(stderr, "towire_channel_id called!\n"); abort(); }
/* Generated stub for towire_node_id */
void towire_node_id(u8 **pptr UNNEEDED, const struct node_id *id UNNEEDED)
{ fprintf(stderr, "towire_node_id called!\n"); abort(); }
/* Generated stub for towire_sciddir_or_pubkey */
void towire_sciddir_or_pubkey(u8 **pptr UNNEEDED,
			      const struct sciddir_or_pubkey *sciddpk UNNEEDED)
{ fprintf(stderr, "towire_sciddir_or_pubkey called!\n"); abort(); }
/* AUTOGENERATED MOCKS END */

static void json_strfield(const char *name, const char *val)
{
	printf("\t\"%s\": \"%s\",\n", name, val);
}

#define ALICE 0
#define BOB 1
#define CAROL 2
#define DAVE 3

/* Updated each time, as we pretend to be Alice, Bob, Carol */
static const struct privkey *ecdh_key;

void ecdh(const struct pubkey *point, struct secret *ss)
{
	if (secp256k1_ecdh(secp256k1_ctx, ss->data, &point->pubkey,
			   ecdh_key->secret.data, NULL, NULL) != 1)
		abort();
}

static u8 *next_onion(const tal_t *ctx, u8 *omsg,
		      const struct privkey *nodekey,
		      const struct pubkey *expected_path_key)
{
	struct onionpacket *op;
	struct pubkey path_key, ephemeral;
	struct pubkey next_path_key;
	struct tlv_onionmsg_tlv *om;
	struct secret ss, onion_ss;
	const u8 *cursor;
	size_t max, maxlen;
	struct route_step *rs;
	struct tlv_encrypted_data_tlv *enc;

	assert(fromwire_onion_message(tmpctx, omsg, &path_key, &omsg));
	assert(pubkey_eq(&path_key, expected_path_key));

	op = parse_onionpacket(tmpctx, omsg, tal_bytelen(omsg), NULL);
	ephemeral = op->ephemeralkey;

	ecdh_key = nodekey;
	assert(unblind_onion(&path_key, ecdh, &ephemeral, &ss));

	ecdh(&ephemeral, &onion_ss);
	rs = process_onionpacket(tmpctx, op, &onion_ss, NULL, 0);
	assert(rs);

	/* The raw payload is prepended with length in the modern onion. */
	cursor = rs->raw_payload;
	max = tal_bytelen(rs->raw_payload);
	maxlen = fromwire_bigsize(&cursor, &max);
	om = fromwire_tlv_onionmsg_tlv(tmpctx, &cursor, &maxlen);

	if (rs->nextcase == ONION_END)
		return NULL;

	enc = decrypt_encrypted_data(tmpctx, &ss, om->encrypted_recipient_data);
	assert(enc);
	blindedpath_next_path_key(enc, &path_key, &ss, &next_path_key);
	return towire_onion_message(ctx, &next_path_key,
				    serialize_onionpacket(tmpctx, rs->next));
}

int main(int argc, char *argv[])
{
	struct privkey nodekey[4], path_key[4], override_path_key;
	struct pubkey id[4], path_key_pub[4], override_path_key_pub, alias[4];
	struct secret self_id;
	struct tlv_encrypted_data_tlv *tlv[4];
	u8 *enctlv[4];
	u8 *onionmsg_tlv[4];
	u8 *omsg;
	struct sphinx_path *sphinx_path;
	struct secret *path_secrets;
	struct onionpacket *op;
	const char *names[] = {"Alice", "Bob", "Carol", "Dave"};

	common_setup(argv[0]);

	for (size_t i = 0; i < 4; i++) {
		memset(&nodekey[i], names[i][0], sizeof(nodekey[i]));
		pubkey_from_privkey(&nodekey[i], &id[i]);
	}

	/* Make enctlvs as per enctlv test vectors */
	memset(&path_key[ALICE], 5, sizeof(path_key[ALICE]));
	pubkey_from_privkey(&path_key[ALICE], &path_key_pub[ALICE]);

	tlv[ALICE] = tlv_encrypted_data_tlv_new(tmpctx);
	tlv[ALICE]->next_node_id = &id[BOB];
	enctlv[ALICE] = encrypt_tlv_encrypted_data(tmpctx,
						   &path_key[ALICE],
						   &id[ALICE], tlv[ALICE],
						   &path_key[BOB],
						   &alias[ALICE]);

	pubkey_from_privkey(&path_key[BOB], &path_key_pub[BOB]);

	/* We override path_key for Carol. */
	memset(&override_path_key, 7, sizeof(override_path_key));
	pubkey_from_privkey(&override_path_key, &override_path_key_pub);

	tlv[BOB] = tlv_encrypted_data_tlv_new(tmpctx);
	tlv[BOB]->next_node_id = &id[CAROL];
	tlv[BOB]->next_path_key_override = &override_path_key_pub;
	enctlv[BOB] = encrypt_tlv_encrypted_data(tmpctx,
						 &path_key[BOB],
						 &id[BOB], tlv[BOB],
						 &path_key[CAROL],
						 &alias[BOB]);

	/* That replaced the path_key */
	path_key[CAROL] = override_path_key;
	path_key_pub[CAROL] = override_path_key_pub;

	tlv[CAROL] = tlv_encrypted_data_tlv_new(tmpctx);
	tlv[CAROL]->next_node_id = &id[DAVE];
	tlv[CAROL]->padding = tal_arrz(tlv[CAROL], u8, 35);
	enctlv[CAROL] = encrypt_tlv_encrypted_data(tmpctx,
						   &path_key[CAROL],
						   &id[CAROL], tlv[CAROL],
						   &path_key[DAVE],
						   &alias[CAROL]);

	for (size_t i = 0; i < sizeof(self_id); i++)
		self_id.data[i] = i+1;

	tlv[DAVE] = tlv_encrypted_data_tlv_new(tmpctx);
	tlv[DAVE]->path_id = tal_dup_arr(tlv[DAVE], u8,
					 self_id.data, ARRAY_SIZE(self_id.data),
					 0);
	enctlv[DAVE] = encrypt_tlv_encrypted_data(tmpctx,
						  &path_key[DAVE],
						  &id[DAVE], tlv[DAVE],
						  NULL,
						  &alias[DAVE]);
	pubkey_from_privkey(&path_key[DAVE], &path_key_pub[DAVE]);

	/* Create an onion which encodes this. */
	sphinx_path = sphinx_path_new(tmpctx, NULL, 0);
	for (size_t i = 0; i < 4; i++) {
		struct tlv_onionmsg_tlv *payload
			= tlv_onionmsg_tlv_new(tmpctx);
		payload->encrypted_recipient_data = enctlv[i];
		onionmsg_tlv[i] = tal_arr(tmpctx, u8, 0);
		towire_tlv_onionmsg_tlv(&onionmsg_tlv[i], payload);
		sphinx_add_hop(sphinx_path, &alias[i], onionmsg_tlv[i]);
	}
	op = create_onionpacket(tmpctx, sphinx_path, ROUTING_INFO_SIZE,
				&path_secrets);

	/* And finally, the onion message as handed to Alice */
	omsg = towire_onion_message(tmpctx, &path_key_pub[ALICE],
				    serialize_onionpacket(tmpctx, op));

	printf("{\n");
	json_strfield("description",
		      "Onion message encoding enctlv as per enctlvs.json");
	printf("\t\"onionmsgs\": [");
	for (size_t i = 0; i < 4; i++) {
		printf("{\n");
		json_strfield("node name", names[i]);
		json_strfield("node_secret",
			      fmt_privkey(tmpctx, &nodekey[i]));
		json_strfield("node_id",
			      fmt_pubkey(tmpctx, &id[i]));

		printf("\t\"onion_message\": {");
		json_strfield("raw", tal_hex(tmpctx, omsg));
		json_strfield("path_key_secret",
			      fmt_privkey(tmpctx, &path_key[i]));
		json_strfield("path_key",
			      fmt_pubkey(tmpctx, &path_key_pub[i]));
		json_strfield("blinded_alias",
			      fmt_pubkey(tmpctx, &alias[i]));
		json_strfield("onionmsg_tlv",
			      tal_hex(tmpctx, onionmsg_tlv[i]));
		printf("\"enctlv\": \"%s\"}\n", tal_hex(tmpctx, enctlv[i]));

		printf("}");
		if (i != DAVE)
			printf(",\n");

		/* Unwrap for next hop */
		omsg = next_onion(tmpctx, omsg, &nodekey[i], &path_key_pub[i]);
	}
	assert(!omsg);
	printf("\n]}\n");

	common_shutdown();
}
