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 22, 2024
1 parent 58f9aac commit 663f8d0
Show file tree
Hide file tree
Showing 2 changed files with 112 additions and 45 deletions.
51 changes: 37 additions & 14 deletions src/Examples/bech32-encoded-entities.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,47 +7,70 @@
use swentel\nostr\Key\Key;
use swentel\nostr\Nip19\Nip19Helper;

/**
* Example snippet where we encode key ands ids into bech32 formatted entities.
*/

try {
$nip19 = new Nip19Helper(); // Helper.
$id = '43fb0422457c1fadec68c5ad18378abb2c626d6b787790973e888d0998f6ced4'; // This is an event hex id.
$nip19 = new Nip19Helper(); // The helper.
$event_id = '43fb0422457c1fadec68c5ad18378abb2c626d6b787790973e888d0998f6ced4'; // This is an event hex id.
//$id = 'fb0422457c1fadec68c5ad18378abb2c626d6b787790973e888d0998f6ce'; // This is an invalid ID.

// Encode it to a bech32 encoded note ID.
$note = $nip19->encodeNote($id);
$note = $nip19->encodeNote($event_id);
// Expected result:
// note1g0asggj90s06mmrgckk3sdu2hvkxymtt0pmep9e73zxsnx8kem2qulye77
print $note . PHP_EOL;
// Alternative: using the more generic encode method with the encode() method.
$note1 = $nip19->encode($event_id, 'note');
//print $note1 . PHP_EOL;

// Encode a profile pubkey or npub, this already works.
$key = new Key();
$pubkey = '06639a386c9c1014217622ccbcf40908c4f1a0c33e23f8d6d68f4abf655f8f71';
// Alternative way: $npub = $key->convertPublicKeyToBech32($pubkey);
// Alternative way:
// $key = new Key();
// $npub = $key->convertPublicKeyToBech32($pubkey);
$npub = $nip19->encodeNpub($pubkey);
// Expected result:
// npub1qe3e5wrvnsgpggtkytxteaqfprz0rgxr8c3l34kk3a9t7e2l3acslezefe
print $npub . PHP_EOL;

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

// TODO
// Encode to nevent with TLV data
$nevent_1 = $nip19->encodeEvent($event_id);
$nevent_11 = $nip19->encode($event_id, 'nevent');
// Expected result, checked with nak:
// $ ./nak encode nevent 43fb0422457c1fadec68c5ad18378abb2c626d6b787790973e888d0998f6ced4
// nevent1qqsy87cyyfzhc8ada35vttgcx79tktrzd44hsausjulg3rgfnrmva4qey0p0j
print $nevent_1 . PHP_EOL;

// TODO
$nevent_2 = $nip19->encodeEvent($event_id, [], $pubkey, 1);
// Expected result, checked with nak:
// $ ./nak encode nevent --author 06639a386c9c1014217622ccbcf40908c4f1a0c33e23f8d6d68f4abf655f8f71 43fb0422457c1fadec68c5ad18378abb2c626d6b787790973e888d0998f6ced4
// nevent1qqsy87cyyfzhc8ada35vttgcx79tktrzd44hsausjulg3rgfnrmva4qzyqrx8x3cdjwpq9ppwc3ve085pyyvfudqcvlz87xk668540m9t78hz5s5hp9
print $nevent_2 . PHP_EOL;

// TODO
$pubkey = 'npub1qe3e5wrvnsgpggtkytxteaqfprz0rgxr8c3l34kk3a9t7e2l3acslezefe'; // This npub will be converted to a hex formatted pubkey.
$nevent = $nip19->encodeEvent($id, ['wss://nostr.sebastix.dev'], $pubkey, 1);
// Expected result:
// nevent1qqsy87cyyfzhc8ada35vttgcx79tktrzd44hsausjulg3rgfnrmva4qey0p0js
print $nevent . PHP_EOL;
$relays = ['wss://nostr.sebastix.dev'];
//$nevent_3 = $nip19->encodeEvent($event_id, $relays, $pubkey, 1);
// Expected result, checked with nak:
// $ ./nak encode nevent --author 06639a386c9c1014217622ccbcf40908c4f1a0c33e23f8d6d68f4abf655f8f71 --relay wss://nostr.sebastix.dev 43fb0422457c1fadec68c5ad18378abb2c626d6b787790973e888d0998f6ced4
// nevent1qqsy87cyyfzhc8ada35vttgcx79tktrzd44hsausjulg3rgfnrmva4qprpmhxue69uhkummnw3ezuum9vfshxarf0qhxgetkqgsqvcu68pkfcyq5y9mz9n9u7sys33835rpnuglc6mtg7j4lv40c7ugdggh4t
//print $nevent_3 . PHP_EOL;

// TODO
// Encode to pubkey profile with TLV data
// Encode to nprofile with TLV data
$pubkey = '3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d';
$relays = ['wss://r.x.com', 'wss://djbas.sadkb.com'];
//$nprofile = $nip19->encodeProfile($pubkey, $relays);
// Expected result with TLV items:
// - pubkey: 3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d
// - relay: wss://r.x.com
// - relay: wss://djbas.sadkb.com
// $ ./nak encode nprofile --relay wss://r.x.com --relay wss://djbas.sadkb.com 3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d
// nprofile1qqsrhuxx8l9ex335q7he0f09aej04zpazpl0ne2cgukyawd24mayt8gpp4mhxue69uhhytnc9e3k7mgpz4mhxue69uhkg6nzv9ejuumpv34kytnrdaksjlyr9p

// TODO
// Encode to naddr with TLV data
Expand Down
106 changes: 75 additions & 31 deletions src/Nip19/Nip19Helper.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,21 @@
use BitWasp\Bech32\Exception\Bech32Exception;
use swentel\nostr\Key\Key;
use swentel\nostr\Nip19\TLVEnum;

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

/**
* NIP-19 bech32-encoded entities
*
* Example reference: https://github.com/nbd-wtf/go-nostr/blob/master/nip19/nip19.go
* Example reference Go library: https://github.com/nbd-wtf/go-nostr/blob/master/nip19/nip19.go
* Example reference Javascript library:
* Example reference Python library:
*
* https://github.com/Bit-Wasp/bech32/blob/master/src/bech32.php
*
* Other helpfull resources
* https://www.geeksforgeeks.org/how-to-convert-byte-array-to-string-in-php/
*/
class Nip19Helper
{
Expand Down Expand Up @@ -74,12 +78,7 @@ public function encodeEvent(string $event_hex, array $relays = [], string $autho
// Optional
if (!(empty($relays))) {
foreach ($relays as $relay) {
// Encode as ascii.
//$relay = implode('', unpack('C*', $relay));
// 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($prefix, TLVEnum::Relay, urlencode($relay));
array_push($tlvEntry, ...$this->writeTLVEntry($prefix, TLVEnum::Relay, $relay));
}
}
// Optional
Expand All @@ -90,14 +89,14 @@ public function encodeEvent(string $event_hex, array $relays = [], string $autho
if (strlen(hex2bin($author)) !== 32) {
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($prefix, TLVEnum::Author, $author);
}
// Optional
if ($kind !== null) {
// Convert kint int to unsigned integer, big-endian.
$tlvEntry .= $this->writeTLVEntry($prefix, TLVEnum::Kind, $kind);
array_push($tlvEntry, ...$this->writeTLVEntry($prefix, TLVEnum::Author, $author));
//$tlvEntry = array_merge($tlvEntry, $this->writeTLVEntry($prefix, TLVEnum::Author, $author));
}
// // Optional
// if ($kind !== null) {
// // Convert kind int to unsigned integer, big-endian.
// array_push($tlvEntry, ...$this->writeTLVEntry($prefix, TLVEnum::Kind, $kind));
// }
$data = $tlvEntry;

return $this->encodeBech32($data, $prefix);
Expand Down Expand Up @@ -135,10 +134,8 @@ public function encodeNsec(string $seckey): string
return $key->convertPrivateKeyToBech32($seckey);
}

public function encodeBech32(string $value, string $prefix): string
public function encodeBech32(array $bytes, string $prefix): string
{
// TODO
$bytes = [$value];
return encode($prefix, $bytes);
}

Expand All @@ -152,11 +149,17 @@ private function convertToBech32(string $key, string $prefix): string
{
$str = '';

/** @var array $dec */
// This is our bits array with decimal formatted values.
$dec = [];
/** @var array $split */
// Split string into data chucks with a max length of 2 chars each chunk. This will create the byte array.
$split = str_split($key, 2);
foreach ($split as $item) {
// Loop over the byte array and convert each chuck from a hex formatted value into a decimal formatted chunks.
$dec[] = hexdec($item);
}
// Convert the bits array to a bytes array.
$bytes = convertBits($dec, count($dec), 8, 5);
$str = encode($prefix, $bytes);

Expand Down Expand Up @@ -195,34 +198,47 @@ private function readTLVEntry(string $data, TLVEnum $type): string {}
* @param string $prefix
* @param \swentel\nostr\Nip19\TLVEnum $type
* @param string|int $value
* @return string
* @return array
*/
private function writeTLVEntry(string $prefix, TLVEnum $type, string|int $value): string
private function writeTLVEntry(string $prefix, TLVEnum $type, string|int $value): array
{
$buf = '';
$buf = [];
try {
if ($prefix === 'nevent' && $type->name === 'Special') {
// TODO Return the 32 bytes of the event id.

// Convert hexadecimal string to its binary representation.
$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;

// // Convert to ... ?
// $uint32 = $this->uInt32($value, null);
// // Bytes or bits (?) array with decimal formatted chunks
// $byte_array = unpack('C*', $value);
// // Some from byte array to string methods:
// $strFromByteArray1 = implode(array_map("chr", $byte_array));
// $strFromByteArray2 = pack('C*', ...$byte_array);
// $strFromByteArray3 = '';
// foreach ($byte_array as $byte) {
// $strFromByteArray3 .= chr($byte);
// }

$buf = $this->convertToBytes($value);
}
if ($prefix === 'nevent' && $type->name === 'Author') {
// TODO Return the 32 bytes of the pubkey of the event
$buf .= $this->uInt32($value, null);
$buf = $this->convertToBytes($value);
}
if ($prefix === 'nevent' && $type->name === 'Relay') {
// TODO encoded as ascii
$buf .= $value;
// TODO
$relay = urlencode($value);
//$buf = $this->convertToBytes($relay);
}
if ($prefix === 'nevent' && $type->name === 'Kind') {
// TODO Return the 32-bit unsigned integer of the kind, big-endian
$buf .= $this->uInt32($value, true);
//$buf = $this->uInt32($value, true);
}

if ($prefix === 'profile') {
Expand All @@ -231,10 +247,12 @@ private function writeTLVEntry(string $prefix, TLVEnum $type, string|int $value)
if ($prefix === 'naddr') {

}

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

Expand All @@ -244,6 +262,27 @@ private function encodeTLV(Object $TLV): array
return [];
}

/**
* @param string $str
* @return array
* @throws Bech32Exception
*/
private function convertToBytes(string $str): array
{
/** @var array $dec */
// This will our bits array with decimal formatted values.
$dec = [];
/** @var array $split */
// Split string into data chucks with a max length of 2 chars each chunk. This will create the byte array.
$split = str_split($str, 2);
foreach ($split as $item) {
// Loop over the byte array and convert each chuck from a hex formatted value into a decimal formatted chunks so we get our bits array.
$dec[] = hexdec($item);
}
// Convert bits to bytes.
return convertBits($dec, count($dec), 8, 5);
}

/**
* @param $i
* @return mixed|string
Expand Down Expand Up @@ -273,6 +312,11 @@ private static function uInt16($i, $endianness = false)
return is_array($i) ? $i[1] : $i;
}

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

0 comments on commit 663f8d0

Please sign in to comment.