Skip to content

Commit

Permalink
wip encoding TLV identifiers
Browse files Browse the repository at this point in the history
  • Loading branch information
Sebastix committed Nov 21, 2024
1 parent 2c0315d commit 58f9aac
Show file tree
Hide file tree
Showing 2 changed files with 133 additions and 35 deletions.
23 changes: 12 additions & 11 deletions src/Examples/bech32-encoded-entities.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
try {
$nip19 = new Nip19Helper(); // Helper.
$id = '43fb0422457c1fadec68c5ad18378abb2c626d6b787790973e888d0998f6ced4'; // This is an event hex id.
//$id = 'fb0422457c1fadec68c5ad18378abb2c626d6b787790973e888d0998f6ce'; // Invalid ID.
//$id = 'fb0422457c1fadec68c5ad18378abb2c626d6b787790973e888d0998f6ce'; // This is an invalid ID.

// Encode it to a bech32 encoded note ID.
$note = $nip19->encodeNote($id);
Expand All @@ -21,28 +21,29 @@
// Encode a profile pubkey or npub, this already works.
$key = new Key();
$pubkey = '06639a386c9c1014217622ccbcf40908c4f1a0c33e23f8d6d68f4abf655f8f71';
// Alternative: $npub = $key->convertPublicKeyToBech32($pubkey);
// Alternative way: $npub = $key->convertPublicKeyToBech32($pubkey);
$npub = $nip19->encodeNpub($pubkey);
// Expected result:
// npub1qe3e5wrvnsgpggtkytxteaqfprz0rgxr8c3l34kk3a9t7e2l3acslezefe
print $npub . PHP_EOL;

// Using the more generic encode method
// Alternative: using the more generic encode method with the encode() method.
$note1 = $nip19->encode($id, 'note');
print $note1 . PHP_EOL;
//print $note1 . PHP_EOL;

// TODO:
// TODO
// Encode to nevent with TLV data

$pubkey = 'npub1qe3e5wrvnsgpggtkytxteaqfprz0rgxr8c3l34kk3a9t7e2l3acslezefe'; // This npub will be converted to a hex formatted pubkey.
$nevent = $nip19->encodeEvent($id, ['wss://nostr.sebastix.dev'], $pubkey, 1);
// Expected result:
// nevent1qqsy87cyyfzhc8ada35vttgcx79tktrzd44hsausjulg3rgfnrmva4qey0p0j
// print $nevent . PHP_EOL;
// nevent1qqsy87cyyfzhc8ada35vttgcx79tktrzd44hsausjulg3rgfnrmva4qey0p0js
print $nevent . PHP_EOL;

// TODO
// Encode to pubkey profile with TLV data
$pubkey = '3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d';
$relays = ['wss://r.x.com', 'wss://djbas.sadkb.com'];
$nprofile = $nip19->encodeProfile($pubkey, $relays);
//$nprofile = $nip19->encodeProfile($pubkey, $relays);
// Expected result with TLV items:
// - pubkey: 3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d
// - relay: wss://r.x.com
Expand All @@ -52,11 +53,11 @@
// Encode to naddr with TLV data

// TODO
// Decode a bech32 encoded entity to an event ID.
// Decode a bech32 encoded event entity to an event ID.
$nevent = '';

// TODO
// Decode to event with TLV data
// Decode a bech32 encoded profile entity with TLV data
$profile_id = 'nprofile1qqsrhuxx8l9ex335q7he0f09aej04zpazpl0ne2cgukyawd24mayt8gpp4mhxue69uhhytnc9e3k7mgpz4mhxue69uhkg6nzv9ejuumpv34kytnrdaksjlyr9p';
// Expected result with TLV items:
// - pubkey: 3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d
Expand Down
145 changes: 121 additions & 24 deletions src/Nip19/Nip19Helper.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use swentel\nostr\Nip19\TLVEnum;

use function BitWasp\Bech32\convertBits;
use function BitWasp\Bech32\decode;
use function BitWasp\Bech32\encode;

/**
Expand All @@ -25,10 +26,6 @@ class Nip19Helper
*/
protected $prefix;

public function __construct()
{
}

public function decode(string $bech32string)
{
$length = strlen($bech32string);
Expand Down Expand Up @@ -65,20 +62,15 @@ public function encodeNote(string $event_hex): string
* @param string $event_hex
* @param array $relays
* @param string $author
* @param int $kind
* @param int|null $kind
* @return string
* @throws \Exception
*/
public function encodeEvent(string $event_hex, array $relays = [], string $author = '', int $kind = null): string
{
$data = '';
$prefix = 'nevent';
$event_hex_in_bin = hex2bin($event_hex); // Convert hex formatted pubkey string to binary string.
if (strlen($event_hex_in_bin) !== 32) {
throw new \Exception(sprintf('This is an invalid event ID: %s', $event_hex));
}
// TODO: process TLV entries
$tlvEntry = $this->writeTLVEntry(TLVEnum::Special, $event_hex_in_bin);
$tlvEntry = $this->writeTLVEntry($prefix, TLVEnum::Special, $event_hex);
// Optional
if (!(empty($relays))) {
foreach ($relays as $relay) {
Expand All @@ -87,22 +79,24 @@ public function encodeEvent(string $event_hex, array $relays = [], string $autho
// Alternative which requires the icon PHP extension installed on the host machine.
// $relay = iconv('UTF-8', 'ASCII', $relay);
// decode ascii relay string
$tlvEntry .= $this->writeTLVEntry(TLVEnum::Relay, urlencode($relay));
$tlvEntry .= $this->writeTLVEntry($prefix, TLVEnum::Relay, urlencode($relay));
}
}
// Optional
if (!(empty($author))) {
if (str_starts_with($author, 'npub') === true) {
$author = $this->convertToHex($author);
}
if (strlen(hex2bin($author)) !== 32) {
throw new \Exception(sprintf('This is an invalid author ID: %s', $event_hex));
throw new \RuntimeException(sprintf('This is an invalid author ID: %s', $event_hex));
}
// Convert hex formatted pubkey to 32-bit binary value.
$tlvEntry .= $this->writeTLVEntry(TLVEnum::Author, hex2bin($author));
$tlvEntry .= $this->writeTLVEntry($prefix, TLVEnum::Author, $author);
}
// Optional
if ($kind !== null) {
// Convert kint int to unsigned integer, big-endian.
$v = pack('N', $kind);
$tlvEntry .= $this->writeTLVEntry(TLVEnum::Kind, $v);
$tlvEntry .= $this->writeTLVEntry($prefix, TLVEnum::Kind, $kind);
}
$data = $tlvEntry;

Expand Down Expand Up @@ -144,7 +138,7 @@ public function encodeNsec(string $seckey): string
public function encodeBech32(string $value, string $prefix): string
{
// TODO
$bytes = [];
$bytes = [$value];
return encode($prefix, $bytes);
}

Expand All @@ -169,25 +163,128 @@ private function convertToBech32(string $key, string $prefix): string
return $str;
}

private function readTLVEntry(string $data, TLVEnum $type)
/**
* Convert a bech32 encoded string to hex string.
*
* @param string $key
*
* @return string
*/
private function convertToHex(string $key): string
{
$str = '';
try {
$decoded = decode($key);
$data = $decoded[1];
$bytes = convertBits($data, count($data), 5, 8, false);
foreach ($bytes as $item) {
$str .= str_pad(dechex($item), 2, '0', STR_PAD_LEFT);
}
} catch (Bech32Exception $e) {
throw new \RuntimeException($e->getMessage());
}

return $str;
}

private function readTLVEntry(string $data, TLVEnum $type): string {}

/**
* https://nips.nostr.com/19#shareable-identifiers-with-extra-metadata
*
* @param string $prefix
* @param \swentel\nostr\Nip19\TLVEnum $type
* @param string $value
* Binary string.
* @param string|int $value
* @return string
*/
private function writeTLVEntry(TLVEnum $type, string $value)
private function writeTLVEntry(string $prefix, TLVEnum $type, string|int $value): string
{
// TODO
return $value;
$buf = '';
try {
if ($prefix === 'nevent' && $type->name === 'Special') {
$event_hex_in_bin = hex2bin($value);
if (strlen($event_hex_in_bin) !== 32) {
throw new \RuntimeException(sprintf('This is an invalid event ID: %s', $value));
}
// TODO Return the 32 bytes of the event id.
$byte_array = unpack('C*', $event_hex_in_bin);
$uint32 = $this->uInt32($value, null);
$buf .= $uint32;
//print $event_hex_in_bin;
}
if ($prefix === 'nevent' && $type->name === 'Author') {
// TODO Return the 32 bytes of the pubkey of the event
$buf .= $this->uInt32($value, null);
}
if ($prefix === 'nevent' && $type->name === 'Relay') {
// TODO encoded as ascii
$buf .= $value;
}
if ($prefix === 'nevent' && $type->name === 'Kind') {
// TODO Return the 32-bit unsigned integer of the kind, big-endian
$buf .= $this->uInt32($value, true);
}

if ($prefix === 'profile') {

}
if ($prefix === 'naddr') {

}

} catch (Bech32Exception $e) {
throw new \RuntimeException($e->getMessage());
}
return $buf;
}

private function encodeTLV(Object $TLV): array
{

// TODO
return [];
}

/**
* @param $i
* @return mixed|string
*/
private static function uInt8($i)
{
return is_int($i) ? pack("C", $i) : unpack("C", $i)[1];
}

/**
* @param $i
* @param $endianness
* @return mixed
*/
private static function uInt16($i, $endianness = false)
{
$f = is_int($i) ? "pack" : "unpack";

if ($endianness === true) { // big-endian
$i = $f("n", $i);
} elseif ($endianness === false) { // little-endian
$i = $f("v", $i);
} elseif ($endianness === null) { // machine byte order
$i = $f("S", $i);
}

return is_array($i) ? $i[1] : $i;
}

private static function uInt32($i, $endianness = false)
{
$f = is_int($i) ? "pack" : "unpack";

if ($endianness === true) { // big-endian
$i = $f("N", $i);
} elseif ($endianness === false) { // little-endian
$i = $f("V", $i);
} elseif ($endianness === null) { // machine byte order
$i = $f("L", $i);
}

return is_array($i) ? $i[1] : $i;
}
}

0 comments on commit 58f9aac

Please sign in to comment.