diff --git a/src/Examples/bech32-encoded-entities.php b/src/Examples/bech32-encoded-entities.php index 60b441b..ee2ad06 100644 --- a/src/Examples/bech32-encoded-entities.php +++ b/src/Examples/bech32-encoded-entities.php @@ -7,40 +7,61 @@ 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); @@ -48,6 +69,8 @@ // - 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 diff --git a/src/Nip19/Nip19Helper.php b/src/Nip19/Nip19Helper.php index c2ac0db..2f89ce6 100644 --- a/src/Nip19/Nip19Helper.php +++ b/src/Nip19/Nip19Helper.php @@ -7,7 +7,6 @@ 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; @@ -15,9 +14,14 @@ /** * 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 { @@ -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 @@ -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); @@ -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); } @@ -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); @@ -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') { @@ -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; } @@ -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 @@ -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";