Function createOnionPacket

Create an onion packet for the given path. Each hop will contain an ephemeral public key to derive a common secret with which the hop's payload will be encrypted and may later be decrypted by the hop node which owns their private key.

OnionPacket createOnionPacket (
  in const(Amount) amount,
  in const(Hop[]) path,
  out Amount total_amount,
  out Height use_lock_height,
  out agora.crypto.ECC.Point[] shared_secrets
) @safe;

The onion packet is fixed in size, and uses encrypted padding bytes to obfuscate its true size. When a node peels of their layer of the encrypted packet, it adds additional pading to fill the packet size back to its expected fixed length size.

Parameters

NameDescription
lock_height the initial lock height
amount the amount for the payment
path the individual hops (including destination hop)
total_amount will contain the amount which needs to be paid to the first channel along the route. Different to amount as it also includes fees.
shared_secrets shared secret used in each hop. (in reverse)

Returns

the onion packet ready to be routed through the first payment path.

Example

Pair kp1 = Pair.random();
Pair kp2 = Pair.random();
Pair kp3 = Pair.random();
Pair kp4 = Pair.random();

Hop[] hops = [
    Hop(kp1.V, hashFull(1), Amount(100), 1),
    Hop(kp2.V, hashFull(2), Amount(200), 1),
    Hop(kp3.V, hashFull(3), Amount(300), 1),
    Hop(kp4.V, hashFull(4), Amount(400), 1),
];

Amount total_amount;
Height use_lock_height;
Point[] shared_secrets;
auto packet = createOnionPacket(Amount(1000),
    hops, total_amount, use_lock_height, shared_secrets);
assert(total_amount == Amount(2000));
assert(use_lock_height == 4);

Point shared_secret;
Payload payload;
assert(!decryptPayload(packet.encrypted_payloads[0],
    kp2.v, packet.ephemeral_pk, payload));  // cannot decrypt with other keys
assert(decryptPayload(packet.encrypted_payloads[0],
    kp1.v, packet.ephemeral_pk, payload, shared_secret));
assert(shared_secrets[3] == shared_secret);
assert(payload == Payload(hashFull(2), Amount(1900)));

assert(!decryptPayload(packet.encrypted_payloads[1],
    kp2.v, packet.ephemeral_pk, payload));  // cannot decrypt with same ephemeral key
packet = nextPacket(packet);  // switch ephemeral key
assert(decryptPayload(packet.encrypted_payloads[0],
    kp2.v, packet.ephemeral_pk, payload, shared_secret));
assert(shared_secrets[2] == shared_secret);
assert(payload == Payload(hashFull(3), Amount(1700)));

packet = nextPacket(packet);
assert(decryptPayload(packet.encrypted_payloads[0],
    kp3.v, packet.ephemeral_pk, payload, shared_secret));
assert(shared_secrets[1] == shared_secret);
assert(payload == Payload(hashFull(4), Amount(1400)));

packet = nextPacket(packet);
assert(decryptPayload(packet.encrypted_payloads[0],
    kp4.v, packet.ephemeral_pk, payload, shared_secret));
assert(shared_secrets[0] == shared_secret);
assert(payload == Payload(Hash.init, Amount(1000)));